Electronic – Reverse-engineering IR check bits/CRC

crcinfraredprotocol

I'm trying to build a simple Arduino-based IR remote control for a cheap toy IR-controlled helicopter (same as this one – it's called "Diamond Gyro" or "Diamond Force"). I've decoded the IR protocol except for the last few bits. These last bits seem to be a check or CRC, but I haven't been able to "crack" it.

It's easy enough to simply repeat a recorded packet, but I want to fully control the helicopter. That means reverse-engineering the check bits.

(I should add that I do software by day, but electronics is a sometimes-hobby, so maybe I'm just missing something very basic.)

The details of the protocol are in the question and answer, but here are the basics:

  • 32 bit packet spanning multiple individual values/commands of varying length (plus 1 start bit/preamble, which doesn't count as data)
  • The values are little endian (MSB first)
  • I'm positive I've got the first 22 bits mapped…
  • … but the following 4 bits are a little mysterious, although I know the purpose of at least 2 of them.
  • The last 6 bits do seem to be a check or CRC of some kind

Here's a diagram

 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 | C |X|X|  CRC?

P: Preamble (always 1), T: Trim, C: Channel,
X: Either part of the channel data or the checksum.

First of all, I'm not sure whether the X bits are part of the channel value, part of the check, or something else. They always follow the channel value though, so it's likely the channel value is 3-4 bits wide, although 2 bits would be sufficient for the 3 possible channel values.

Second, there are the last 6 bits (marked CRC? above) which are clearly some kind of check (and indeed, the helicopter doesn't respond if I change any of those bits).

So basically, there's a packet of 24-26 data bits, followed by 6-8 check bits. And I'd really like to figure those check bits out so I can compose packets myself.

Below are a bunch of samples of the binary data I'm getting. The preamble "1" is always present, and I don't believe it counts as part of the data, but I've included it anyway, just in case it's the key to everything.

Again, I don't know if the X bits are part of the data or the check. Depending on the way the check's calculated, it might be that the first 1-2 bits of the check just happen to follow the channel value. But it's also quite possible that the channel value is 4 bits long, incorporating the X bits. Or it's in between with one X bit being part of the channel, and the other being part of the check. I don't know.

If anyone knows what that check is, or how I could go about finding out, I'd love to know. I imagine I can brute-force it, but even if that's the only option, I'd love to hear some hints for how to best do that.

The helicopter is dirt cheap, so I doubt there's anything really fancy going on.

Channel A                                                       
P  Yaw     Throttle  Pitch   Tr  Ch  XX  Check     Description
--------------------------------------------------------------
1  000100  10000100  000000  00  01  01  000101    Left Mid + throttle 
1  000000  10000110  010001  00  01  01  010010    Left Max + throttle 
1  100001  10000110  000000  00  01  01  100010    Right Mid + throttle 
1  100100  10000100  010001  00  01  01  110100    Right Max + throttle
1  010001  00000000  001011  00  01  01  011111    Forward Min 
1  010001  00000000  000000  00  01  01  010100    Forward Max 
1  010001  00000000  011000  00  01  01  001100    Back Min 
1  010001  00000000  100101  00  01  01  110001    Back Max
1  010001  00000000  010001  01  01  01  010101    Left Trim 
1  010001  00000000  010001  10  01  01  100101    Right Trim 
1  010001  00000011  010001  00  01  01  000110    Throttle 01 (min)
1  010001  00010110  010001  00  01  01  010011    Throttle 02
1  010001  00011111  010001  00  01  01  011010    Throttle 03
1  010001  00101111  010001  00  01  01  101010    Throttle 04
1  010001  00111110  010001  00  01  01  111011    Throttle 05
1  010001  01010101  010001  00  01  01  010000    Throttle 06
1  010001  01011111  010001  00  01  01  011010    Throttle 07
1  010001  01101100  010001  00  01  01  101001    Throttle 08
1  010001  01111010  010001  00  01  01  111111    Throttle 09
1  010001  10000101  010001  00  01  01  000000    Throttle 10 (max)

Channel B
P  Yaw     Throttle  Pitch   Tr  Ch  XX  Check     Description
--------------------------------------------------------------
1  000000  10000110  010001  00  00  10  010101    Left Max + throttle 
1  100100  10000110  010001  00  00  10  110001    Right Max + throttle 
1  010001  00000000  001001  00  00  10  011010    Forward Min 
1  010001  00000000  000000  00  00  10  010011    Forward Max 
1  010001  00000000  010111  00  00  10  000100    Back Min 
1  010001  00000000  100110  00  00  10  110101    Back Max
1  010001  00000000  010001  01  00  10  010010    Left Trim 
1  010001  00000000  010001  10  00  10  100010    Right Trim 
1  010001  00000001  010001  00  00  10  000011    Throttle Min 
1  010001  00110100  010001  00  00  10  110110    Throttle Mid 
1  010001  01100111  010001  00  00  10  100101    Throttle High 
1  010001  10001111  010001  00  00  10  001101    Throttle Max 

Channel C
P  Yaw     Throttle  Pitch   Tr  Ch  XX  Check     Description
--------------------------------------------------------------
1  000000  10000101  010001  00  10  00  011100    Left Max + throttle
1  100100  10000101  010001  00  10  00  111000    Right Max + throttle 
1  010001  00000000  001010  00  10  00  010011    Forward Min 
1  010001  00000000  000000  00  10  00  011001    Forward Max 
1  010001  00000000  010111  00  10  00  001110    Back Min 
1  010001  00000000  100110  00  10  00  111111    Back Max
1  010001  00000000  010001  01  10  00  011000    Left Trim 
1  010001  00000000  010001  10  10  00  101000    Right Trim 
1  010001  00000001  010001  00  10  00  001001    Throttle Min 
1  010001  00110100  010001  00  10  00  111100    Throttle Mid 
1  010001  01100110  010001  00  10  00  101110    Throttle High 
1  010001  10000101  010001  00  10  00  001101    Throttle Max

Best Answer

First of all, it's pretty clear that the "XX" bits are part of the channel designation, since that's the only thing they depend on. The "XX" bits may simply be a check on the "Ch" bits.

The check bits are a simple bitwise XOR of 24 of the 26 data bits: If you take the 6 Yaw bits, the 6 LSBs of the Throttle, the 6 Pitch bits, and the next 6 bits, and XOR these quantities together, you get the 6 check bits. It appears that the upper 2 bits of the Throttle do not affect the check bits at all.

The following Perl script verifies this.

#!/usr/bin/perl

# crc.pl - verify decoding of check bits

# On the lines starting with '1', just keep the '0's and '1's in an array. 
while (<>) {
    my @letters = split '', $_;
    next unless $letters[0] eq '1';
    @letters = grep /[01]/, @letters;
    @letters = @letters[1..32];
    $a = string2bin (@letters[0..5]);
    $b = string2bin (@letters[8..13]);
    $c = string2bin (@letters[14..19]);
    $d = string2bin (@letters[20..25]);
    $e = string2bin (@letters[26..31]);
    $f = $a ^ $b ^ $c ^ $d;
    printf "%02X %02X %02X %02X %02X %02X %s\n", $a, $b, $c, $d, $e, $f,
        $e == $f ? '-' : '###';
}

sub string2bin {
    my $temp = 0;
    for (@_) {
        $temp = ($temp << 1) + ($_ eq '1' ? 1 : 0);
    }
    $temp;
}