I have central air-conditioning in my apartment, and it’s controlled by a remote, employing IR signals to send commands to the A/C control unit.
As any decent geek would, I’d like to be able to control my A/C using other means (e.g., a smartphone).
Towards that goal, I figured I should first reverse engineer the IR commands the remote sends to the A/C, so I could later send these commands using other methods.
The gist: using an Arduino Uno board with a Phototransistor circuit (see below), I was able to obtain the IR waveform using Ken Shirriff’s Arduino IRremote library (slightly modified), and even graph these waveforms with Python’s matplotlib, as shown below.
Keep reading for a full drill down, or jump straight into my home-control-arduino GitHub project for the actual code and documentation.
Building the IR Receiver Circuit
Buy list:
- SparkFun Inventor’s Kit for Arduino (available in Israel via 4Project.co.il, or via friends who sell their own kits (h/t YoniC) π ) (also see a similar Arduino starter kit on Amazon or on eBay), that comes with Arduino UNO R3 (Amazon, eBay), breadboard and a convenient holder for both (and lots of other useful stuff to get you going).
-
A simple Phototransistor (like this one, also from 4Project.co.il).
Assemble the components according to the schematic shown above, which will look something like this:
Connect the Arduino to the PC via USB, and compile and upload my IRACreceiver program to the board:
Capturing A/C Commands
Now, with the circuit built and the program loaded, open the Arduino Serial Monitor, aim the A/C remote at the phototransistor, and press buttons on the remote to make it transmit IR signals to the receiver:
and watch as the Arduino prints to the serial monitor the raw IR signals info:
Analyzing the A/C Commands
In order to produce nice graphs of these signals, like the one above, I created a data-set of A/C-command samples to process using my IRAnalysis Python script.
But before that, a couple of words are in place concerning the specifics of this A/C remote and how it functions.
Basically, the communication channel between the remote and the A/C control unit is one-way – from the remote to the control unit. This means, among other things, that the remote has no knowledge of the current state of the A/C (whether it’s on or off, what state it is set to at the moment with respect to operation mode, fan speed, temperature, etc.). Because of this, every press of a button on the remote will send the entire state as it is currently presented on the display of the remote, including information about whether the power button was pressed or not.
The implications of this include:
- By solely sending IR signals in lieu of the remote, it is not possible to explicitly control the On/Off state of the A/C, but only toggle the power state. If you think about this for a second, you can see this is not an issue when operating the remote locally, because the “human-in-the-loop” (you) knows the power state of the A/C, so the remote doesn’t need to. It becomes a problem once we take out the human from the loop – but that’s for another post.
-
There are no “relative” commands (e.g. “increase temperature by 1 degree”) – only absolute ones, which means we need to map all possible state-combinations that we want to be able to send on behalf of the remote to their corresponding IR signals.
So, in order to build this mapping, I decided that I am only interested in a subset of state-combinations that include A/C mode (cool / heat / …), fan speed (low / high / auto) and the temperature. In addition, I need to include the power-button in the mapping (whether a command with a given mode-fan-temp combination should toggle the power state or leave it as it was).
After all this, lets get back to the IRAnalysis Python script. The script expects to get a data-set of A/C-command samples as a collection of plaintext files (in a single directory). Each file has the state-mapping encoded in the filename (Power-Mode-Fan-Temp
), and at least one IR capture as the content of the file (simply copy & paste the captures from the serial monitor into the body of the file).
For example, I have included in the GitHub project three sample files, that produce the graphs shown above using the script with the “graph” command.
Since the Phototransistor is susceptible to ambient noise, together with the quantization noise that arises from the way the Arduino-IRremote library samples the signal, the readings produced may contain outliers. For that reason, it is highly recommended that all sample files contain multiple independent readings (say at least 3), so my script could try and detect anomalous readings.
The script tries to do this by comparing each reading to the average signal of all readings, and flag any single reading that has an element differing by more than 100us from the average reading as anomalous.
Whenever the script reports such anomalies, you should manually remove that outlying reading from the sample file, and maybe replace it with another, better, reading.
Finally, it is a natural path to dig deeper in the waveforms of different A/C-command samples, and characterize (reverse engineer) them up to the level of understanding which part of the signal encodes which part of the state, and then use this characterization to recreate waveforms from a given state.
I started looking into such undertakings (this is actually the reason that I implemented the graphing functionality), but pretty quickly I reached an understanding that it is not going to be easy (for example, I saw that two commands that differ only by 1 degree in the temperature value, differ in multiple regions of their respective waveforms) – so I left that part as an exercise for the devoted reader π (or a future self?)
Conclusion
With that, this post reaches its conclusion.
Feel free to go on and read my other posts related to my A/C-control project!
May 26, 2014
thank you dude, your receiving sketch was all i needed to get the intervals and assembly program an atmega328p for my final semester project =)
May 26, 2014
My pleasure!
Glad to hear you found it helpful π
September 24, 2014
Hi Itamar
I wrote an Electra A/C IR sender the uses IRremote, it might be helpful.
https://github.com/barakwei/IRelectra
September 24, 2014
Cool. Good stuff π
September 22, 2015
Hi there! Some of the signals i receive are 194 int long and some are 188 int long. Is it supposed to be like that? Am i foing something wrong here?
Toda. Thank you
David
September 22, 2015
The signals are supposed to be repeatable, AFAIK, unless the same remote control button triggers different IR commands on every push… (Or if you’re trying with different remote buttons)
September 28, 2015
hey Itamar. it seems like my last post haven’t been sent,
i attached a screenshot of the IR output.
while pressing the same button (temp up) i get a different array long.
what seems to be the problem?
http://postimg.org/image/51ezxbuc5/
thank you!
David
September 28, 2015
At least on my remote, “temp up” is not the same command every time, because it always sends the explicit temperature as shown on the remote. But it’s also possible that your environment is noisy (IR-wise), making measurements inconsistent.
What kind of remote and AC do you have? Can you try to record and retransmit the most basic commands (something like power on, with all other settings always the same, in a dark room, with the remote very close to the circuit)?
September 28, 2015
i am using the Electra remote (mine written ELCO),
i know each time you press the ‘temp up’ button the command is different.
each time the remote sends a different array with the current settings (which are displayed on the remote)
but the array length should stay the same. (isn’t it?)
i will try to capture and retransmit the command, ill report the results
thank you
September 26, 2015
Hi there!
I got the raw buff code from my ac remote. But im not sure how you generate the arduino code from that to send the ir signal to the ac. I created a text file named βLeave-Cool-Auto-25β³ with my raw buffer data in it and put it in the same folder with the IRAnalysis Python script. Not sure if that is correct? However, the IRAnalysis Python script couldn’t compile as well. My first error compiling the IRAnalysis Python script was empty character constant on “print ””. Then when I took that off i got my second compiling error ‘prog_uint16_t’ does not name a type. I am nont sure where I did wrong. Can you be more specific on the IRAnalysis Python script ?
Many thanks,
Andy
September 26, 2015
Hi Andy!
Sorry about the confusion.
The raw-buff files should all be together in one directory, with no other files, wherever you want. The path to that directory should be passed to the
IRAnalysis
script via the--dumps-dir
flag.If you have any more trouble with it, feel free to share your raw-buff file and the command-line you try to execute.
September 26, 2015
Hi,
Sorry I am still confused. So am I supposed to copy the code from iranalysis.py and execute it in the arduino ide?
heres my raw code i got from iracrev
0x25D5AC5F (32 bits)
Raw (100): -7142 3400 -1700 450 -1250 400 -1300 450 -400 450 -450 400 -450 400 -1300 400 -450 450 -400 450 -1300 400 -1300 400 -450 400 -1300 400 -450 400 -500 400 -1300 400 -1300 400 -450 400 -1300 450 -1300 400 -450 400 -450 400 -1300 450 -400 450 -450 400 -1300 400 -450 400 -450 400 -450 450 -400 450 -450 400 -450 400 -450 450 -400 450 -400 450 -450 400 -450 400 -450 400 -450 450 -400 450 -450 400 -450 400 -450 400 -450 400 -450 400 -450 450 -1300 400 -450 400 -450 400
how do i generate a code from this that i can replace it in the uSendBuff_Toggle_Cool_Auto_25 function in the iracsender code??
Thank you!
Andy
September 26, 2015
The IRAnalysis.py script is pretty lazy actually. All it intended to do is to write the generated Arduino array(s) to stdout. You are left with copying it from the output of the script to the Arduino code, in the appropriate location.
September 28, 2015
Hi sorry I was confused about python before
now executing it, i got an error
PS C:\Users\YW\temp\stuff> python IRAnalysis.py
usage: IRAnalysis.py [-h] [-d DUMPS_DIR] {graph,code} …
IRAnalysis.py: error: too few arguments
September 28, 2015
What’s the exact command line you are trying to run?
September 28, 2015
I was running python IRAnalysis.py using powershell (not sure if this was right) and putting Leave-Cool-Auto-25 under directory C:\User\dumps
September 28, 2015
powershell is fine. the command line should look something like this
python IRAnalysis.py --dumps-dir C:\User\dumps code
(note that the flag comes before the name of the command – this is the way the argparse module works with the way the parsers are defined here).September 30, 2015
Sorry to keep bothering you,
here is what i got for entering that command
PS C:\Users\YW\temp\stuff> python IRAnalysis.py –dumps-dir C:\Users\YW\dumps code
Traceback (most recent call last):
File “IRAnalysis.py”, line 154, in
args.func(load_from_raw(args.dumps_dir))
File “IRAnalysis.py”, line 118, in load_from_raw
sample = AcSample(pwr, mode, fan, int(temp))
ValueError: invalid literal for int() with base 10: ’25.txt’
September 30, 2015
the files in the dumps directory should have no extension at all. this error indicates that the files have
.txt
extension.October 1, 2015
Thank you!! I got this
PS C:\Users\YW\temp\stuff> python IRAnalysis.py –dumps-dir C:\Users\YW\dumps code
PROGMEM prog_uint16_t uSendBuff_Leave_Cool_Auto_25[] = {99, 3400, 1700, 450, 1250, 400, 1300, 450, 400, 450, 450, 400, 450, 400, 1300, 400, 450, 450, 400, 450, 1300, 400, 1300, 400, 450, 400, 1300, 400, 450, 400, 500, 400, 1300, 400, 1300, 400, 450, 400, 1300, 450, 1300, 400, 450, 400, 450, 400, 1300, 450, 400, 450, 450, 400, 1300, 400, 450, 400, 450, 400, 450, 450, 400, 450, 450, 400, 450, 400, 450, 450, 400, 450, 400, 450, 450, 400, 450, 400, 450, 400, 450, 450, 400, 450, 450, 400, 450, 400, 450, 400, 450, 400, 450, 400, 450, 450, 1300, 400, 450, 400, 450, 400};
but when i tried put this into your irAcsender code, i got an error ‘prog_uint16_t’ does not name a type
November 1, 2015
Hi, sorry for the delay in response!
I haven’t compiled this code for the Arduino for a while, so maybe things have changed in the Arduino environment and the PROGRAM stuff are broken now.
I found this – https://github.com/arduino/Arduino/issues/2456
Can you try to follow the suggestions there and modify the generated code, and see if it works? If you get a successful result, I will appreciate the feedback to fix my code generation accordingly π
November 11, 2015
Hello Itamar
I have Halcyon AC with Fujitsu Generic ac remote. I am getting 32 bit raw code each time. Its length is 116 when I OFF the AC. The OFF signal is working fine and I can turn the AC off with arduio. I don’t exactly know the length of other button press. At raw=255 I received signals of length 255 so I thought of increasing rawbuf. When I set rawbuf above 255, I am unable to receive raw code for other buttons except the same OFF signal of length 116. The code I receive at rawbuf <= 255 doesn’t work. Please help.
November 13, 2015
Hi Manav,
Sorry, but I haven’t got a clue why you are not receiving codes with rawbuf > 255.
What I would do in your situation is to go deeper into the IRremote library, and try to figure out how it’s working. Since 255 is MAXBYTE, I would look for byte-variables that are used when reading signals, to see if it’s limited because of something related to this.
November 16, 2015
Hello Itamar,
I need some help with your Python IRAnalysis part. So far I have been able to get raw code from iracrev. I saved the code under the name Leave-Cool-Auto-25 under the directory C:\User\M.SartaJ Khan which also have your IRAnalysis python script. After that I tried running the command (like the one you adviced to andy)
PS C:\Users\M.SartaJ khan> python IRAnalysis.py βdumps-dir C:\Users\M.SartaJ khan
I keep getting the following error:
usage: IRAnalysis.py [-h] [-d DUMPS_DIR] {graph,code} …
IRAnalysis.py: error: invalid choice: ‘\x96dumps-dir’ (choose from ‘graph’, ‘code’)
Can you please guide me in the python part as I am totally new with this software. Thankyou
November 17, 2015
Hi!
Are you trying to generate Arduino code, or graph the the raw IR data?
In any case, you should put the raw data file under a dedicate directory, so it’s the only file in the directory, e.g.
C:\User\M.SartaJ Khan\IR Data
.Then, for graphing that data, you would run the script like this:
C:\Users\M.SartaJ khan> python IRAnalysis.py --dumps-dir "C:\Users\M.SartaJ khan\IR Data" graph
Note that you should have two regular
-
symbols before thedumps-dir
flag (and not one weird-
which translates to\x96
and confuses Python), and the path should be quoted if it contains spaces, and the command should be specified after it (e.g.graph
in this case, orcode
if you’d like code generation).Also note that if you have a recent version of the Arduino compiler, the generated code might not work for you, as the case for Andy. If you run into the same issue, and you’re able to find a way to change the generated Arduino code to fix it, please let me know. I’d love to fix the code generation so it works again.
November 17, 2015
Hi Itamar,
Thanks for replying so fast, Actually I’m trying to generate code. I saved my raw IR data in a dedicate directory, so itβs the only file in the directory, i.e. C:\User\M.SartaJ Khan\IR Data. After that I run the command as to your instruction C:UsersM.SartaJ khan> python IRAnalysis.py –dumps-dir “C:UsersM.SartaJ khanIR Data” code
This is what I’m getting upon running it
prog_uint16_t * getAcSendBuff() {
return 0;
}
It’s empty. I am unable to get the code to replace it in the uSendBuff_Toggle_Cool_Auto_25 function in the iracsender code??
I saved the file under the name :Toggle-Cool-Auto-25 with atleast 3 sample Raw IR codes
Thank you.
Josh
November 17, 2015
Hi Josh,
Can you paste the output of running the
dir
command on theIR Data
dir, and also any output of the IRAnalysis script?November 18, 2015
Hi Itamar,
Sorry I hope I’m not disturbing you a lot.
On running the dir command this is what i got
PS C:\Users\Project> dir
Directory: C:\Users\Project
Mode LastWriteTime Length Name
—- ————- —— —-
-a— 31/10/2015 1:08 AM 6435 IRAnalysis.py
-a— 18/11/2015 12:29 AM 1510 Toggle-Cool-Auto-25.txt
I am unable to get any output of IRAnalysis script.
Upon running the command All I get is this
PS C:\Users\Project> python IRAnalysis.py –dumps-dir “C:UsersProject” code
prog_uint16_t * getAcSendBuff() {
return 0;
}
It’s empty again. I have created an entirely new directory under the name Project which only have two files IRAnalysis script and my raw IR code file
Thank you.
Josh
November 18, 2015
Not disturbing at all π
Try removing the “.txt” extension from the filename and try again.
November 18, 2015
After removing the .txt extension
PS C:\Users\Project> dir
Directory: C:\Users\Project
Mode LastWriteTime Length Name
—- ————- —— —-
-a— 31/10/2015 1:08 AM 6435 IRAnalysis.py
-a— 18/11/2015 4:16 PM Toggle-Cool-Auto-25
PS C:\Users\Project> python IRAnalysis.py –dumps-dir “C:UsersProject” code
prog_uint16_t * getAcSendBuff() {
return 0;
}
It’s still empty.
The data I have saved in Toggle-Cool-Auto-25 is as follows:
E458A050
0xE458A050 (32 bits)
Raw (101): -3920 5700 -2550 400 -350 400 -950 350 -950 400 -350 400 -900 400 -350 400 -350 400 -350 400 -350 400 -900 400 -350 400 -350 400 -950 350 -400 350 -400 350 -400 350 -400 350 -950 400 -350 400 -350 350 -400 350 -400 350 -400 350 -400 350 -400 350 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -900 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -900 400 -350 400 -350 400 -350 400 -950
E458A050
0xE458A050 (32 bits)
Raw (101): -35558 5700 -2550 350 -400 350 -950 400 -950 350 -400 350 -950 400 -350 350 -400 350 -400 350 -400 350 -950 400 -350 400 -350 400 -900 400 -350 400 -350 400 -350 400 -350 400 -950 350 -400 350 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 400 -350 350 -400 350 -400 350 -400 350 -400 350 -400 350 -950 400 -350 400 -350 350 -400 350 -400 350 -400 350 -400 350 -400 350 -950 400 -350 400 -350 350 -400 350 -950
E458A050
0xE458A050 (32 bits)
Raw (101): -45616 5700 -2600 350 -400 350 -950 350 -950 400 -350 400 -950 350 -400 350 -400 350 -400 350 -350 400 -950 350 -400 350 -400 350 -950 350 -400 350 -400 350 -400 350 -400 350 -950 400 -350 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -400 350 -350 400 -350 400 -350 350 -1000 350 -400 350 -400 350 -400 350 -400 350 -400 350 -350 400 -350 350 -1000 350 -400 350 -400 350 -400 350 -950
Thank you
November 20, 2015
Hi,
Now in the output of your
dir
command I see that theIRAnalysis.py
script is also there, together with the IR data file. The script fails if thedumps-dir
contains anything other than what it expects (yes, I know, it’s not very robust..).I know your IR data is valid, because I copied it into a file named
Toggle-Cool-Auto-25
with no other files in a directory calledIR_data
, and ranpython IRAnalysis.py --dumps-dir IR_data code
, and got non-empty output:So try moving the data file to a sub-directory and running again with the
--dumps-dir
pointing to that sub-directory.