4

I am reverse engineering IR protocol of Sanyo air conditioner. AC sends the whole current state of remote on each key press. Data sent is 67 bits long, last 4 bits seem to be the some kind of error checking.

I figured most things out, but I can not find how the error checking is done. Temp + mode are part of it, but I can not find a solution to the entire puzzle. Also any suggestions as to what col. 16 may be (am i missing functionality in the overview?) would be great.

Here are sample values:

 1  2  3 4 5   6   7   8   9  10 11 12      13      14    15  16        17          18
100 1 00 0 0 1011 00 1111 000 1   1  0 00001010010 0000 00000 0 000100000000000000 1010 ->
100 1 00 0 0 1011 00 1111 000 1   1  1 00001010010 0000 00000 0 000100000000000000 1010 ->
100 1 00 1 0 0000 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 1010
100 1 00 1 0 1000 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0110
100 1 00 1 0 0100 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 1110
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 00 0 0 0011 00 1111 000 1   1  0 00001010010 0000 00000 0 000100000000000000 0010 ->
100 1 00 0 0 0011 00 1111 000 1   1  0 00001010010 0000 00000 0 000100000000000000 0010 ->
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 0100 00000 1 000100000000000000 0100
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 1100 00000 1 000100000000000000 0100
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 0010 00000 1 000100000000000000 0100
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 1010 00000 1 000100000000000000 0100
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 0110 00000 1 000100000000000000 0100
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1000 00000 1 000100000000000000 0100
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1101 00000 1 000100000000000000 0100
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1001 00000 1 000100000000000000 0100
100 1 00 0 0 1011 00 0000 000 1   1  0 00001010010 0000 00000 1 000100000000000000 0100
100 1 10 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 01 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 11 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 1 000100000000000000 0100
100 1 00 0 0 0011 00 0000 000 0   1  0 00001010010 0000 00000 0 000100000000000000 1000
100 1 00 0 0 0011 00 0000 000 1   1  0 00001010010 0000 00000 0 000100000000000000 1000
000 1 10 0 0 1001 00 0000 000 1   1  0 00001010010 0000 00000 0 000100000000000000 1011
100 1 00 0 0 0011 00 0000 000 1   1  0 00001010010 0000 00000 0 000100000000000000 1000
010 1 10 0 0 0100 00 0000 000 1   1  0 00001010010 0000 00000 0 000100000000000000 0001
110 1 00 1 0 1011 00 0000 000 1   1  0 00001010010 1110 00000 0 000100000000000000 0010
001 1 00 0 0 0011 00 0000 000 1   1  0 00001010010 0000 00000 0 000100000000000000 0010
100 0 00 1 0 1011 00 0000 000 1   0  0 00001010010 1110 00000 1 000100000000000000 0101

What I found out so far:

1: mode (cool, dry, fan, etc.)
2: appliance on
3: fan speed (00 = auto)
4: sweep motor
5: constant 0
6: temperature (16 + bit pattern)
7: constant 00
8: 0000, but 1111 if an additional packet is sent with time data ( -> )
9: constant 000
10: light on
11: appliance on
12: blower on
13: constant
14: direction of airflow (low, high, and various angles)
15: constant
16: yet unknown
17: constant
18: error checking (at least temp + mode is a component of this magic number)

EDIT1 Stripped rows/cols that don't add information:

 1  2   6   8   11 16  18
100 1 1011 1111  1  0 1010 ->
100 1 0000 0000  1  1 1010
100 1 1000 0000  1  1 0110
100 1 0100 0000  1  1 1110
100 1 0011 1111  1  0 0010 ->
100 1 1011 0000  1  1 0100
100 1 0011 0000  1  0 1000
000 1 1001 0000  1  0 1011
010 1 0100 0000  1  0 0001
110 1 1011 0000  1  0 0010
001 1 0011 0000  1  0 0010
100 0 1011 0000  0  1 0101
1
  • Please take a look a link, it should help in your reverse engineering – Sergey Apr 16 '18 at 23:18
1

I approached this from the perspective of a boolean logic simplifier: The more values you can trim, the less inputs your checksum function has and the simpler it will be to understand.

Not a complete solution by any measure, but a few thoughts:

  • Trim the rows with identical values from your dataset. They're just noise.
  • Columns 5, 7, 9, 13, 15, and 17 have the same values for every row, so you can only guess as to how they are factored into the checksum. They therefore do not help with determining how the other values are used.
  • Columns 3, 4, 10, 12, and 14 are not included in the checksum, because there are rows with all other values the same and the same checksum.
  • Columns 1, 6, and 8 are definitely included in the checksum, because there are rows with all other values the same, but have a different checksum.

TL;DR : Simplify your dataset.

