Skip to content

Steps of the reverse engineering

Thomas A edited this page Aug 19, 2016 · 4 revisions

Intro

Thanks for reading this.

This wiki page will describe most of the steps taken for reverse engineering the infrared codes of my DeLonghi PAC N81 airconditioning unit.

While the unit was easily controllable via infrared, it lacked the option to turn it on from the internet.

Basic Observations about the IR Protocol

Remote

First it is noticeable that the remote has its own display - this means that either the unit sends back its settings once changed or that the remote has its own settings.

Changing the values on the remote and on the unit revealed that all settings entered on the remote get sent to the unit but not the other way. So even if you cover the transmitter for one button press, the change will be sent in the next packet too. E.g. you set the temperature to 24°C, cover the sensor, set to 23°, uncover it and set to 22°C - after that, the unit will be set to 22°C.

Observation / Assumption: Each IR-Packet sends the whole configuration

Decoding the IR Packet

Hardware

First we need to check if the remote speaks a known IR protocol (Sony, Panasonic, ...)

To do this, we whip out an Arduino, connect a IR receiver and load the IRRecvDump from https://github.com/z3t0/Arduino-IRremote

When pointing the remote to the sensor and pressing any button, we get the following infomation from the serial terminal.

Decoded NEC:
12181140 (32 bits)

Observation: The remote sends in NEC format, 32 bits of data

This is great news since the NEC protocol is widely supported and we only have to find out the structure of the 32bit value instead of also analyzing the protocol used.

Getting the most important Bit: On/Off

This step will be the easiest part. Simply pointing the remote to the arduino and switching on/off multiple times, the packets yield the following data.

12181120
12181020
12181120
12181020

Comparing the 2 codes, it becomes obvious that on/off toggles one bit.

I use the notation as in RFCs where bits in network protocols are shown to give a picture of the currently known fields.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    |ON|                       |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

The 2nd important value: temperature

Getting a single on/off bit was easy, but getting a value may be a bigger challenge.

So lets again send some messages to the arduino to see what happens when the temperature is changed.

12181000 //set to 20°C
12181020 //set to 20°C
121810A0 //set to 21°C
12181060 //set to 22°C
121810E0 //set to 23°C
12181004 //set to 32°C

Observation: Changing the temperature changes the last byte

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                                               |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

This is good, but the values are not clearly logical. 20°C->21°C->22°C is 0x20->0xA0->0x60. The only good point is that 16°C is mapped to 0x00. So lets start there and take a look at the bits

16°C 0000 0000
17°C 1000 0000
18°C 0100 0000
19°C 1100 0000
20°C 0010 0000

Hey! There is a pattern. The bits are simply flipped. Extending the IRRecvDump sketch to display the last byte flipped yields a simple picture.

16°C to 32°C is assigned to 0-16 while 61°F to 89°F is assigned to 61-89.

Next Step: Fan speed

The A/C unit features 3 fanspeeds: High, Medium and Low.

Switching through the modes yields the following codes sent:

12181000 //High
12281000 //Medium
12481000 //Low

In that case, only 3 bits changed. Each bit represents one mode (0x1, 0x2, 0x4) while the others dont need to be set. So this was not a big riddle to solve.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                          |Lo|Md|Hi|           |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Mode

The next setting open for investigation is the operational mode. My unit supports A/C, dehumidify and fan only.

I suspect that this will be handled the same way as the fan. But lets take a look.

12181000 //AC
12121000 //Dehumidify
12111000 //Fan

So its nearly the same, but the bit 0x4 is not used. Only 0x8, 0x2 and 0x1 get sent by the remote

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                          |Lo|Md|Hi|Ac|  |Dh|Bl|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                    |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Timer: When you only want to start it remote for later

The next portion would be the timer. The unit supports setting a timer to either turn the unit off after a specified number of hours or turn it on.

So lets see what enabling the timer does in the code.

12181300 //Timer on, Unit on
12181100 //Timer off, Unit on
12181200 //Timer on, Unit off
12181000 //Timer off, Unit off

Now we see, the bit for the timer is in the same byte as the on/off bit. And both on/off and timer get sent in each message.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                          |Lo|Md|Hi|Ac|  |Dh|Bl|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                 |T |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

The hunt for the timer value still hasn't ended...

12188200 //Timer on, 1 hour
12184200 //Timer on, 2 hours
1218C200 //Timer on, 3 hours

So, only one nibble changes, and as before with the temperature the bits are flipped to 0b1000 is 1 / 0b0100 is 2 Also, no matter if the timer is enabled or not, the value of the timer is always sent and set to 1 by default.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                          |Lo|Md|Hi|Ac|  |Dh|Bl|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| Timer     |     |T |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Last but not least, °C or °F

The last thing to change on the remote is the switch between °C and °F.

To make it short, here is the bit.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                          |Lo|Md|Hi|Ac|  |Dh|Bl|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| Timer     |  |CF|T |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

End

So, all buttons on the remote have been pressed. This leaves only one byte unknown: 0x12 on the start of the code.

I suspect that this is simply a fixed prefix sent in every message. And so far-it is working There are a few bits in the middle of the modes, so I suspect that there are bigger A/C units that may use those remaining 0 bits to have more functionality like a low-noise night mode.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|   0x01    |   0x02    |0 |Lo|Md|Hi|Ac|0 |Dh|Bl|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| Timer     |0 |CF|T |ON|    Temperature        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+