Aug 152012
 August 15, 2012  Posted by  Add comments

Flashing Turnigy 9x With ER9X And Other Mods

As I was planning my first dive into RC and quadcopters, one of the components I was looking for was the TX (transmitter). I wanted something fairly cheap, full featured so that it can serve any RC platform, and easy to use. The Turnigy 9x fulfilled all of these requirements! After using it for a while, I found that the stock firmware and the hardware needed upgrades. This post is about the changes I made to my Turnigy 9x.

The Issues

  • AA battery holder – It would easily come loose and cause a signal failure and crash.
  • Mediocre range from stock module – For the best range use a FrSky Module and antenna.
  • Trainer Mode – The TX module must be removed for it to be used in training or connected to a PC for simulator practice.
  • No Programming header to change the firmware.
  • No backlight on the LCD – Impossible to see the screen at night.


  • Annoying button beeps – EVERY single time I pressed a button it emitted a very loud beep.
  • Messy tree navigated menus.
  • Inflexible
  • Missing some features
  • Makes easy things hard – mixes, switches etc

Continue reading to see how I solved these problems…

The Solutions

Battery Replacement

Things I Needed:

  1. A new battery from the list below.
  2. Soldering Iron
  3. Old AA battery Holder
  4. 9x
  5. Wire Strippers

This was probably the easiest one as I didn’t have to remove the back TX cover. I took measurements of the battery box (110mm x 30mm x 30mm)  and then bought a new transmitter LiPo (or LiFe) from HK. Then I cutoff the battery connector from the old AA pack and soldered that onto the LiPo ( I triple checked the polarity here!). My battery also came with two power plugs and one balance plug so I cut off one power plug to have more room in the battery box. Below is a list of battery’s that will fit inside a Turnigy 9x. Don’t mind the “(Futaba/JR)” specification; they still work.

Turnigy 2650mAh 3S 1C LLF Tx Pack (Futaba/JR) This one gives the most space in the battery box for the connectors!
Turnigy nano-tech 2100mAh 3S1P 20~40C LiFePo4 Transmitter Pack
ZIPPY Flightmax 2500mAh Transmitter Pack (Futaba/JR)
HobbyKing 1500mAH LiFe 3S 9.9v Transmitter pack
Rhino 2620mAh 3S 11.1v Low-Discharge Transmitter Lipoly Pack

FrSky TX Module Upgrade

I haven’t done this mod yet as I don’t need the longer range that the FrSKy module gives. I would have two options:

  1. The FrSky JR Compatible Telemetry Combo Module snaps into the back of the 9x like the current module and is much simple, but more expensive. Only thing to do is cut the old antenna wiring going to the 9x stock antenna. See this video.
  2. The second option is to use the cheaper but harder to install FrSky DHT 8ch DIY Telemetry Module. This requires taking off the case and doing some other more complex things. Click here for more info.

Trainer Mode Resistor Fix

Things I Needed:

  1. Phillips Screwdriver
  2. Soldering Iron
  3. Wire Strippers
  4. X-Acto Knife

First, I took out all the screws in the back of the case.

I unplugged the cable connecting the two halves.

I looked at the back half of the TX and found the trace pointed to in the picture.

I cut the trace with an knife and made sure that it was cut with a multimeter (continuity function). I saw solid dark green below where I cut the trace like the green around the light green trace. Now, I had two options:

  1. Put a 1k resistor in between the two blue circled solder points
  2. Scrape off the light green part above and below the trace until I hit copper, and then solder the 1k resistor on the two scraped off places.
I chose option two because I already had to cut the trace, but option one is easier as I wouldn’t have had to scrape the copper too.

Thats it! Now I closed it back up, but i did the mods below first. Mine wouldn’t turn on when I put it back together, but then I realized that I didn’t connect that header from the back of the case to the front!

Programming Header

Things I Needed:

  1. Wire
  2. Soldering Iron
  3. Tape/Hot Glue to hold the wires to the board (reduce stress and possibly breakage)

This is used to change the firmware of the TX (by a USBASP or Arduino etc) and has to be manually added on to the stock TX

