-
Notifications
You must be signed in to change notification settings - Fork 7
Steps of the reverse engineering
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.
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
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.
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| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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.
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 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
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 |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+