An Air Conditioner Remote Replacement

9.6K3616

Intro: An Air Conditioner Remote Replacement

Update (01/2014): correction of the schematics and picture update to comply to the current setup (not necessary final but at least more reliable than the previous version).

This instructable follows my previous one (https://www.instructables.com/id/Reverse-engineering-of-an-Air-Conditioning-control)
Now we have a good understanding of what is sent by the remote, we can make our own.

Like the previous instructable, this circuit was made some months ago, so multiple solutions were tried, leading to a rather strange set up.

The setup goes like this :
A server (Pi or other) hosts a website presenting commands
Connected to the server (USB) is an Arduino with a custom Shield with an IR LED.

This instructable covers the Arduino connected by USB, the associated circuit and programming.

BOM:
- An Arduino, with auto reset function disabled (I used a seeeduino board, it has a switch to disable the auto-reset feature, but solutions exist to disable the feature with any arduino) - http://www.seeedstudio.com/depot/Seeeduino-V30-At...
- An IR LED. Be careful about the characteristics, such as max current in burst and light emission angle (the wider the angle the easier it is to aim the air conditioner but the less is the reach)
- A NPN transistor
- A set of resistors

- An RGB LED is used here but not necessary

This project is well under 30$, if you already have an arduino it would be less that 2$ I think

Note: As the project is old (although still ongoing... well more like "on pause" actually) I updated it so the schematics and pictures are more accurate and I also cleaned and simplified the code for it to be understandable here. But doing so I might have broken it to some extent so don't hesitate to tell me if you use it and encounter problems.

As you can see on the picture no box has been hurt during this instructable (I think I will print one someday). ;)

STEP 1: Circuit

Nothing complicated here, the circuit is mainly an LED driver with an NPN transistor and a resistor to limit the current flowing through the LED. I added RGB LED to indicate the schedule mode status (programmed, forced, off...) and a temperature probe. The circuit is powered by the USB cable of the arduino.

Tip: to check that the IR LED is working properly you can watch it through a digital camera (or a smartphone camera), The IR frequency can appear on the screen. I'm not sure it's good for the camera though.

STEP 2: Programming - Arduino Side

