Reverse engineering IR Airconditioning codes
This idea started one afternoon talking to my colleague Takami. We were talking about what could be done with a Raspberry Pi and he commented he would like to be able to control his air conditioning before arriving home since winters in Tokyo are cold and summers are pretty warm. This idea caught my attention and I began to investigate how it could be done and how could I control my AC from my Android phone. The code can be found here.
Infrared in air conditioning
In order to carry out the project, we must have a general idea of how the air conditioner communicates with the remote control. Most air conditioning units use the NEC protocol. The basic concept of the protocol is as follows:
Each burst has a length of 562.5μs, at a carrier frequency of 38kHz (26.3μs). The logical bits are transmitted as follows:
- The logical ‘0’: consists of a pulse of 562.5μs followed by a gap of 562.5μs, with a total transmission time of 1.125 ms
- The logical ‘1’: consists of a pulse of 562.5μs followed by a gap of 1.6875ms, with a total transmission time of 2.25ms
When a key is pressed on the remote control, the transmitted message consists of the following, in order:
- A master pulse of 9 ms
- A gap of 4.5 ms
- The 8-bit address for the receiving device
- Reverse 8-bit address logic
- The 8-bit command
- The 8-bit logical inverse of the command
- A final pulse of 562.5 μs to indicate the end of the transmission of the message.
Now that we know how the NEC protocol works, we are going to build the circuit to send and receive infrared. We will need the following components to build it:
- 1 infrared LED.
- 1 PN2222A or PN2222 transistor
- Resistance of 220 ohms
- Infrared Receiver VS1838B
The GPIO pin does not provide enough current to the LED to be directly connected, so we need to amplify the signal to reach a higher range. My knowledge in electronics is very limited and after several tests with different resistances I got a radius of about 7 meters, this is the circuit that I am using to send infrared:
The connection of the infrared receiver VS1838B is very simple, simply connect the source to 3.3 volts, ground and the data pin to our GPIO. This is the final aspect of the circuit with the receiver and the infrared emitter:
Reverse engineering the IR codes
The first thing we are going to do is to test the LED circuit, in order to do that, let’s execute ledtest.py which turns on and off the LED every second, as it is an infrared LED the best way to check if it is actually working is pointing at the LED with our phone camera.
Once we have verified that everything is working correctly, let’s start decoding the codes of our remote control. In order to do that, let’s execute:
python ir_tools.py readRawCommands
Each time a key is pressed in our remote control we will see a JSON containing the intervals in which the receiver was on and off, it should be noted that most infrared receivers return 0 when an infrared pulse is received and 1 otherwise. Let’s analyze how my remote control sends the commands:
[0, 3188], [1, 1568], [0, 420], [1, 375], [0, 418], [1, 1171] ...
To begin with, we have received a pulse of approximately 3ms (remember that the infrared receivers return 0 when they receive the pulse), followed by a gap of approximately 1.5ms, if we observe the NEC protocol we realize that it is very similar, only the times vary.
If we continue analyzing we’ll see that we have received a pulse of 420μs followed by a gap of 375μs, reviewing the NEC protocol we see that the logical 0 is composed by a pulse of 562.5 μs followed by a gap of 562,5μs, therefore, we can guess that this is a logical 0. Next, we see that we have a pulse of 418μs followed by a gap of 1171μs that would correspond to the logical 1.
Analyzing this manually is quite tedious, to facilitate the process we can execute:
python ir_tools.py readBinaryCodes
Now every time we press the key on our remote, we will see the binary code received instead of the pulses.
Looks like we have everything ready to send infrared codes! We just have to replicate the pulses we have seen: activate the LED 3188μs, deactivate 1568μs etc for the air conditioning to recognize it as a command. But unfortunately, we have to solve another small problem first. Raspberry runs a system based on Linux which is not real-time capable, in addition to that Python is an interpreted programming language, that is to say, the operating system will introduce delays in our signals to provide CPU power to other processes.
Researching the internet I found this interesting library IR Slinger which is based on this other one Pigpio. The important point is that this library provides us with callbacks every time the state of the PGIO changes in the order of milliseconds, which is exactly what we need. I have modified the code a bit to be able to send binary codes from the terminal:
We have to adjust the times of leadingPulseDuration, leadingGapDuration, onePulse, zeroPulse, oneGap and zeroGap to match those of our remote controller. After every change we make we have to recompile it using the following flags:
Gcc -o beaver_remote remote.c -lm -lpigpio -pthread -lrt
Let’s export a reference command to compare it with the commands we sent from our Raspberry Pi and thus adjust the times to match our remote control. Let’s take a command as a reference, for example, 24 degrees heat with the fan to the maximum. If we run:
python ir_tools.py exportRawCommand --commandName 24_heat_fan_max
Aiming with our remote control to the infrared receiver let’s send 24 degrees heat with the fan to the maximum, if everything works correctly a
24_heat_fan_max file should have been created containing a JSON with the pulses and gaps received.
Next, let’s see what’s the binary code for 24 degrees heat with the fan to the maximum. Again aiming with our remote control to the infrared receiver, but this time we are going to execute:
python ir_tools.py readBinaryCodes
My remote with 24 degree heat fan at maximum generates:
1 01001010 01110101 11000011 01100100 10011011 10011110 01100001 11111110 00000001 11000001 00111110
Now we need to open two terminals, in one we will leave the command comparator running and from the other one we will send commands generated from our Raspberry.
Open a terminal and leave the command comparator running with the file we previously exported (24_heat_fan_max):
python ir_tools.py rawCodeComparator 24_heat_fan_max
Open another terminal and send the binary code:
sudo ./beaver_remote "0100101001110101110000110110010010011011100111100110000111111001000001101100000100111110"
In the command comparator, we will see the received pulses. In blue will appear the pulses that are shorter than they should be, in red we will see the pulses that are longer than they should and in green color the pulses of the adequate duration.
First, I tried applying the NEC protocol’s pulses (
leadingPulseDuration = 9000,
leadingGapDuration = 4500,
onePulse = 562,
zeroPulse = 562,
oneGap = 1688,
zeroGap = 562), after executing the command comparator I got:
As we can see the NEC protocol’s pulses are longer than what my AC unit expects, after several attempts I found that the best combination for my AC unit is (
leadingPulseDuration = 3000,
leadingGapDuration = 1500,
onePulse = 350,
zeroPulse = 350,
oneGap = 1150,
zeroGap = 350):
This is how the command comparator looks after several attempts:
As we can see most of the values are green, this was enough to synchronize my Raspberry Pi with my AC unit. Now that we have synchronized our raspberry pi with our air conditioner, we have to decode the binary codes that the remote sends for different functions.
Analyzing all possible codes would be a very long process. In my air conditioning, I have 3 fan modes: strong, medium and slow. Three different modes: heat, dehumidifier and cold and I have 13 temperatures to choose from 18º to 30º, this makes a total of 13 x 3 x 3 = 117 codes to analyze!
Luckily we can analyze each thing separately so we just have to analyze 13 + 3 + 3 = 19 codes and then combine them to generate the 117. I created a small tool to make things a bit easier. Let’s run the binary code analyzer:
python ir_tools.py readBinaryCodes
This tool reads commands from the remote control and compares them with the previous command received so you can easily check which bits changed. Let’s see how the remote codifies the temperature, first let’s fix the other variables(fan speed and mode) to medium and heat, and then start changing just the temperature:
This is enough to realize that the temperature is codified in the 4 least significant bytes of the last two words, if you look at the last word we see that:
0011 1000 → 18
0011 0100 → 19
0011 1100 → 20
When looking carefully we’ll realize it is the number 1,2, and 3 in binary in reverse:
0001 → 1 → 18
0010→ 2 → 19
0011→ 3 → 20
0100 → 4 → 21
Every AC unit is different and the way the temperature is codified it’s going to be different in each one so you have to find the pattern in yours!
Once we have all the codes, the next step will be to set up a HTTP server in our Raspberry Pi and create an API to be able to make requests to interact with the AC remotely from the client, Android in my case. But I’ll leave that for the next post!