The picture above shows all the connections I needed. The soldered pad to the immediate right of the label is the one being described. I took a 10pin header cable, split it for each of the connections, stripped off a bit of casing and then soldered them onto the pads.

I then labeled each wire on the other end of the cable.

Finally, I cut a rectangle in the battery box so that I could put a 10-pin ISP header inside. First I hot glued the header in place. Then, I soldered each each wire to it according the picture below for easy programming.

Backlight Mod

Things  Needed:

  1. Phillips screw driver
  2. Turnigy 9X LCD Backlight Kit – Blue (DIY) or Turnigy 9X LCD Backlight Kit – White (DIY)
  3. Double Sided Tape

The backlight isn’t like a desktop, laptop or television etc, it is more like a E-book reader display that doesn’t require a backlight during the day and therefore is impossible to see at night. I haven’t done this mod yet, but HK yas both blue and white backlight versions and gives a detailed guide on the product pages above. From the images at those links, it looks like the white versions gives a clearer image.

Flashing The ER9x Firmware

This solved all of my firmware problems! At first, I used an Arduino with the Arduino ISP sketch to flash the TX because I didn’t have a USBASP at the time. But, the second time I did use an USBASP, so i will detail both here.

Arduino ISP Method

Things I needed:

  1. Arduino. HobbyKing has three Arduinos to choose from (for compatibility of the code below in step 3, I stuck with the first two options):
    Arduino Nano V3.0 Microcontroller Board
    Arduino Uno – Atmel Atmega 328-PU (Clone)
    Arduino Mega 2560 Microcontroller Board (clone)
  2. Some wire to connect the TX and Arduino.