We need to communicate over serial interface and send IR commands.
For the IR remote part we will use the IRremote Arduino library (https://github.com/shirriff/Arduino-IRremote). This library can be used to send predefined data over IR, as LIRC does. But We'll use it only for its helper functions (to set the 38KHz frequency for IR and to manage timings while sending messages).

In the previous instructable we found that to communicate commands to the air conditioner we needed to send a lock sequence, a constant introduction, the lock sequence again, then the actual payload. Well the Arduino can very well have the introduction in its code and just wait for the payload. We could just send the options values and make the Arduino modify a template with these values, but to keep it simple we'll just provide the entire payload and let it send it as is. To send the bits it will turn the IR LED ON and OFF according the the timings found previously. 

Here the Arduino acts as a "slave", never initiating a communication itself. As I intend to make multiple uses of this unit I begin the communications by a single character 'I' meaning "send by IR", followed by 19 bytes of payload. The Arduino then sends the message to the air conditioner.
On the Arduino side the code can look a little over-complicated, but it's because it is supposed to serve as a radio relay as well, so the communication between the Pi and the Arduino over Serial interface won't always be just to send IR message.
Here is the arduino source that will be used to send commands to the Air Conditioner.

A bit of explanation:
-> the loop code just checks if a command is ready. In that case it executes the command (for now just "send an IR message").
-> the SerialEvent() is called after a loop(), if there is serial data in the buffer. In this function the command is read, the payload is prepared, then a flag is set to tell the loop() function that a command is ready

serialEvent will receive ASCII characters representing HEX values. To convert it we use the sscanf function with the "%x" (hexadecimal) format option. We store the received characters in a 5 char array and fill it with : "0x"+the 2 chars+'\0' (to finish the string).

          char hexConvert[5] = "0x";
          // ASCII is received
          for (int i = 0 ; i < 19 ; i++){
            hexConvert[2] = Serial.read();
            hexConvert[3] = Serial.read();
            hexConvert[4] = '\0';
            // convert the received chars to a numeric value in payload[i]
            sscanf(hexConvert, "%x", &(payload[i]));
          } 
At this point payload[i] contains the binary value represented by the received chars.

In the send command proper timings are used to send locks, introduction, and payload. To send data, the code has to send each bit separately with the function irsend.mark(duration) and irsend.space(duration). As explained in the previous instructable the duration is of about 400us (should actually be closer to 420 I think) for the mark (ON state), and either 400us or 1300us for the space (OFF state) to send a 0 or a 1, respectively.
To send each bit of each byte the following code is used :

for (int s = 0 ; s < payloadSize ; s++ ){
    for (int i = 7 ; i >= 0 ; i--){ // Most Significant Bit comes first
      irsend.mark(irSpace);
      irsend.space((payload[s] >> i) & 1 == 1 ? irOne : irZero);
    }
  }

If you are not familiar with bitwise operations there are plenty of tutorials available on the net.
What happens here is:
For each byte of the payload (works the same manner with the intro):
   For i going from 7 to 0 
      shift the byte for i ranks to the right
      look at the last bit
      if that bit is one, send a signal of "irOne" us (1300us), else send a signal of "irZero" us (400us)

Basically what we obtain by shifting the byte right of N ranks (adding zeros at the left side) is the Nth bit on the rightmost side. When masking with a bitwise AND (&) 1 we take only the rightmost bit, so globally we obtain the Nth bit value.

Example with 5B in hexa = 0101 1011 in binary. The parenthesis are here to emphasize the shifted bits, for readability...

i = 7 => shift (0)101 1011 for 7 ranks  => 0000 000(0)
               mask with 1 => 0000 0000 & 0000 0001 =  0 => send irZero

i = 6 => shift (01)01 1011 for 6 ranks => 0000 00(01)
               mask with 1 => 0000 0001 & 0000 0001 =  1 => send irOne

i = 5 => shift (010)1 1011 for 5 ranks => 0000 0(010)
               mask with 1 => 0000 0010 & 0000 0001 =  0 => send irZero

i = 4 => shift (0101) 1011 for 4 ranks => 0000 (0101)
               mask with 1 => 0000 0101 & 0000 0001 =  1 => send irOne

i = 3 => shift (0101 1)011 for 3 ranks => 000(0 1011)
               mask with 1 => 0000 1011 & 0000 0001 =  1 => send irOne

i = 2 => shift (0101 10)11 for 2 ranks => 00(01 0110)
               mask with 1 => 0001 0110 & 0000 0001 =  0 => send irZero

i = 1 => shift (0101 101)1 for 1 rank => 0(010 1101)
               mask with 1 => 0010 1101 & 0000 0001 =  1 => send irOne

i = 0 => shift (0101 1011) for 0 rank => (0101 1011)
               mask with 1 => 0101 1011 & 0000 0001 =  1 => send irOne

So we sent bits in the left to right order.

Note that IRremote library uses pin3 to send messages. I suppose it could be possible to use the pin 11 (same timer for PWM) by modifying the target pin in the library code, but I haven't tested it.

STEP 3: Programming - PC Side

On the PC side (be it a Pi or any other linux PC), here is a program to send commands to the arduino. It is based on the previously given "encode.c" to build an IR command.

The program will build the command payload according to the command line options (see usage) and send the command to the device connected to the USB port.
The port can be specified with the -u option, otherwise it will try /dev/ttyUSB0, /dev/ttyUSB0, /dev/ttyACM0, /dev/ttyACM1, as the arduinos can be mapped as (at least) these files, depending on the arduino type and disconnections/reconnections.

As indicated in the introduction the Arduino has to have its autoreset function disabled, otherwise it will reset each time a connection is made. This is the way the IDE can program the chip, but it is not desirable in our case. Should you wish to test the code with an autoreset enabled arduino, you should add a sleep(2) command after the connection (after opening the tty file) to let the arduino reset and being able to get the messages.

As expected the code sends a 'I' character to the Arduino, then sends the payload and read the file to wait the Arduino's response. This step is not crucial for a simple command send, but it will be if the goal is to fetch information from the Arduino itself or another device controlled by radio. For now the Arduino just responds "OK" so the program just displays the response as is.

As for the Arduino part, there is a part of the code where the message is converted to ASCII representation of the binary message.

message[0] = 'I';
  // turning binary payload into ASCII characters (HEX representation)
  for (i = 1, j = 0 ; i < 39 ; i+=2, j++){
   message[i] = (payload[j] & 0xF0) >> 4;
   if (message[i] < 10){
     message[i] += '0'; // ASCII
   } else {
     message[i] += 'A' - 10;
   }
   message[i+1] = payload[j] & 0x0F;
   if (message[i+1] < 10){
     message[i+1] += '0'; // ASCII
   } else {
     message[i+1] += 'A' - 10;
   }
   printf("0x%c%c ",message[i], message[i+1]);
  }

Once again bitwise operators are used to select and shift the first or last 4 bits and convert it to a digit or a character (A to F):
message[i] = (payload[j] & 0xF0) >> 4;
Here we take the byte and apply a mask 11110000 so we obtain only the foremost left bits, and we shift these bits to the right
message[i+1] = payload[j] & 0x0F;
Same here but selected bits are already at the right of the byte so no shift is necessary.

if (message[i] < 10){
     message[i] += '0'; // ASCII
   } else {
     message[i] += 'A' - 10;
   }

Here if the digit is < 10 the ASCII value is comprised between 48 and 57 (decimal), and we just have to add the represented value to the character '1' value (which is 48 but '1' is usable without knowing the ASCII table by heart ;) ). The same apply to characters A to F but A(Hex) is 10(Decimal) so if we added the represented value to the ASCII value for 'A' we'd be 10 values to high. So we add the value -10 and end up with the proper character.

