Saturday 4 July 2015

signal - Help with or hints for decoding an IR protocol


Some time ago I bought a simple and cheap little IR-controlled toy helicopter (same as this one - it's called "Diamond Gyro" or "Diamond Force"). For fun, I've been looking into controlling it via an Arduino.



Update: Got the protocol figured out; see answer


Others have already shared their results on hacking a different IR toy helicopter and decoding its IR protocol. Really cool, but unfortunately my helicopter uses a different protocol. One that I can't quite figure out. (I should add that electronics is purely a sometimes-hobby for me, so I may have overlooked something obvious).


Much like in the 2nd link above, I took the controller apart, located the IC pin controlling the LEDs (the IC's markings have been erased, by the way), and hooked up a logic analyzer.


Got lots of good data, but I still can't figure out the protocol. This site is a great resource, but none of the protocols listed seem to fit. And nothing else I've found seems to fit the signal I've captured either. I have to imagine, though, that it's a simple, off-the-shelf protocol, only because it's a cheap little toy.


So I'd appreciate any ideas you may have. Maybe I'm just looking at it wrong.
(More info below the image)


Samples from channel A


Signal/protocol characteristics


I captured this at 16MHz with the controller set to channel A; should be accurate, timing-wise. (There are 3 IR channels you can pick from, but using the two other channels don't change the characteristics, only parts of the packet itself.) The timings are very consistent (+/- 10µs max). Packets are repeated with varying intervals but at minimum they're about 100ms apart.


Carrier: 38kHz @ 50% duty-cycle



Lows:
- Short: 285µs
- Long: 795µs


Highs:
- Short: 275µs
- Long: 855µs


Always 17 highs per packet.


Controls/inputs


The heli's got 3 controls: "Throttle" (i.e. lift/rotor speed), pitch (forward/back), and yaw (rotation around the rotor axis) all controlled with 2 thumbsticks. They all have some kind of range (not just on/off) and are, as far as I can tell, all being transmitted in a single packet. The left/right inputs are only sent if something else is being sent, so I applied max throttle when sampling that. Throttle and pitch input on its own trigger packets being sent, as soon at you push the thumbsticks past some threshold/deadband (in the graph below the "min" label is for first packet sent when slowly pushing a control past its deadband).


It's also got buttons for trimming left & right, as the heli's not a precision instrument (at all) and tends to spin slowly otherwise. The left/right trim buttons unfortunately don't seem to send a signal that's incrementing/decrementing something for each press (which would be handy for figuring the protocol); it seems to just be single command, telling the helicopter to trim left/right, and then it keeps track of it.




Answer



I'm taking the liberty of answering my own question as I got most of it figured out and this is a good way to share my findings. My thanks to Olin Lathrop for giving me a place to start and some ideas to try out, but ultimately, the protocol turned out quite different from Olin's guess, hence me posting this answer.




Update: I posted a follow-up question regarding the last 8 bits, which I didn't fully understand, and Dave Tweed figured it out. I'll include the details here, so this answer can work as full protocol spec, but do go check out Dave's answer.




I had to try some different things to get this figured out, but I'm pretty confident that I got it. Oddly, I haven't found anything resembling this protocol elsewhere, but it may very well be a common protocol that I just don't know about.


Anyway, here's what I've found:


Protocol/encoding


Both pulses and the spaces in between are used to encode the data. A long pulse/space is binary one (1), and a short pulse/space is binary zero (0). The pulses are sent using standard consumer infrared 38kHz modulation @ 50% duty-cycle.


The pulse/space timings are in the original question, but I'll repeat them here for completeness:



 Bit    Pulse     Space
-----+---------+---------
0 | 275µs | 285µs
1 | 855µs | 795µs

All ±10µs max., ±5µs typ.. This is based on samples captured with a logic analyzer at 16MHz; I don't have an oscilloscope, so I don't know the exact profile (i.e. rise/fall times).


Packets are repeated as long as the control inputs are applied and appear to be spaced a minimum of 100ms apart.


Packet transmission starts with a "pulse 1" preamble, which is fixed and not part of the data. The following space encodes the first data bit of packet, and the last pulse encodes the last bit.


Each packet is 32 bits long, and contains every input the remote control can provide. Values are read as little endian, i.e. MSB first.


Data structure



Below is the basic structure of the individual packets. The last 8 bits had me confused, but that's been figured out now (see below).


 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
--+---------------------------+-----------+---+-------+-----------
P| Yaw | Throttle | Pitch | T | Chan. | Check

P: Preamble (always a pulse-1), T: Trim, Chan.: Channel

Bit Length Description (see note below)
-----------------------------------------------

0 1 Preamble. High 1
1-6 6 Yaw. Range 0-36 for left-right, 17 being neutral
7-14 8 Throttle. Range 0-134
15-20 6 Pitch. Range 0-38 for forward-back, 17 being neutral
21-22 2 Trim. Left = 1, right = 2, no trim = 0
23-26 4 Channel. A = 5, B = 2, C = 8
27-32 6 Check bits

Note: Ranges are based on the highest readings I got. The protocol is capable of larger ranges - up to 255 for throttle, 63 for pitch/yaw - but cap out at about half that.
The pitch value appears to have a deadband from 14-21 (inclusive); only values of above or below actually makes the helicopter react. I don't know if it's the same for the yaw (hard to tell, as the helicopter is unstable anyway, and may just spin slightly on its own).



Here it is in graphical terms (compare with the graphic in the original question)


packet structure


The 6 check bits are calculated by XOR'ing all the preceding values. Each value is treated as 6 bits. This means that the 2 MSBs of the 8-bit throttle value are simply ignored. I.e.


check = yaw ^ (throttle & 0x3F) ^ pitch ^ trim ^ channel

Practical notes


The signal timings and modulation doesn't need to be super accurate. Even my Arduino's not-at-all-accurate timing works fine despite dodgy modulation and a bit of hit and miss on the pulse/space durations compared to the real remote control.


I believe - but haven't tested - that the helicopter will simply latch on to the channel of the first signal it finds. If it's left without a signal for too long (couple of seconds), it appears to go back to its "search" mode, until it acquires a signal again.


The helicopter will ignore pitch and yaw values if the throttle is zero.


The trim commands are sent only once per button-press on the remote control. Presumably the trim value simply increments/decrements a value in the helicopter's own controller; it's not something the remote control keeps track of. So any implementation of this should probably stick to that scheme, and only send the occasional trim left/right value, but otherwise default to a zero trim value in the packets.



I recommend having a kill switch that simply sets throttle to zero. This will cause the helicopter to drop out of the sky, but it will sustain less damage when it's not spinning its motors. So if you're about to crash or hit something, hit the kill switch to avoid stripping the gears or breaking the blades.


The original remote control's IR LEDs appear to have a >900nm wavelength, but I've got no problems using a ~850nm LED.


The helicopter's IR receiver is ok, but not super sensitive, so the brighter your IR source, the better. The remote control uses 3 LEDs in series, sitting on the 9V rail instead of the 5V rail used by the logic. Haven't checked their current draw very precisely, but I'd wager it's 50mA.


Sample data


Here are a bunch of packets, for anyone interested (yes, I scripted a decoder; I didn't hand-decode all this). The channel A packets come from the same captures as the graphs in the original question.


Channel A                                                       
Yaw Throttle Pitch Tr Chan Check Description
-----------------------------------------------------------
000100 10000100 000000 00 0101 000101 Left Mid + Throttle
000000 10000110 010001 00 0101 010010 Left Max + Throttle

100001 10000110 000000 00 0101 100010 Right Mid + Throttle
100100 10000100 010001 00 0101 110100 Right Max + Throttle
010001 00000000 001011 00 0101 011111 Forward Min
010001 00000000 000000 00 0101 010100 Forward Max
010001 00000000 011000 00 0101 001100 Back Min
010001 00000000 100101 00 0101 110001 Back Max
010001 00000000 010001 01 0101 010101 Left Trim
010001 00000000 010001 10 0101 100101 Right Trim
010001 00000011 010001 00 0101 000110 Throttle 01 (min)
010001 00010110 010001 00 0101 010011 Throttle 02

010001 00011111 010001 00 0101 011010 Throttle 03
010001 00101111 010001 00 0101 101010 Throttle 04
010001 00111110 010001 00 0101 111011 Throttle 05
010001 01010101 010001 00 0101 010000 Throttle 06
010001 01011111 010001 00 0101 011010 Throttle 07
010001 01101100 010001 00 0101 101001 Throttle 08
010001 01111010 010001 00 0101 111111 Throttle 09
010001 10000101 010001 00 0101 000000 Throttle 10 (max)

Channel B

Yaw Throttle Pitch Tr Chan Check Description
-----------------------------------------------------------
000000 10000110 010001 00 0010 010101 Left Max + Throttle
100100 10000110 010001 00 0010 110001 Right Max + Throttle
010001 00000000 001001 00 0010 011010 Forward Min
010001 00000000 000000 00 0010 010011 Forward Max
010001 00000000 010111 00 0010 000100 Back Min
010001 00000000 100110 00 0010 110101 Back Max
010001 00000000 010001 01 0010 010010 Left Trim
010001 00000000 010001 10 0010 100010 Right Trim

010001 00000001 010001 00 0010 000011 Throttle Min
010001 00110100 010001 00 0010 110110 Throttle Mid
010001 01100111 010001 00 0010 100101 Throttle High
010001 10001111 010001 00 0010 001101 Throttle Max

Channel C
Yaw Throttle Pitch Tr Chan Check Description
-----------------------------------------------------------
000000 10000101 010001 00 1000 011100 Left Max + Throttle
100100 10000101 010001 00 1000 111000 Right Max + Throttle

010001 00000000 001010 00 1000 010011 Forward Min
010001 00000000 000000 00 1000 011001 Forward Max
010001 00000000 010111 00 1000 001110 Back Min
010001 00000000 100110 00 1000 111111 Back Max
010001 00000000 010001 01 1000 011000 Left Trim
010001 00000000 010001 10 1000 101000 Right Trim
010001 00000001 010001 00 1000 001001 Throttle Min
010001 00110100 010001 00 1000 111100 Throttle Mid
010001 01100110 010001 00 1000 101110 Throttle High
010001 10000101 010001 00 1000 001101 Throttle Max




As mentioned above, the last 8 bits have been figured out, but just for posterity, here are my original thoughts. Feel free to ignore it completely, as I was pretty much wrong in my guesses.


The last 8 bits


The last 8 bits of the packet are still a bit of mystery.


The 4 bits from bit 23 to 26 all appear to be entirely determined by the remote control's channel setting. Changing the channel on the remote control doesn't alter the protocol or modulation in any way; it only changes those 4 bits.


But 4 bits is double what's actually needed to encode the channel setting; there are only three channels, so 2 bits is plenty. Hence, in the structure description above, I've only labelled the first 2 bits as "Channel", and left the other two labelled as "X", but this is a guess.


Below is a sample of the relevant bits for each channel setting.


Chan.   Bits 23-26
-----+-------------

A | 0 1 0 1
B | 0 0 1 0
C | 1 0 0 0

Basically, there are 2 bits more than there needs to be to transmit the channel setting. Maybe the protocol has 4 bits set aside to allow for more channels later, or so the protocol can be used in entirely different toys, but I simply don't know. For the larger values, the protocol does use extra bits that could be left out (yaw/throttle/pitch could get by with a bit less each), but for trim - which also has 3 states - only 2 bits are used. So one could suspect that the channel is also just 2 bits, but that leaves the next 2 unaccounted for.


The other possibility is that the packet's checksum is 8 bits long, beginning with the "X bits", and - through the checksumming magic - they just happen to somehow always reflect the channel setting. But again: I don't know.


And speaking of: I have no idea how those check bits are formed. I mean, they are check bits, since they don't correspond to any single control input, and the helicopter doesn't seem to respond if I fiddle with them. I'm guessing it's a CRC of some kind, but I haven't been able to figure it out. The check is 6-8 bits long, depending on how you interpret the "X bits", so there are a lot of ways that could be put together.


No comments:

Post a Comment

arduino - Can I use TI's cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...