Reverse engineering Air Conditioner IR Remote control protocol
soruce : http://www.instructables.com/id/Reverse-engineering-of-an-Air-Conditioning-control/?ALLSTEPS
Hi, this is my first instructable, hope you like it.
To get into electronics I chose a home automation project: a system allowing me to control and program both air conditioner units in my flat. In this instructable I show how I got to understand the IR protocol.
To get into electronics I chose a home automation project: a system allowing me to control and program both air conditioner units in my flat. In this instructable I show how I got to understand the IR protocol.
Material:
- Panasonic inverter air conditioner remote control
- Raspberry Pi with raspbian and lirc installed
- 38kHz Infrared (IR) Receiver Module (for instance RadioShack Catalog #: 276-640)
- breadboard
The raspberry Pi is useful in my setup to analyse the incoming IR signals, but also to host other components on the global project. Any other computer equipped with an IR receiver could do. An Arduino could also output the needed information using the IRremote library (https://github.com/shirriff/Arduino-IRremote). This library will be used to control the air conditioners in the end.
The method used here could work with other remote protocols (with adaptations).
- Raspberry Pi with raspbian and lirc installed
- 38kHz Infrared (IR) Receiver Module (for instance RadioShack Catalog #: 276-640)
- breadboard
The raspberry Pi is useful in my setup to analyse the incoming IR signals, but also to host other components on the global project. Any other computer equipped with an IR receiver could do. An Arduino could also output the needed information using the IRremote library (https://github.com/shirriff/Arduino-IRremote). This library will be used to control the air conditioners in the end.
The method used here could work with other remote protocols (with adaptations).
Please excuse the screen caps that don't show exactly the same output format: this part of the project was done during last summer and I used files generated by multiple versions of the codes I wrote.
decode.c1 KB
Remove these ads by Signing Up
Step 1: The remote to study
The remote shows multiple options to transmit:
- The target temperature to achieve
- The "mode" ("cool","heat","dry", "auto")
- The air flow swing (5 possible positions, and one automatic mode)
- The fan speed
- A powerful or quiet option (which impacts the fan speed (and more?))
- 2 timers: one to turn off the unit after a certain amount of time, one to bring it back on
One thing to know is that usual remotes controls, for TV, HI-FI, ... send a signal for each key pressed (often in loops while the key is pressed). An air conditioning remote often displays information about the parameters selected. But of course parameters can be changed on the remote while the unit is out of reach, which could lead to synchronization problems between the display and the unit in some cases if it worked like the TV remotes. This implies that when such a remote sends a signal, it sends the whole parameters set. What we'll do is then study what signals are sent to try and understand which part of the signal is for which parameter.
- The target temperature to achieve
- The "mode" ("cool","heat","dry", "auto")
- The air flow swing (5 possible positions, and one automatic mode)
- The fan speed
- A powerful or quiet option (which impacts the fan speed (and more?))
- 2 timers: one to turn off the unit after a certain amount of time, one to bring it back on
One thing to know is that usual remotes controls, for TV, HI-FI, ... send a signal for each key pressed (often in loops while the key is pressed). An air conditioning remote often displays information about the parameters selected. But of course parameters can be changed on the remote while the unit is out of reach, which could lead to synchronization problems between the display and the unit in some cases if it worked like the TV remotes. This implies that when such a remote sends a signal, it sends the whole parameters set. What we'll do is then study what signals are sent to try and understand which part of the signal is for which parameter.
Step 2: Gathering Data
Off we go.
Note1: command recording is a repetitive and slightly boring process, but necessary.
Note2: I don't own an oscilloscope so the only way for my to plot the recorded values is to use a plotting program (gnuplot) on raw data. This can useful to have a visual idea of what happens, but it requires some adaptation in raw data and is in my opinion not necessary at all. For that reason I have not included any graph.
I used the instructions from this page: http://alexba.in/blog/2013/01/06/setting-up-lirc-... to plug the receiver on the Pi and prepare lirc.
I then recorded commands with the -raw option and redirected output to a file. The goal is to have a record of each value for ON and OFF of course, but also for each mode (AUTO, COOL, HEAT, DRY), for each swing and fan value, and, say, for min and max temperature (16 to 30°C in my case). The important part here is to make a reference record and to make a recording for each change of option, making only one option change every single time. Once a record is done, press CTRL+C to terminate the command and do it again with the next command/file
Note1: command recording is a repetitive and slightly boring process, but necessary.
Note2: I don't own an oscilloscope so the only way for my to plot the recorded values is to use a plotting program (gnuplot) on raw data. This can useful to have a visual idea of what happens, but it requires some adaptation in raw data and is in my opinion not necessary at all. For that reason I have not included any graph.
I used the instructions from this page: http://alexba.in/blog/2013/01/06/setting-up-lirc-... to plug the receiver on the Pi and prepare lirc.
I then recorded commands with the -raw option and redirected output to a file. The goal is to have a record of each value for ON and OFF of course, but also for each mode (AUTO, COOL, HEAT, DRY), for each swing and fan value, and, say, for min and max temperature (16 to 30°C in my case). The important part here is to make a reference record and to make a recording for each change of option, making only one option change every single time. Once a record is done, press CTRL+C to terminate the command and do it again with the next command/file
$ mode2 -d /dev/lirc0 -m > auto_21C_swing0_fan0
It could be necessary to gain super privileges to run this command ("sudo mode2 ...") and the lirc daemon can block the file, so it may be necessary to kill it first:
$ sudo /etc/init.d/lirc stop $ sudo mode2 -d /dev/lirc0 -m > filename $ [...] $ sudo /etc/init.d/lirc start
When reading the generated files, what we see is all numbers, organized in 6 columns. These numbers indicate durations in microseconds. The columns work in pair, so
740 1495 920 1345 735 1335
means : "the IR LED was ON for 740us, then OFF for 1495us, then ON for 920us, then OFF for 1345us, etc."
Exemple :
Exemple :
3523 1766 414 451 418 1312 423 448 419 452 418 452 428 443 417 453 418 457 418 452 418 452 416 454 419 452 417 453 417 1316 418 452 418 466 410 451 419 453 414 457 416 452 418 453 417 1316 418 1315 418 1320 426 443 419 451 419 1314 419 452 418 452 418 452 418 452 418 457 419 450 420 451 419 452 421 451 417 452 418 452 418 452 419 457 418 452 418 449 422 451 418 452 419 451 420 451 426 444 418 457 418 452 417 452 419 452 416 454 420 450 418 452 419 452 419 456 418 452 418 1323 411 1313 421 451 419 451 418 452 418 452 418 454 419 9997 3521 1764 417 452 419 1315 418 449 421 452 418 453 417 453 418 451 427 448 419 453 417 453 417 452 419 452 417 453 417 1317 417 451 419 456 419 451 420 458 411 452 418 452 416 455 418 1315 418 1315 418 1321 417 452 419 452 416 1318 418 452 418 451 419 451 419 452 418 457 418 452 418 452 420 448 422 451 419 457 412 452 417 452 419 457 418 1315 417 453 419 449 421 452 418 452 418 452 418 453 424 450 418 453 419 1314 418 452 418 452 418 1316 418 1315 417 453 418 465 410 452 419 452 416 453 418 452 418 452 418 452 418 453 417 1338 418 1316 415 1318 418 1315 419 1314 418 452 418 1315 419 449 421 1328 410 452 418 453 417 452 418 452 420 450 419 451 417 455 416 457 419 451 418 452 419 452 419 453 417 453 417 453 418 451 419 456 418 453 418 1315 418 1315 418 452 419 459 411 451 419 451 416 460 418 452 419 451 418 453 417 453 418 451 418 1315 419 1313 420 458 425 445 413 460 418 445 422 452 418 452 418 452 418 452 419 455 421 450 420 463 407 452 417 452 418 453 418 452 418 452 418 457 418 450 421 452 418 452 417 453 419 451 424 445 419 451 419 1321 417 453 417 454 416 452 418 452 419 452 418 452 418 452 418 468 408 452 417 1313 422 1314 418 452 420 451 418 452 418 452 419 456 418 453 416 454 418 1315 417 452 418 1315 418 452 418 1316 418 451 422
Ok, that seems insane :)
Note that the file will begin with a line with a single value : it is the time elapsed between the recording start and the arrival of the first IR signal. This line should be ignored.
Of course, as these are measures, with a time scale as small as the microsecond, all timings are different, which makes the detection of small differences between 2 commands impossible.
It can be observed that values are always close to 400 or 1300us, except for 3 (closer to 4400, 9900 and 1700). So what we'll do to make figures comparable is "round" the numbers to the closest of these 2 "reference" values (a spreadsheet is handy at first).
What this manipulation shows is that except for the 3 singular values the ON time is always 400us, what changes is only the OFF time.
Let's make the hypothesis that the OFF time is coding 0 and 1, and let's assume that 400us is for 0 and 1300us for 1. With this assumption it is possible to change each pair of columns to a single bit.
Let's also make an observation : the part between the 3 "singular timings" is always the same, in all the recordings. It can be assumed that :
- the part is an introduction, maybe identifying the remote or the Air Conditioner, and it will never change
- the different timings are Locks and separators between the introduction and the actual payload
It is therefore acceptable to take that part of the message as an invariant and not to study it.
For the ease of exploiting the data I wrote a small c program to round the values and transform it to binary digits. For ease of reading the code skips the intro part. Should you wish to use this code the timing values are defined in the beginning of the program, you will probably need to adapt it.
After compilation (gcc -o decode decode.c) you can use it on every data file :
Note that the file will begin with a line with a single value : it is the time elapsed between the recording start and the arrival of the first IR signal. This line should be ignored.
Of course, as these are measures, with a time scale as small as the microsecond, all timings are different, which makes the detection of small differences between 2 commands impossible.
It can be observed that values are always close to 400 or 1300us, except for 3 (closer to 4400, 9900 and 1700). So what we'll do to make figures comparable is "round" the numbers to the closest of these 2 "reference" values (a spreadsheet is handy at first).
What this manipulation shows is that except for the 3 singular values the ON time is always 400us, what changes is only the OFF time.
Let's make the hypothesis that the OFF time is coding 0 and 1, and let's assume that 400us is for 0 and 1300us for 1. With this assumption it is possible to change each pair of columns to a single bit.
Let's also make an observation : the part between the 3 "singular timings" is always the same, in all the recordings. It can be assumed that :
- the part is an introduction, maybe identifying the remote or the Air Conditioner, and it will never change
- the different timings are Locks and separators between the introduction and the actual payload
It is therefore acceptable to take that part of the message as an invariant and not to study it.
For the ease of exploiting the data I wrote a small c program to round the values and transform it to binary digits. For ease of reading the code skips the intro part. Should you wish to use this code the timing values are defined in the beginning of the program, you will probably need to adapt it.
After compilation (gcc -o decode decode.c) you can use it on every data file :
$ ./decode auto_21C_swing0_Fan0 auto_21C_swing0_Fan0.decoded
Example with mode auto target temperature 25°C:
$ more auto_25.decode 01000000 00000100 00000111 00100000 00000000 10000000 01001100 00000001 11110101 00000000 00000000 01100000 00000110 00000000 00000000 00000001 00000000 01100000 00101010
decode.c1 KB
Step 3: Exploiting the data
OK, now what we have is a collection of data files containing bits (actually characters representing binary digits, which is technically different).
The use of a file comparison program will help in identifying which part changes for each parameter.
What we can see is not only one part changes but the ending part of the message also. Generally when communication is involved the data correctness is never guarantied so a checksum is usually used to ensure that no data has been corrupted. The checksum will be studied later.
The commandsThe use of a file comparison program will help in identifying which part changes for each parameter.
What we can see is not only one part changes but the ending part of the message also. Generally when communication is involved the data correctness is never guarantied so a checksum is usually used to ensure that no data has been corrupted. The checksum will be studied later.
ON/OFF:
Only one bit changes, the first of the 6th byte (or 40th bit). If 1 the command is ON, if 0 the command is OFF
AUTO/HEAT/COOL/DRY
3 bits change : bits 45 to 48 (see picture)
Only one bit changes, the first of the 6th byte (or 40th bit). If 1 the command is ON, if 0 the command is OFF
AUTO/HEAT/COOL/DRY
3 bits change : bits 45 to 48 (see picture)
0000 = AUTO 1100 = COOL 0100 = DRY 0010 = HEAT
swing
bits 64 to 67:
1111 = swing auto 1000 = swing lowest 0100 = swing low 1100 = swing high 0010 = swing higest
Fan speed
bit 68 to 72:
1100 = lowest 0010 = low 1010 = medium 0110 = high 1110 = highest 0101 = auto
Options (Byte 13):
Powerful: 10000000 Quiet: 00000100 (and the Fan speed is set to minimum) None: 00000000
Temperature:
Here it becomes a little trickier because it would be nice to understand how the value is coded and not scan all possibilities.
We have sampled 16°C, 30°C, and 21°C. In binary :
16 = 10000 22 = 10110 30 = 11110
The correct zone for the temperature is not found using these values. But often hardware use bits in reverse order (Most Significant Bit or Least Significant Bit on the left of the number, also referred as Little Endian or Big Endian). If reversing the bits the values obtained is:
16 = 00001 22 = 01101 30 = 01111
This gives a match for the 3 values at bits 49 to 53. This is rather surprising as the value is not aligned with a byte (closest would be 6x8=48th bit). This means that when building a message the code will have to shift of one bit the temperature value.
Which consequently validates the first hypothesis concerning timing values used for 0 and 1 :)
Timer A:
Timer A:
The timers are coded in multiple positions: first avalue is used to indicate that the A timer is on, then a second one is used to indicate the timer value.
Timer active state can be found at the same byte as ON_OFF (6th byte, second bit, so starting from the beginning of the payload 41st bit).
Timer active state can be found at the same byte as ON_OFF (6th byte, second bit, so starting from the beginning of the payload 41st bit).
So the Timer A is active if the byte is the 6th byte looks like :
x010xxx, the first bit being the ON/OFF bit.
For the timer duration, using the same encoding that with temperature and codingminutes and not hours (as the remote displays), the value can be found on the second half of the 12th byte.
Timer B:
Timer B is active if the 2nd bit of the ON/OFF byte is 1. As timer A is always ON when using the timer B, the byte looks like :
x110xxx, the first bit being the ON/OFF bit.
Ok, here I must admit that I failed to find the key to this value, maybe it is a delta between timer A and B, maybe another unit... Truth is: who cares about timers when the goal is to control it (automatically) from a computer ? :) Even timer A is not used in the end in my system, I was just happy to have found it.
Checksum:
Ah. Here is a value that cannot be disregarded. It is crucial to understand it.
After a few tests it appears that the checksum is for the payload part only (not including the introduction), and is the result of the simple sum of all bytes, keeping only one byte of the result (ignoring overflows). The trick is, of course, that this checksum is made by a Big Endian system, so every byte has to be reversed before being summed, then the result reversed again.
Checksum:
Ah. Here is a value that cannot be disregarded. It is crucial to understand it.
After a few tests it appears that the checksum is for the payload part only (not including the introduction), and is the result of the simple sum of all bytes, keeping only one byte of the result (ignoring overflows). The trick is, of course, that this checksum is made by a Big Endian system, so every byte has to be reversed before being summed, then the result reversed again.
Step 4: Verifying the analysis
The goal of the study is to be able to generate messages that the air conditioner will understand and accept. So the program joined will take arguments and generate a binary message. Now all that's left to do (until we have a working IR emitter) is to compare the message generated by the program and the recorded messages we had in inputs.
The values and position of each data in the payload is defined here.
Once compiled (gcc -o encode encode.c) this code should allow us to verify that the generated frame is equal to the one sampled
The encode program separates the bytes for easier reading.
$ more auto_25.decode 01000000 00000100 00000111 00100000 00000000 10000000 01001100 00000001 11110101 00000000 00000000 01100000 00000110 00000000 00000000 00000001 00000000 01100000 00101010 $ ./encode -m AUTO 25 01000000 00000100 00000111 00100000 00000000 10000000 01001100 00000001 11110101 00000000 00000000 01100000 00000110 00000000 00000000 00000001 00000000 01100000 00101010
That's all for this instructable, not much pictures or fun in it, I must admit, but there's more to come. Hope it might help someone.
Don't hesitate to ask for precision and comment.
encode.c6 KB
댓글