The write up for this project is split up into 4 parts – part 1: hacking the controller, part 2: decoding the protocol, part 3: the protocol definition, and part 4: hacks using the protocol. That way you can just jump to whatever interests you, or follow the whole thing first start to finish. There’s also a bonus post on achieving a similar goal by emulating the controller joysticks using an Arduino Uno.
- 1 Introduction
- 2 Prior Art
- 3 Step 1: Observation
- 4 Step 2: Examining the controller
- 5 Step 3: Sniffing the bus
The latest fad in our lab is the amazing Hubsan X4 H107L micro-quadcopter – cheap, fun, decent quality, and some of the coolest technology I’ve seen packed into something so small. This was my first brush with quad-rotors, and I think I’ve got the bug.
Of course, no gadget is particularly interesting unless you can hack it, so the first thing on my mind was how the control protocol works.
A quick Google search showed that there has already been some great work done on investigating and re-implementing the Hubsan X4 protocol:
- PhracturedBlue (DeviationTX Project)
As far as I can tell, the earliest attempts at discovering the mechanics of the Hubsan protocol for the Deviation 3rd-party firmware for Walkera RC controllers.
- Instructables/GitHub peeps
A great guide on how to implement the protocol, with some really good improvements to PhracturedBlue/the Deviation project’s A7105 and Hubsan protocol Arduino libraries in the comments.
These resources were really helpful in getting my head around the protocol and interfacing the A7105 with an Arduino – but they were just a bit light on reverse-engineering detail and I wanted to know how this thing worked inside and out.
Step 1: Observation
The first step in reverse-engineering the protocol is to understand what the controller and X4 can actually do. This means flying and crashing it an awful lot (at the lab we just broke our last replacement rotor… so there’s that).
Flying the quad-rotor is a lot of fun – but it takes a little getting used to if you’ve never flown one before. There are a ton of great tips in the manual on how to keep the thing in the air, and even do 360-degree flips.
The Quadrotor has 4 flight control axis’ – Elevation, Yaw, Pitch, and Roll. It also has trim control on all axes in fine increments. With a push of the left stick, you can swap the control scheme to your preference. You can also calibrate the sensitivity of each axis too. The X4 also has 2 flight modes – “anti-flip” and “expert”. Anti-flip is the default mode and is relatively easy to control, whilst Expert mode allows much faster and greater range of movement. You need to be in Expert mode to perform a flip by applying a bunch of throttle then rocking either pitch or slide quickly from one side to the other.
Binding the controller with the quadrotor is a matter of powering them both on and ensuring the quadrotor is on a flat surface (presumably for the internal gyro calibration). The lights on the Quad should stop flashing and you’re ready to go. If you kill the power to the controller mid-flight, the quad will just drop out of the sky. Similarly, if you power off the controller and then power it back on, the quadrotor won’t respond.
With these observations we can make the following guesses (which may be wrong!):
- Trim, Calibration, and Control-mapping are done on-controller
The settings seem to be remembered between flights, even when the battery is pulled from the Quad –it seems unlikely the vehicle has any non-volatile memory.
- The controller is communicating constantly.
Dropping the power on the controller results in an immediate loss of quad flight – it doesn’t hold station or perform a graceful emergency landing.
- The controller and quadrotor only bind once.
Cycling the power on the controller once it has already bound to the quad results in the quad being unresponsive until the power is cycled on that too. The binding negotiation isn’t remembered between power-cycles.
- Expert mode/Anti-Flip mode is probably just a change to the calibration range or sensitivity of the controls.
Watching a 360 flip in action, it looks like you have to use the quadcopter’s inertia by shifting it’s mass one way, then using it’s agility to rock the quadcopter into a roll in the opposite direction. As far as I can tell, this ability is provided purely by the power and agility of the quad’s motors.
Step 2: Examining the controller
When reversing toy RC helicopter/quadrotors, we can almost always assume that the controller is going to do the lion’s share of the communicating. It will almost certainly be a lot easier to look at than the control board on the vehicle too.
Taking the controller apart, we can see that there is a single PCB with all of the control input components surface-mounted directly on it. This includes the LCD screen, a bunch of micro-switches, 2 dual-axis thumbsticks, and the on/off switch. You might notice that there are no external antenna wires and that little pointy antenna nub on the molding of controller is purely for decoration.
So we’re looking for the juicy bits – the main microcontroller, the RF hardware, and any debug ports/test access points that could be handy.
The Microcontroller Unit (mcu)
The brains of this thing appear to be an STMicroelectronics STM8S003. This is the value line of the 8-bit STM8S series of general-purpose embedded microcontrollers.
As this is a general-purpose part, we can hope that ST will have made the Datasheet available. Google shows that this is the case:
A quick glance at the marketing specs on page one of the datasheet shows that it supports UART, SPI and I2C communications. This kind of systems-engineering information is important to remember when we try and piece together how this thing works.
Unlike the big through-hole DIP IC in the Syma S107G controller, the MCU here is a small TSSOP20 package. This isn’t ideal, but it could be worse – if we can’t find a debug or test port, the IC pins are exposed so we could always solder some fine magnet wire onto them and sniff them that way.
The RF Transmitter (Tx)
The wiggly antenna traces give away the approximate location of the RF transmitter part. A Google search of the RF part shows that it is an AMIC A7105 2.4Ghz Transceiver module (transceiver means it can transmit and receive). Not only that, but the datasheet is freely available too:
This datasheet isn’t quite as easy to read as the ST one, but it still tells us everything we need to know. The summary on page 5 tells us that it supports 3-wire and 4-wire SPI communication – this is perfect. Not only is SPI one of the communication protocols that our MCU supports, it is also widely supported by logic analyzers.
Sadly, the pins on this package are inaccessible as it’s a tiny QFN package, so we’re stuck with reading the MCU pins or finding a debug port.
Any debug or test ports?
Now then – this row of unpopulated pads is interesting. It looks like traces from all over the board run towards it – on both sides and apparently on different PCB layers too. That is a great indicator that this is a debug/test port.
These kinds of ports are often used in manufacturing PCBs to aid in the efficient automated programming and/or testing that the assembled devices go through in the factory. Such ports are a goldmine for reverse-engineers and hackers as they can be a window into the devices soul…or in this case maybe the communications bus between the MCU and the Transceiver. They may be distributed across the board as a constellation of test pads (ready to be probed by a bed-of-nails test jig) or closely grouped like this one.
So what did we learn?
Now we’ve had a good look at the PCB and parts that make up the controller, lets summarize all the things we’ve learned about how this thing probably works:
The Microcontroller is an STMicroelectronics STM8S003-series 8-bit processor.
- The datasheet for it is here: http://www.st.com/web/en/resource/technical/document/datasheet/DM00024550.pdf
- It is a TSSOP20 package, so we have access to the pins directly if necessary.
- It supports UART, SPI, and I2C communications protocols.
The RF part is an AMIC A7105 2.4GHz Transceiver module.
- The datasheet for it is here: http://www.avantcom.com.tw/AVANTCOM/TC/DATA/PRODUCT/SOLVE/18_3.pdf
- It can be controlled over 3-wire and 4-wire SPI.
There is a promising-looking set of unpopulated pads on the top-left of the board that look like a debug/test port.
It should be pretty obvious to anyone reading that it’s probably not a coincidence that the Tx requires SPI, and the MCU supports it. But to remove any doubt we can take a look at the board to confirm our suspicions:
Here we can see a clear communications bus between the MCU and the Transceiver chip. As we have the Datasheet, let’s take a look at what the MCU pins connected to the bus traces do – counting from the pin marker dot at the top left, we can see the connected pins are 13, 15, and 16. Page 21 of the STM8S003 datasheet shows the pinouts for the TSSOP20 variant of the chip. We can see that pin 15 is “SPI_SCK”, pin 16 is “SPI_MOSI”, and pin 13 is “TIM1_CH3”. This is all we need for 3-wire SPI, so now we know exactly which pins to sniff with the logic analyzer if need to, and a good idea about what they will be doing.
Step 3: Sniffing the bus
As we’re interested in the data at a protocol level (we already know it must be SPI), we’re going to want to probe the test pads (or failing that the MCU pins) with a logic analyzer. Ultimately we want to design a test regime that will allow us to empirically manipulate the controls in isolation and observe the output. Understanding the relationship between changes to a given control axis and the resulting changes to the protocol data is crucial to reversing the protocol.
But first we need to get access to the test port while still being able to fly the quad – this means breaking out the test port.
Step 3a: breaking out the test port
What we need is a makeshift extension for the test port to make it accessible from outside the controller’s case. Ideally it should terminate in some kind of connector that makes it easy to access each pin individually, mitigate the chance of accidental short circuits, and help keep your work tidy.
As the pointy “antenna” nub part of the case is purely decorative and the test port is nearby, I think this is a good place for us to make a hole for the breakout without worrying about other components or structural parts of the case getting in the way. We’ll start by drilling a hole in the controller casing with incrementally increasing-gauge bits (starting with a small bit and getting larger reduces the chance of the plastic cracking and make it a bit easier to get a clean cut). A 6.5mm bit looks like it is wide enough to feed the 9 wires we’ll need through (8 test pads + GND reference).
Next let’s cut 8 pieces of insulated wire long enough to reach from the test port pads out through the hole in the case we drilled (to common length of about an inch outside the case). These are easily stripped and soldered onto the test port pads.
Very important! It’s essential we make a note of the order that breakout wires go in from top to bottom – we’ll be soldering them to the breakout connector in the same order. This is very important if you have multiple devices or communications buses broken out to the test port – you’re going to want to know which pin is which!
We also want to run a wire from the negative terminal of the battery compartment out through the hole too. This will be our ground reference for voltage and logic measurements.
Next we want to solder the wires (in order) to the breakout connector of choice. I really like using standard 0.1” pin header for this – it’s super cheap, easy to grab with probes, and not too hard to solder wires to. With a bit of flux, some Helping Hands, and some heat-shrink tubing, you can have a really nice and discreet breakout in no time.
Step 3b: Finding the signals
We’re almost ready to start sniffing for protocol data, but first we need to do a bit of due-diligence to make sure it’s safe to connect the logic probe. As with the Syma S107G, I’m going to be using my trusty Saleae Logic 8. The Logic 8 has a maximum input voltage of 5V, so we’re going to want to make sure none of the test points exceed that at any time during our testing.
NB. The Saleae Logic 8 doesn’t have great input protection, so I’m always a bit paranoid about this bit. Apparently the Logic 16 has much better safeguards against over-volt inputs.
To do this we can use a digital multimeter to monitor the voltage-level of each pin – in this case, we want to make sure the voltages don’t change after the controller has bound to the quad too.
Here’s the table I used to keep track of my observations:
This is looking good, and already I’d be willing to wager pins 2,3, and 4 are going to be interesting. Let’s wire up our logic analyzer harness:
To ensure we can accurately and fully sample the protocol we need a sample rate of at least that of the expected communications protocol. From the A7105 datasheet (in the SPI Timing Characteristics section), we can tell the maximum possible clock rate supported for the SPI input is 10 MHz. Let’s configure our capture with a sample rate of 24MHz for 500 million samples (giving us a capture window of 20.8 seconds).
The first capture will just be to see if we can even find the SPI bus on the test port – powering the controller on, wait a few seconds, and then powering it off:
There are clearly a lot of transitions on channels 4, 5, and 6 (corresponding to pins 2, 3, and 4 – sorry the pins and channel numbering is backwards…). This is a great sign that there’s digital communications occurring. Let’s look closer at those 3 channels:
This is looking great – and to explain why, I should probably talk about the SPI protocol briefly. Serial Peripheral Interface (SPI) is a clocked serial protocol that allows full-duplex (simultaneous two-way) communication and multiple devices on a shared bus. This diagram shows how multiple devices on an SPI bus can be logically connected:
The SPI master (usually the MCU) provides the oscillating clock signal for the bus. When we want to send a 1 or a 0, we pull the Master-Out-Slave-In (MOSI) or Master-In-Slave-Out (MISO) line HIGH (for a 1) or LOW (for a 0). The state of the MOSI/MISO line at the rising or falling edge (which one depends on implementation – check the datasheet) of each clock oscillation is the bit value that is transmitted. The Master signals that it wants to communicate with a Slave device by pulling the Chip Select pin (SS above) LOW (the default inactive state is usually HIGH for Chip Select).
For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus.
So back to the Logic Analyzer – we clearly have 3 interesting channels, each that look very different from each other:
Channel 5 – This one immediately jumps out as a clock signal – it’s regular and is apparently grouped into 8 oscillation sets (8 oscillations, 8 bits = 1 byte?). We can also tell from the period of the oscillations that the apparent clock rate is about 235kHz.
Channel 4 – This one is much less regular than Ch5 and often transitions in the middle of Ch5’s oscillations. That sounds a lot like a MISO/MOSI data line.
Channel 6 – This one just goes LOW for the duration of the transmission – looks just like you’d expect a Chip Select line to look.
Saleae Logic’s software package has built-in support for SPI protocol decoding. Let’s configure it with our guesses about the channel types. We’ll leave the other values as default for now – we can always check the datasheet if we have problems.
Now lets have another look at our capture:
Excellent – it looks like our guesses were correct. The packet we’re zoomed in on appears to be a 5 byte transmission:
0x06 0x55 0x20 0x10 0x41
Using the A7105 Datasheet (page 31, page 37, and page 16), we can decode this as:
0x06 = Command: write to ID Code (hex 06) control register. 0x55201041 = 4 byte/32-bit ID code data value.
More on how this is done in Part 2, but for now we’ve confirmed that our methodology is good: we now have a way to observe and record all communication between the MCU and transceiver, and we’ve just observed the MCU configuring the Transceiver’s ID code (and much more in the rest of the capture). In Part 2 I’ll cover how to devise a test regime to generate good data and how to logically dissect the captured traffic – hope you found this informative, and if you have any questions please leave a comment below.