1

I know this question is old but I found it by sheer chance while working out the bit mappings for this same kind of AC. Look at the post by Catalyn here: https://myesp8266.blogspot.com.ar/2016/05/keep-your-house-cool-with-esp8266.html . It seems to be about the same AC protocol and there's something in there about how to calculate the CRC. I haven't tried it yet as I'm currently mapping the bits to the functionality. You have a few I hadn't found yet and saved me some time. BTW Your constant 0 in the 5th column is actually the sleep function.

Edit: I have this sorted out! I found some other good sources of intel, some with crappy code that sometimes worked, and some good documentation with missing assumptions. Merged them and now I have working code. I ignored some bits that I didn't care about but you can figure out how to add them. The CRC calculation I made already takes all into account regardless of them being fixed.

This (java)script will generate a packet according to the the parameters with a proper CRC:

const header = [9050, 4450];
const zero_bit = [700, 550];
const one_bit = [700, 1650];

const separator = [700, 19900];

const MODE = {
  AUTO:   0x0,
  COOL:   0x1,
  DRY:    0x2,
  FAN:    0x3,
  HEAT:   0x4
};

const POWER = {
  OFF: 0x0,
  ON: 0x1
};

const FAN = {
  AUTO: 0x0,
  SPEED_1: 0x1,
  SPEED_2: 0x2,
  SPEED_3: 0x3,
  SPEED_4: 0x4,
};

const SWING = {
  OFF: 0x0,
  ON: 0x1
};

const SLEEP = {
  OFF: 0x0,
  ON: 0x1
};

const TURBO = {
  OFF: 0x0,
  ON: 0x1
};

const LIGHT = {
  OFF: 0x0,
  ON: 0x1
}

const HEALTH = {
  OFF: 0x0,
  ON: 0x1
}

const XFAN = {
  OFF: 0x0,
  ON: 0x1
}

const TEMPERATURE = {
  BASE: 16
};

let state = {
  mode: MODE.COOL,
  power: POWER.ON,
  temperature: 27 - TEMPERATURE.BASE,
  xfan: XFAN.ON,
  health: HEALTH.ON,
  light: LIGHT.ON,
  turbo: TURBO.OFF,
  swing: SWING.ON,
  fan: FAN.AUTO
};

function reverseBits(x, bitLength) {
    let result = 0;

    for (let index = 0; index < bitLength; ++index) {
      result |= ((x >> index) & 1) << (bitLength - index - 1)
    }
    return result
}

function reverseByte(value) {
  return reverseBits(value, 8);
}

function reverseNibble(value) {
  return reverseBits(value, 4);
}

function buildPacket(state) {
  const byte1 = (reverseBits(state.mode, 3) << 5) | (reverseBits(state.power, 1) << 4) | (reverseBits(state.fan, 2) << 2) | (reverseBits(state.swing, 1) << 1) | (reverseBits(state.sleep, 1) << 0);
  const byte2 = (reverseBits(state.temperature, 4) << 4);
  const byte3 = (reverseBits(state.turbo, 1) << 3) | (reverseBits(state.light, 1) << 2) | (reverseBits(state.health, 1) << 1) | (reverseBits(state.xFan, 1) << 1);
  const byte4 = 0x0A;

  const byte5 = 0;
  const byte6 = 4;
  const byte7 = 0;
  const byte8 = reverseNibble(
  (
    (reverseByte(byte1) & 0x0F) +
    (reverseByte(byte2) & 0x0F) + 
    (reverseByte(byte3) & 0x0F) +
    (reverseByte(byte4) & 0x0F) +
    ((reverseByte(byte5) & 0xF0) >> 4 ) +
    ((reverseByte(byte6) & 0xF0) >> 4 ) + 
    ((reverseByte(byte7) & 0xF0) >> 4 ) + 10
  ) & 0x0F);

  let data = [];
  data = data.concat(header);
  data = pushBits(data, byte1);
  data = pushBits(data, byte2);
  data = pushBits(data, byte3);
  data = pushBits(data, byte4);
  data = data.concat(zero_bit);
  data = data.concat(one_bit);
  data = data.concat(zero_bit);
  data = data.concat(separator);
  data = pushBits(data, byte5);
  data = pushBits(data, byte6);
  data = pushBits(data, byte7);
  data = pushBits(data, byte8);
  data = data.concat([650]);

  return data;
}

function pushBits(data, byte) {
  for (let i = 7; i >= 0; --i) {
    if ((byte & (1 << i)) !== 0) {
      data = data.concat(one_bit);
    }
    else {
      data = data.concat(zero_bit);
    }
  }
  return data;
}

const data = buildPacket(state);
console.log(data)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.