Now, you might wonder why we bother converting from Hex to ASCII and from ASCII to Hex on the other side. You'd be right to ;)
Indeed, it is useless in that case, and the first version of the code worked directly with binary values. Actually it worked by sending only the 5 bytes that are actually useful and the Arduino made the update of the payload template. So now we send 40 bytes instead of 5. For what reason?
Well, although this is out of this particular instructable I indicated that I had a second micro controller in my room for the second Air Conditioner unit. This chip is a Seeediuno Wifi Bee. I found it difficult to implement the correct behavior in that chip using sockets so I ended up using the embedded minimalist web server, so I send the full IR command in the URL in Hex characters.
Now you have the reason: I wanted to use the same code to control both units, sending data to USB or over WiFi but manipulating the same message, in the same format.

STEP 4: Conclusion

In this instructable I obtained a proper replacement to my air conditioner remote with an Arduino connected to a PC. Now I can control the unit by issuing commands to my computer, which is pretty cool :)

$ sendCmd -m HEAT -q -s 5 23
will set the air conditioner to HEAT mode, with the "quiet" option activated (visible by an LED on the unit), with the air flow stuck in high position (swing 5) and a temperature of 23°

$ sendCmd -o OFF
sends an OFF command that turns off the unit.


Air conditioning control from Mat_fr on Vimeo.


In the video I use another version of the code which displays plenty of messages, just ignore that ;)

OK, That's fun. But what is really cool is that from this starting point I will be able to:
- send commands from a remote location (connecting via ssh for instance)
- set a command to be sent at a specific time using the "at" command or a crontab

What comes next (in my project, not necessarily in instructables...) is:
- a remote controller to relay orders to the second unit
- a web page to send commands from a browser instead of a command line (as seen in the picture, sorry it's in french ;) )
- a database to store commands to be sent at specific times, potentially with conditions such as current season, current inside or outside temperature, ...
- a web page to manage programs

Hope this instructable helps.

15 Comments

Hi Mat, how about if my AC Panasonic Remote Control is different from yours, the buttons are just on off, air swing, mode, fanspeed, there isnt option button, does the serial IRsender for arduino same as on your instructables? thank you

Hi,

I can't say for sure, but it is possible the protocol would still be the same: the protocol I analyzed has many chunks that are not used on my AC. I can imagine a firm like Panasonic would develop only one protocol for all their products, using more or less options depending on the product. So you can test the code on your AC.

On the other hand, there are 2 parts on the signal, maybe the first part corresponds to the product to control, I don't know. If that's the case, you could have to change the "introduction" of the signal to match your remote. But that's only a guess. See my other instructable for details.

Hi Mat, so what i have to do is compiling the SerialIRSend on the Arduino and compile the sendcmd on Raspberry Pi right? i have done it but it didnt work, do you have any account to chat like facebook? thank you very much

Is it possible to use a teensy or a digispark attiny85 instead of the arduino?

It should be possible to use either one, although I'm not sure the IR
remote lib works with the Attiny85. Anyway it's just a matter of setting
the correct PWM frequency (38KHz) for it to work, so I assume I could
have done without the lib.

The other problem with the Attiny85 is that it doesn't have hardware serial if I remember correctly, so you would have to work out another way of communicating with it whatever the chosen solution (software serial or network).

I see, but how will I be able to read the IR signal from my remote with the teensy for example?

Where will the information go?

This is explained in my other instuctable. I used a Raspberry Pi with LIRC installed and an IR Receiver module to read the signal from my remote. You could do the same with an Arduino or equivalent plugged into your computer and an IR receiver. I think the IRRemote library has an example to dump the raw signal to the serial connection, so you can read it from the serial console of the Arduino IDE.

This instructable assumes you already know how the signal is made.

Hi Mat,

Your article is very good. I was doing the same exercise with my air conditioner when I found your work ! And Cherry on the cake, I have the same one.

I try to do a n autonomous WiFi to InfraRed convertor.

The idea is to send via IP commands to the convertor which will Activate functions on the air conditioner using IR.

Regards and many thanks !!!

Seb

Hi, glad it helped :)
I tried using WIFI for the unit in my room, unfortunately it kept loosing the connection... I ended up building an atmega based circuit with an enc28j60 ethernet module. Cheap and reliable :)

