Toshiba Air Conditioner IR signal Reverse Engineering

Introduction

I am in the process of installing some Toshiba air conditioners in my house, and am planning to control them remotely using OpenHab (http://www.openhab.org/).
The first step to be able to control the air conditioner is to be able to emulate IR signals as they are sent by the AC remote.

In order to do that I have modified an open source IR library (https://github.com/r45635/HVAC-IR-Control) to add support for the Toshiba AC units.

Air Conditioning remotes work in a slightly different way than standard remotes as they usually transmit a packet containing all information related to the AC as opposed to, say, TV remotes where every keypress transmits only a single information like a button press.
This is a typical packet sent from a Toshiba Remote (in Hexadecimal):
#KeyHex    # Bits
1fan1F2 0D 03 FC 01 00 40 00 41 F2 0D 03 FC 01 00 40 00 41    144
it is a 72 bit packet that is transmitted twice (I imagine to rule out transmission errors), this is the binary representation:

11110010 00001101 00000011 11111100 00000001 00000000 01000000 00000000 01000001 11110010 00001101 00000011 11111100 00000001 00000000 01000000 00000000
and this is the graphic representation, with timings for the packet header:

In this post I'll try to explain how I proceeded to analyse different signals and discover how to generate them using code as opposed to memorise all possible signals and repeat them back (not doable given all the possible permutations and the limited memory available on the ESP8266/Arduino platforms)


Tools used for the reverse engineering:


  • AnalysIR (paid version): a tool that really makes decoding IR signals easy for moderately competent software engineers

The tool facilitates recording and analising IR signals, it already has supports for a lot of IR protocols (TOSHIBA AC remotes included) and is able to:
    • Record IR signals using multiple IR sensors hardware (arduino,esp8266,raspberry, A.IR shields) on two separate channels for easy comparison
    • Analyze visually the signal and quickly calculate IR base settings like timings for marks, ones and zeros to e.g. configure common IR libraries
    • Export the signal to source code for various platform
    • help decoding the signal and identifying CRC methods

    • quickly find variations on different IR signals



  • A.IR Shield: an arduino nano shield that is able to receive and log IR signals to AnalysIR. It is not necessary for AnalysIr, but for 20 euro for the shield and an Arduino nano clone it can get you started really quickly in the IR decoding business. The A.IR shield also has IR sending capabilities if needed
  • SendIR module: an arduino/esp8266 controllable module that has two high power/high range IR emitters, used to replicate the AC remote IR signals

  • ESP8266 based dev board: used to control the SendIR module, any arduino will do, in my case I will use the ESP8266 to control multiple SendIr modules by forwarding data to it from Openhab

Protocol Basics

The Toshiba IR protocol, as far as I could understand uses three possible signal lengths
  • 144 bits for signals that control
    • power
    • temperature
    • ac mode (auto/hot/dry/cool)
    • fan speed 
  • 160 bits for signals that control eco/hi power mode
  • 112 bits for signals that control swing mode
I have worked in decoding and reproducing only the 144 bit signals, so far
The 144 bit signal is in reality a 72 bit (9byte signal) repeated twice

Let's consider this signal:

#KeyHex    # Bits
1fan1F2 0D 03 FC 01 00 40 00 41 F2 0D 03 FC 01 00 40 00 41    144
the first 9 bytes are:
F2 0D 03 FC 01 00 40 00 41
  • F2 0D 03 FC 01 : bytes 1-5 are fixed for the 144 bit signal
  • 00 : byte 6 stores two kind of information
    • first 4 bits: temperature in the range: 17-30

17F2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144

18F2 0D 03 FC 01 10 00 00 11 F2 0D 03 FC 01 10 00 00 11    144

19F2 0D 03 FC 01 20 00 00 21 F2 0D 03 FC 01 20 00 00 21    144

20F2 0D 03 FC 01 30 00 00 31 F2 0D 03 FC 01 30 00 00 31    144

21F2 0D 03 FC 01 40 00 00 41 F2 0D 03 FC 01 40 00 00 41    144

22F2 0D 03 FC 01 50 00 00 51 F2 0D 03 FC 01 50 00 00 51    144

23F2 0D 03 FC 01 60 00 00 61 F2 0D 03 FC 01 60 00 00 61    144

24F2 0D 03 FC 01 70 00 00 71 F2 0D 03 FC 01 70 00 00 71    144

25F2 0D 03 FC 01 80 00 00 81 F2 0D 03 FC 01 80 00 00 81    144

26F2 0D 03 FC 01 90 00 00 91 F2 0D 03 FC 01 90 00 00 91    144

27F2 0D 03 FC 01 A0 00 00 A1 F2 0D 03 FC 01 A0 00 00 A1    144

28F2 0D 03 FC 01 B0 00 00 B1 F2 0D 03 FC 01 B0 00 00 B1    144

29F2 0D 03 FC 01 C0 00 00 C1 F2 0D 03 FC 01 C0 00 00 C1    144
26ON->30F2 0D 03 FC 01 D0 00 00 D1 F2 0D 03 FC 01 D0 00 00 D1    

    • second 4 bits: on/off state
      • 00 is on
      • 02 is off
  • 40: byte 7 stores two kind of information
    • first 4 bits: fan speed
717-fan autoF2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
8fan5F2 0D 03 FC 01 00 C0 00 C1 F2 0D 03 FC 01 00 C0 00 C1    144
9fan4F2 0D 03 FC 01 00 A0 00 A1 F2 0D 03 FC 01 00 A0 00 A1    144
10fan3F2 0D 03 FC 01 00 80 00 81 F2 0D 03 FC 01 00 80 00 81    144
11fan2F2 0D 03 FC 01 00 60 00 61 F2 0D 03 FC 01 00 60 00 61    144
12fan1F2 0D 03 FC 01 00 40 00 41 F2 0D 03 FC 01 00 40 00 41    144
    • second 4 bits: AC mode
317-fan autoF2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
4hotF2 0D 03 FC 01 50 03 00 52 F2 0D 03 FC 01 50 03 00 52    144
5dryF2 0D 03 FC 01 50 02 00 53 F2 0D 03 FC 01 50 02 00 53    144
6coldF2 0D 03 FC 01 50 01 00 50 F2 0D 03 FC 01 50 01 00 50    144
  • 00: byte 8 unused as far as I can tell
  • 41: checksum, obtained by XORing the previous eight bytes

Reverse Engineering Process

I started by recording multiple signals from the Toshiba remote and assigning an identifier like hot-dry-cold-auto when cycling the AC mode setting



after that I used the reverse engineering tool to identify patterns in the signal:

#KeyHex    # Bits
1fan1F2 0D 03 FC 01 00 40 00 41 F2 0D 03 FC 01 00 40 00 41    144
217-fan autoF2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
317-fan autoF2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
4hotF2 0D 03 FC 01 50 03 00 52 F2 0D 03 FC 01 50 03 00 52    144
5dryF2 0D 03 FC 01 50 02 00 53 F2 0D 03 FC 01 50 02 00 53    144
6coldF2 0D 03 FC 01 50 01 00 50 F2 0D 03 FC 01 50 01 00 50    144
717-fan autoF2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
8fan5F2 0D 03 FC 01 00 C0 00 C1 F2 0D 03 FC 01 00 C0 00 C1    144
9fan4F2 0D 03 FC 01 00 A0 00 A1 F2 0D 03 FC 01 00 A0 00 A1    144
10fan3F2 0D 03 FC 01 00 80 00 81 F2 0D 03 FC 01 00 80 00 81    144
11fan2F2 0D 03 FC 01 00 60 00 61 F2 0D 03 FC 01 00 60 00 61    144
12fan1F2 0D 03 FC 01 00 40 00 41 F2 0D 03 FC 01 00 40 00 41    144
1317F2 0D 03 FC 01 00 00 00 01 F2 0D 03 FC 01 00 00 00 01    144
1418F2 0D 03 FC 01 10 00 00 11 F2 0D 03 FC 01 10 00 00 11    144
1519F2 0D 03 FC 01 20 00 00 21 F2 0D 03 FC 01 20 00 00 21    144
1620F2 0D 03 FC 01 30 00 00 31 F2 0D 03 FC 01 30 00 00 31    144
1721F2 0D 03 FC 01 40 00 00 41 F2 0D 03 FC 01 40 00 00 41    144
1822F2 0D 03 FC 01 50 00 00 51 F2 0D 03 FC 01 50 00 00 51    144
1923F2 0D 03 FC 01 60 00 00 61 F2 0D 03 FC 01 60 00 00 61    144
2024F2 0D 03 FC 01 70 00 00 71 F2 0D 03 FC 01 70 00 00 71    144
2125F2 0D 03 FC 01 80 00 00 81 F2 0D 03 FC 01 80 00 00 81    144
2226F2 0D 03 FC 01 90 00 00 91 F2 0D 03 FC 01 90 00 00 91    144
2327F2 0D 03 FC 01 A0 00 00 A1 F2 0D 03 FC 01 A0 00 00 A1    144
2428F2 0D 03 FC 01 B0 00 00 B1 F2 0D 03 FC 01 B0 00 00 B1    144
2529F2 0D 03 FC 01 C0 00 00 C1 F2 0D 03 FC 01 C0 00 00 C1    144
26ON->30F2 0D 03 FC 01 D0 00 00 D1 F2 0D 03 FC 01 D0 00 00 D1    144


and the checksum calculator tool to verify that the Checksum byte was indeed calculated with an XOR algorithm




Then I added a new method to the HVAC IR library that would translate this findings into an actual IR signal.

To do that I had to define a couple of constants:

       

#define HVAC_TOSHIBA_HDR_MARK    4400
#define HVAC_TOSHIBA_HDR_SPACE   4300
#define HVAC_TOSHIBA_BIT_MARK    543
#define HVAC_TOSHIBA_ONE_SPACE   1623
#define HVAC_MISTUBISHI_ZERO_SPACE  472
#define HVAC_TOSHIBA_RPT_MARK    440
#define HVAC_TOSHIBA_RPT_SPACE   7048 // Above original iremote limit

       
 

  • HVAC_TOSHIBA_HDR_MARK is the time duration of an header mark (in microseconds): it is a HIGH signal sent at the beginning of the signal to identify the start point
  • HVAC_TOSHIBA_HDR_SPACE is the time duration of an header space (in microseconds): it is a LOW signal sent after the mark and precedes the bit stream
  • HVAC_TOSHIBA_BIT_MARK is the time duration of a mark for a bit (either 0 or 1,in microseconds): it is a HIGH signal sent at the beginning of the bit to indicate a new bit is incoming
  • HVAC_TOSHIBA_ONE_SPACE is the time duration of a space for a bit with value 1 (in microseconds): it is a LOW signal sent after the bit mark 
  • HVAC_TOSHIBA_ZERO_SPACE is the time duration of a space for a bit with value 0 (in microseconds): it is a LOW signal sent after the bit mark 
  • HVAC_TOSHIBA_RPT MARK is the time duration of an header mark (in microseconds): it is a HIGH signal sent at the end of the first half of the signal to identify the start point of the repetition
  • HVAC_TOSHIBA_RPT SPACE is the time duration of an header mark (in microseconds): it is a LOW signal sent after the repetition mark and marks the beginning of the second half of the bit stream
And here is the actual code that implements the protocol above:




       

/****************************************************************************
/* Send IR command to Toshiba HVAC - sendHvacToshiba
/***************************************************************************/
void sendHvacToshiba(
  HvacMode                HVAC_Mode,           // Example HVAC_HOT  
  int                     HVAC_Temp,           // Example 21  (°c)
  HvacFanMode             HVAC_FanMode,        // Example FAN_SPEED_AUTO  
  int                     OFF                  // Example false
)
{
 
#define HVAC_TOSHIBA_DATALEN 9
#define  HVAC_TOSHIBA_DEBUG;  // Un comment to access DEBUG information through Serial Interface

  byte mask = 1; //our bitmask
  //F20D03FC0150000051
  byte data[HVAC_TOSHIBA_DATALEN] = { 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 };
  // data array is a valid trame, only byte to be chnaged will be updated.

  byte i;

#ifdef HVAC_TOSHIBA_DEBUG
  Serial.println("Packet to send: ");
  for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
    Serial.print("_");
    Serial.print(data[i], HEX);
  }
  Serial.println(".");
#endif

  data[6] = 0x00;
  // Byte 7 - Mode
  switch (HVAC_Mode)
  {
    case HVAC_HOT:   data[6] = (byte) B00000011; break;
    case HVAC_COLD:  data[6] = (byte) B00000001; break;
    case HVAC_DRY:   data[6] = (byte) B00000010; break;
    case HVAC_AUTO:  data[6] = (byte) B00000000; break;
    default: break;
  }


  // Byte 7 - On / Off
  if (OFF) {
    data[6] = (byte) 0x07; // Turn OFF HVAC
  } else {
     // Turn ON HVAC (default)
  }

  // Byte 6 - Temperature
  // Check Min Max For Hot Mode
  byte Temp;
  if (HVAC_Temp > 30) { Temp = 30;}
  else if (HVAC_Temp < 17) { Temp = 17; } 
  else { Temp = HVAC_Temp; };
  data[5] = (byte) Temp - 17<<4;

  // Byte 10 - FAN / VANNE
  switch (HVAC_FanMode)
  {
    case FAN_SPEED_1:       data[6] = data[6] | (byte) B01000000; break;
    case FAN_SPEED_2:       data[6] = data[6] | (byte) B01100000; break;
    case FAN_SPEED_3:       data[6] = data[6] | (byte) B10000000; break;
    case FAN_SPEED_4:       data[6] = data[6] | (byte) B10100000; break;
    case FAN_SPEED_5:       data[6] = data[6] | (byte) B11000000; break; 
    case FAN_SPEED_AUTO:    data[6] = data[6] | (byte) B00000000; break;
    case FAN_SPEED_SILENT:  data[6] = data[6] | (byte) B00000000; break;//No FAN speed SILENT for TOSHIBA so it is consider as Speed AUTO
    default: break;
  }

  // Byte 9 - CRC
  data[8] = 0;
  for (i = 0; i < HVAC_TOSHIBA_DATALEN - 1; i++) {
    data[HVAC_TOSHIBA_DATALEN-1] = (byte) data[i] ^ data[HVAC_TOSHIBA_DATALEN -1];  // CRC is a simple bits addition
  }

#ifdef HVAC_TOSHIBA_DEBUG
  Serial.println("Packet to send: ");
  for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
    Serial.print("_"); Serial.print(data[i], HEX);
  }
  Serial.println(".");
  for (i = 0; i < HVAC_TOSHIBA_DATALEN ; i++) {
    Serial.print(data[i], BIN); Serial.print(" ");
  }
  Serial.println(".");
#endif

  enableIROut(38);  // 38khz
  space(0);
  for (int j = 0; j < 2; j++) {  // For Toshiba IR protocol we have to send two time the packet data
    // Header for the Packet
    mark(HVAC_TOSHIBA_HDR_MARK);
    space(HVAC_TOSHIBA_HDR_SPACE);
    for (i = 0; i < HVAC_TOSHIBA_DATALEN; i++) {
      // Send all Bits from Byte Data in Forward Order (MSB)
      for (mask = 10000000; mask > 0; mask >>= 1) { //iterate through bit mask
        if (data[i] & mask) { // Bit ONE
          mark(HVAC_TOSHIBA_BIT_MARK);
          space(HVAC_TOSHIBA_ONE_SPACE);
        }
        else { // Bit ZERO
          mark(HVAC_TOSHIBA_BIT_MARK);
          space(HVAC_MISTUBISHI_ZERO_SPACE);
        }
        //Next bits
      }
    }
    // End of Packet and retransmission of the Packet
    mark(HVAC_TOSHIBA_RPT_MARK);
    space(HVAC_TOSHIBA_RPT_SPACE);
    space(0); // Just to be sure
  }
}


To debug my code without having a real AC unit go crazy because of fake/wrong signals I used AnalysIr to check that the generated signals are the same with respect to the ones recorder from the real AC remote

The full version of the code (and a test ESP8266 sketch) is available on Github

Commenti

  1. HVAC contractors will be always in demand because individuals and businesses constantly need their HVAC systems either serviced, repaired, or replaced. air conditioning

    RispondiElimina

Posta un commento

Post popolari in questo blog

ESP8266 - Air Conditioner Command and Control Unit

Arduino pulse counting with multiple Energy Meters and logging to Emoncms