How I did it:

  1. I have an ISP header on my TX so connecting the Arduino for programming was really easy. I unplugged the Arduino and then connected these pins:
    Arduino: TX
    10: RST
    11: MOSI
    12: MISO
    13: SCK
    5v: 5v
    Gnd: Gnd
  2. Then I changed the serial buffer of the Arduino because the Arduino can’t keep up with the data (er9x firmware) being sent to it over serial on default settings. To do this, I found my arduino directory (arduino-x.x somewhere where I extracted it), and I went to: arduino-x.x\hardware\arduino\cores\arduino\ and opened HardwareSerial.cpp  in a text editor. I searched for #define SERIAL_BUFFER_SIZE and changed its value from 64 to 128.
  3. The current Arduino ISP sketch can’t handle the large amount of EEPROM that the ER9x firmware uses (the stock firmware does too), so I uploaded this sketch: 
    Arduino ISP Code
    // this sketch turns the Arduino into a AVRISP
    // using the following pins:
    // 10: slave reset
    // 11: MOSI
    // 12: MISO
    // 13: SCK// Put an LED (with resistor) on the following pins:
    // 9: Heartbeat – shows the programmer is running
    // 8: Error – Lights up if something goes wrong (use red if that makes sense)
    // 7: Programming – In communication with the slave
    // October 2010 by Randall Bohn
    // – Write to EEPROM > 256 bytes
    // – Better use of LEDs:
    // — Flash LED_PMODE on each flash commit
    // — Flash LED_PMODE while writing EEPROM (both give visual feedback of writing progress)
    // – Light LED_ERR whenever we hit a STK_NOSYNC. Turn it off when back in sync.
    // October 2009 by David A. Mellis
    // – Added support for the read signature command
    // February 2009 by Randall Bohn
    // – Added support for writing to EEPROM (what took so long?)
    // Windows users should consider WinAVR’s avrdude instead of the
    // avrdude included with Arduino software.
    // January 2008 by Randall Bohn
    // – Thanks to Amplificar for helping me with the STK500 protocol
    // – The AVRISP/STK500 (mk I) protocol is used in the arduino bootloader
    // – The SPI functions herein were developed for the AVR910_ARD programmer
    // – More information at
    #include “pins_arduino.h”  // defines SS,MOSI,MISO,SCK
    #define SCK 13
    #define MISO 12
    #define MOSI 11
    #define RESET 10#define LED_HB 9
    #define LED_ERR 8
    #define LED_PMODE 7
    #define PROG_FLICKER true#define HWVER 2
    #define SWMAJ 1
    #define SWMIN 18// STK Definitions
    #define STK_OK 0×10
    #define STK_FAILED 0×11
    #define STK_UNKNOWN 0×12
    #define STK_INSYNC 0×14
    #define STK_NOSYNC 0×15
    #define CRC_EOP 0×20 //ok it is a space…void pulse(int pin, int times);void setup() {
    pinMode(LED_PMODE, OUTPUT);
    pulse(LED_PMODE, 2);
    pinMode(LED_ERR, OUTPUT);
    pulse(LED_ERR, 2);
    pinMode(LED_HB, OUTPUT);
    pulse(LED_HB, 2);
    }int error=0;
    int pmode=0;
    // address for reading and writing, set by ‘U’ command
    int here;
    uint8_t buff[256]; // global block storage#define beget16(addr) (*addr * 256 + *(addr+1) )
    typedef struct param {
    uint8_t devicecode;
    uint8_t revision;
    uint8_t progtype;
    uint8_t parmode;
    uint8_t polling;
    uint8_t selftimed;
    uint8_t lockbytes;
    uint8_t fusebytes;
    int flashpoll;
    int eeprompoll;
    int pagesize;
    int eepromsize;
    int flashsize;
    parameter;parameter param;// this provides a heartbeat on pin 9, so you can tell the software is running.
    uint8_t hbval=128;
    int8_t hbdelta=8;
    void heartbeat() {
    if (hbval > 192) hbdelta = -hbdelta;
    if (hbval < 32) hbdelta = -hbdelta;
    hbval += hbdelta;
    analogWrite(LED_HB, hbval);
    }void loop(void) {
    // is pmode active?
    if (pmode) digitalWrite(LED_PMODE, HIGH);
    else digitalWrite(LED_PMODE, LOW);
    // is there an error?
    if (error) digitalWrite(LED_ERR, HIGH);
    else digitalWrite(LED_ERR, LOW);// light the heartbeat LED
    if (Serial.available()) {
    }uint8_t getch() {
    void fill(int n) {
    for (int x = 0; x < n; x++) {
    buff[x] = getch();
    }#define PTIME 30
    void pulse(int pin, int times) {
    do {
    digitalWrite(pin, HIGH);
    digitalWrite(pin, LOW);
    while (times–);
    }void prog_lamp(int state) {
    digitalWrite(LED_PMODE, state);
    }void spi_init() {
    uint8_t x;
    SPCR = 0×53;
    }void spi_wait() {
    do {
    while (!(SPSR & (1 << SPIF)));
    }uint8_t spi_send(uint8_t b) {
    uint8_t reply;
    reply = SPDR;
    return reply;
    }uint8_t spi_transaction(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
    uint8_t n;
    //if (n != a) error = -1;
    return spi_send(d);
    }void empty_reply() {
    if (CRC_EOP == getch()) {
    } else {

    void breply(uint8_t b) {
    if (CRC_EOP == getch()) {
    else {

    void get_version(uint8_t c) {
    switch(c) {
    case 0×80:
    case 0×81:
    case 0×82:
    case 0×93:
    breply(‘S’); // serial programmer

    void set_parameters() {
    // call this after reading paramter packet into buff[]
    param.devicecode = buff[0];
    param.revision = buff[1];
    param.progtype = buff[2];
    param.parmode = buff[3];
    param.polling = buff[4];
    param.selftimed = buff[5];
    param.lockbytes = buff[6];
    param.fusebytes = buff[7];
    param.flashpoll = buff[8];
    // ignore buff[9] (= buff[8])
    // following are 16 bits (big endian)
    param.eeprompoll = beget16(&buff[10]);
    param.pagesize = beget16(&buff[12]);
    param.eepromsize = beget16(&buff[14]);

    // 32 bits flashsize (big endian)
    param.flashsize = buff[16] * 0×01000000
    + buff[17] * 0×00010000
    + buff[18] * 0×00000100
    + buff[19];


    void start_pmode() {
    // following delays may not work on all targets…
    pinMode(RESET, OUTPUT);
    digitalWrite(RESET, HIGH);
    pinMode(SCK, OUTPUT);
    digitalWrite(SCK, LOW);
    digitalWrite(RESET, LOW);
    pinMode(MISO, INPUT);
    pinMode(MOSI, OUTPUT);
    spi_transaction(0xAC, 0×53, 0×00, 0×00);
    pmode = 1;

    void end_pmode() {
    pinMode(MISO, INPUT);
    pinMode(MOSI, INPUT);
    pinMode(SCK, INPUT);
    pinMode(RESET, INPUT);
    pmode = 0;

    void universal() {
    int w;
    uint8_t ch;

    ch = spi_transaction(buff[0], buff[1], buff[2], buff[3]);

    void flash(uint8_t hilo, int addr, uint8_t data) {
    addr>>8 & 0xFF,
    addr & 0xFF,
    void commit(int addr) {
    if (PROG_FLICKER) prog_lamp(LOW);
    spi_transaction(0x4C, (addr >> 8) & 0xFF, addr & 0xFF, 0);
    if (PROG_FLICKER) {

    //#define _current_page(x) (here & 0xFFFFE0)
    int current_page(int addr) {
    if (param.pagesize == 32) return here & 0xFFFFFFF0;
    if (param.pagesize == 64) return here & 0xFFFFFFE0;
    if (param.pagesize == 128) return here & 0xFFFFFFC0;
    if (param.pagesize == 256) return here & 0xFFFFFF80;
    return here;

    void write_flash(int length) {
    if (CRC_EOP == getch()) {
    Serial.print((char) STK_INSYNC);
    Serial.print((char) write_flash_pages(length));
    } else {
    Serial.print((char) STK_NOSYNC);

    uint8_t write_flash_pages(int length) {
    int x = 0;
    int page = current_page(here);
    while (x < length) {
    if (page != current_page(here)) {
    page = current_page(here);
    flash(LOW, here, buff[x++]);
    flash(HIGH, here, buff[x++]);


    return STK_OK;

    #define EECHUNK (32)
    uint8_t write_eeprom(int length) {
    // here is a word address, get the byte address
    int start = here * 2;
    int remaining = length;
    if (length > param.eepromsize) {
    return STK_FAILED;
    while (remaining > EECHUNK) {
    write_eeprom_chunk(start, EECHUNK);
    start += EECHUNK;
    remaining -= EECHUNK;
    write_eeprom_chunk(start, remaining);
    return STK_OK;
    // write (length) bytes, (start) is a byte address
    uint8_t write_eeprom_chunk(int start, int length) {
    // this writes byte-by-byte,
    // page writing may be faster (4 bytes at a time)
    for (int x = 0; x < length; x++) {
    int addr = start+x;
    spi_transaction(0xC0, (addr>>8) & 0xFF, addr & 0xFF, buff[x]);
    return STK_OK;

    void program_page() {
    char result = (char) STK_FAILED;
    int length = 256 * getch() + getch();
    char memtype = getch();
    // flash memory @here, (length) bytes
    if (memtype == ‘F’) {
    if (memtype == ‘E’) {
    result = (char)write_eeprom(length);
    if (CRC_EOP == getch()) {
    Serial.print((char) STK_INSYNC);
    } else {
    Serial.print((char) STK_NOSYNC);

    uint8_t flash_read(uint8_t hilo, int addr) {
    return spi_transaction(0×20 + hilo * 8,
    (addr >> 8) & 0xFF,
    addr & 0xFF,

    char flash_read_page(int length) {
    for (int x = 0; x < length; x+=2) {
    uint8_t low = flash_read(LOW, here);
    Serial.print((char) low);
    uint8_t high = flash_read(HIGH, here);
    Serial.print((char) high);
    return STK_OK;

    char eeprom_read_page(int length) {
    // here again we have a word address
    int start = here * 2;
    for (int x = 0; x < length; x++) {
    int addr = start + x;
    uint8_t ee = spi_transaction(0xA0, (addr >> 8) & 0xFF, addr & 0xFF, 0xFF);
    Serial.print((char) ee);
    return STK_OK;

    void read_page() {
    char result = (char)STK_FAILED;
    int length = 256 * getch() + getch();
    char memtype = getch();
    if (CRC_EOP != getch()) {
    Serial.print((char) STK_NOSYNC);
    Serial.print((char) STK_INSYNC);
    if (memtype == ‘F’) result = flash_read_page(length);
    if (memtype == ‘E’) result = eeprom_read_page(length);

    void read_signature() {
    if (CRC_EOP != getch()) {
    Serial.print((char) STK_NOSYNC);
    Serial.print((char) STK_INSYNC);
    uint8_t high = spi_transaction(0×30, 0×00, 0×00, 0×00);
    Serial.print((char) high);
    uint8_t middle = spi_transaction(0×30, 0×00, 0×01, 0×00);
    Serial.print((char) middle);
    uint8_t low = spi_transaction(0×30, 0×00, 0×02, 0×00);
    Serial.print((char) low);
    Serial.print((char) STK_OK);

    int avrisp() {
    uint8_t data, low, high;
    uint8_t ch = getch();
    switch (ch) {
    case ’0′: // signon
    error = 0;
    case ’1′:
    if (getch() == CRC_EOP) {
    Serial.print((char) STK_INSYNC);
    Serial.print(“AVR ISP”);
    Serial.print((char) STK_OK);
    case ‘A’:
    case ‘B’:
    case ‘E’: // extended parameters – ignore for now

    case ‘P’:
    case ‘U’: // set address (word)
    here = getch() + 256 * getch();

    case 0×60: //STK_PROG_FLASH
    low = getch();
    high = getch();
    case 0×61: //STK_PROG_DATA
    data = getch();

    case 0×64: //STK_PROG_PAGE

    case 0×74: //STK_READ_PAGE ‘t’

    case ‘V’: //0×56
    case ‘Q’: //0×51

    case 0×75: //STK_READ_SIGN ‘u’

    // expecting a command, not CRC_EOP
    // this is how we can get back in sync
    case CRC_EOP:
    Serial.print((char) STK_NOSYNC);

    // anything else we will return STK_UNKNOWN
    if (CRC_EOP == getch())

  4. Then I downloaded the ER9x programming software from here.
  5. When opened, it asked me to download the latest ER9x firmware, so, I clicked yes and saved it somewhere. It asks to download it again if there is a new version of the firmware.
  6. I went to burn->configure in the eepe software and selected the avrisp programmer, m64 mcu, the port my arduino is connected to, and -b 19200 in the extra arguments box.
  7. I then backed up the firmware, memory and EEPROM using the options in the burn menu of the stock firmware in case something went wrong with the ER9x firmware.
  8. I finally went to burn->Flash Firmware to TX and selected the firmware I saved in step 5. It said “complete” in a minute or two!


Things I needed:

  2. Some wire to connect the TX and USBASP
How I did it:
  1. I connected my USBASP to my TX via the ISP header.
  2. I plugged in my USBASP, and let it install drivers. I had some issues with the drivers on Windows 7, so I followed these instructions.
  3. Then I opened the eepee software and it asked me to download and save the ER9x firmware. It asks to download it again if there is a new version of the firmware.
  4.  I went to burn->Flash Firmware to TX and watched it work its magic :) I got some USB errors while it was burning, so I clicked on the blue gear towards the upper right of the program and selected USB under port.


Now I have Turnigy 9x that is even better than the original!  It has a stable battery, longer range, easier flight practice, easier programming connections, and in general is easier to use. I will soon be posting guides on how to use the ER9x firmware with a MWC quadcopter.



  12 Responses to “Flashing Turnigy 9x With ER9X And Other Mods”

  1. Hi, just finished setting up (Quad dji450 clone) my new Turnigy 9X to ER9X-r815 using SMARTIEPARTS Board. Splendid work. I have read tons of literature on ER9X and the added board, nowhere I have found a word telling me to keep the board in place to enablle the use of ER9X FW (flash and eeprom). MY QUESTION IS: May I uninstall the board, and still use all functionalities of ER9X FW ?
    I plan to upgrade my other Turnigy and FRSKY radio to ER9X or OpenTX, using the smartiesparts board. Thank you in advance for your advice – Thierry

  2. Hi, I have a weird problem with my 9x after flashing it!(or something went wrong while hacking for JTag connections)

    The Rudder channel(Mode 1) is almost always at -100 on the th9x and open-9x firmware!
    I forgot to back up my Flash and EEPROM,but after using the ones on the internet it goes blank.
    Since the ATMEGA64A shares the pin F7 with JTag functionality for programming,I think it might be a problem caused by hacking 0L0 PF7 is one of ADC pins.
    I measured the pots output with an ohm-meter but seems to be working fine,so it’s not a dirty pot!(I opened and cleaned it)

    could you plz help me :) with this ?!

    • hmmm. since the hardware is ok, id think it is either the firmware or that pin on the ATMega is fried.

      check the continuity from the pot to the atmega and try a different firmware. not sure about this issue.

  3. “I will soon be posting guides on how to use the ER9x firmware with a MWC quadcopter.”

    If you can spare the time and do that it would be awesome.
    Thank you

  4. 1k Ohm mod is now obsolete since there is a possibility in new er9x firmware to output ppm signal directly

  5. Hopefully this isn’t too off topic for this post, but I’m building a quadcopter for the first time and just received the Turnigy 9x mode 2 that I ordered from HobbyKing. I’ve never done any RC anything, so this is my first time buying and using a transmittiner like this. This is probably a really dumb question but I didn’t find anything googling. Also you’ll have to forgive me if I call something the wrong name or use the wrong term. I’m a big time noob. Anyway, the left stick doesn’t return to the center position when moving it vertically. The right stick returns to center whenever you let go. The left does return when just moving it laterally, but not vertically.

    Is it supposed to work that way? Is the left stick not supposed to return to center or six o’clock when you release it?

    I’ve got a cheap gyrocopter (an Interceptor 052) which is sort of what got me started into looking at building a quad, among other things. On that transmitter, the throttle on the left returns to a six o’clock position when you release it, and the right stick returns to center on release. I assumed this would be the same.

    Also, did you get any kind of manual with yours? HK sent me a link to a pdf for a “Eurgle” brand transmitter, but I’m not sure it’s for this transmitter. Their customer service hasn’t exactly been helpful.

    Anyway, if you’ve got time to help me out, I’d appreciate it.

    • It sounds like the sticks are working correctly. The throttle should stay wherever you move it. Most folks reflash these transmitters with the ER9X firmware because it is so powerful and feature rich. When the transmitter is reflashed it completely changes the way it operates. I have never used the standard firmware that comes on the transmitter, but I think they gave you the right manual. This transmitter is sold under different brand names. Here is the link the probably gave you:

    • yep that is how all TXs work. aileron(roll), elevator(pitch), and rudder(yaw) are all spring loaded back to a center position. throttle is not spring loaded for obvious practical reasons(holding the throttle stick for a 30min flight = ;() a mode 2 has the thr stick on the left and a mode 1 has it on the right (elevator moves over to the left side). still springy in the same way.

      i think that answers it.

    • Ok, thanks guys! That does answer it. I figured I’d get my quad built and up in the air before I thought about reflashing the transmitter. However, due to a couple of reasons this project is going to have to wait until winter I’m afraid. Thanks again!

  6. Instead of the simulator resistor hack I bought this wireless simulator adapter for my Turnigy 9X.

    Works great. Without the wires I can lay back on my couch and run Phoenix Rc on my big screen. However as usual it’s out of stock at Hobby King at the moment.

  7. im just a writer, Britt is the main writer/founder/designer. but they definitely need improving haha

 Leave a Reply



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>