Hi Mat,

I finally bought the IRkit (see http://getirkit.com/en/). I did the same job than you to understand how to generate the good codes !

I would like to thank you because with your study, it was easier for me (expectaly for the CRC code).

Regards

Thanks a lot ! You are such a clever, patient and nice person. I took the instructable as an inspiration. Because it is enough for me to switch on, or off my AC (Toshiba) I am using a simple program which I would never write without your help. For unpatient (like me)This is the simplest way how I switch off Toshiba aircondition (Arduino codes captured with LIRC on raspberry):

#include <IRremote.h>

IRsend irsend;

void setup()

{

pinMode(13,OUTPUT);

digitalWrite(13,HIGH);

delay(5000);

digitalWrite(13,LOW);

delay(5000);

digitalWrite(13,HIGH);

delay(5000);

digitalWrite(13,LOW);

irsend.enableIROut(38);

powerToshOff();

}

void loop()

{}

void powerToshOff()

{ //this is just replayng the values as recorded with lirc irrecord -m

irsend.space( 4824102 ); irsend.mark( 4396 );irsend.space( 4355 );irsend.mark( 646 );//here comes the of values as captured with irrecord -m ; each odd is space each even is mark

irsend.space(0); // to leave the LED low

}

Hey,

Glad my instructable helped you.

I just made a replacement circuit for this remote, I should update the instructable at some point.

Hi Mat,

Thanks a lot for posting this article, it's great reading. I have been trying myself to control a Pana HVAC with an Arduino by sending raw codes, but have struggled to make it stable. i realize now that it's probably my timings and maybe the checksum that is not accurate enough. Hence i would like to use your code and use the arguments for the swing, fan, temperature etc.

But unfortunately im a dumb novice in the codings and are struggeling to see how the payload should be build if i am only to use the arduino.

Are you able to provide a little guidance on how the payload variable would look lige, if for instance i would execute a HEAT commando of 5 deg C, powerfull fan, Swing at position 1. And if possible also a turn off command. Then i think i would manage to make things work :-)

/OMJ

Hi,

It is possible to make the Arduino alone handle the computation on the payload, but how will you send the command ? I use the serial interface (or network, depending on the unit) to send the command to relay to the AC so I decided the computer would be the best place to build the payload.

You could just send parameters and let the Arduino build the payload but as you already have something to send...

If you just want to send pre-recorded commands you can very well declare bytes arrays inside the code, one for each command you want to be able to send (there is limited memory on the chip, though).

If your problems come from the timings that would not match what the AC expects there is not much I can do to help... You'd need an IR receiver and a computer with lirc for instance, to record the timings, or an oscilloscope to see what gets recorded.

If the problem is the checksum here what I do:

int checksum = 0;
for ( i = 0 ; i < 19 ; i++ ){
checksum+=reverse_byte(frame[i]);
}
checksum = reverse_byte(checksum);
checksum &= 0xFF; // force 1 byte

See my other instructable for the reverse byte code (which is actually a straightforward list of reversed values for each possible byte, not very memory efficient for an arduino) : https://www.instructables.com/id/Reverse-engineering-of-an-Air-Conditioning-control/