Deciphering the transmission
Important note: Most of what I achieved in this post was thanks to this post: https://perhof.wordpress.com/2015/03/29/reverse-engineering-hitachi-air-conditioner-infrared-remote-commands/. I opted to follow their method and get my own data rather than using theirs so this post details my experience.
One of the main outcomes is to mimic the remote control that comes with the aircon. The remote uses infrared to transmit information so I’m using an IR receiver with an arduino to decipher it.
I found three libraries to help me achieve this: AnalysIR, IRemote and IRLib.
To decode the signal I used some code from AnalysIR (https://www.analysir.com/blog/2014/03/19/air-conditioners-problems-recording-long-infrared-remote-control-signals-arduino/), although I had to make some changes to get it to work on my Leonardo board. My sketch is:
#define LEDPIN 13
#define maxLen 800
#define PIN 7
volatile unsigned int irBuffer[maxLen]; //stores timings - volatile because changed by ISR
volatile unsigned int x = 0; //Pointer thru irBuffer - volatile because changed by ISR
void setup() {
Serial.begin(115200); //change BAUD rate as required
attachInterrupt(digitalPinToInterrupt(PIN), rxIR_Interrupt_Handler, CHANGE);//set up ISR for receiving IR signal
}
void loop() {
// put your main code here, to run repeatedly:
Serial.println(F("Press the button on the remote now - once only"));
delay(5000); // pause 5 secs
if (x) { //if a signal is captured
digitalWrite(LEDPIN, HIGH);//visual indicator that signal received
Serial.println();
Serial.print(F("Raw: (")); //dump raw header format - for library
Serial.print((x - 1));
Serial.print(F(") "));
detachInterrupt(digitalPinToInterrupt(PIN));//stop interrupts & capture until finshed here
for (int i = 1; i < x; i++) { //now dump the times
if (!(i & 0x1)) Serial.print(F("-"));
Serial.print(irBuffer[i] - irBuffer[i - 1]);
Serial.print(F(", "));
}
x = 0;
Serial.println();
Serial.println();
digitalWrite(LEDPIN, LOW);//end of visual indicator, for this time
attachInterrupt(digitalPinToInterrupt(PIN), rxIR_Interrupt_Handler, CHANGE);//re-enable ISR for receiving IR signal
}
void rxIR_Interrupt_Handler() {
if (x > maxLen) return; //ignore if irBuffer is already full
irBuffer[x++] = micros(); //just continually record the time-stamp of signal transitions
I connected the S pin on the module to digital pin 7, the - pin to GND and the middle pin to 5V.
The output of a captured signal looks like this:
"Raw: (531) 3384, -1680, 432, -1252, 436, -408, 432, -412, 432, -412, 432, -412, 432, -412, 432, -408, 436, -408, 436, -408, 432, -412, 432, -412, 432, -412, 432, -1256, 432, -412, 432, etc.
The numbers alternate between positive and negative and indicate lengths of time that either some signal is being received or no signal is being received. I wrote a quick Python script to convert the timings to bytes in CSV format to open in LibreCalc (or Excel) later.
p = re.compile("Raw: \((\d+)\) (\d+, )(-\d+, )")
m = p.match(input)
header = input[0:24]
body = input[24:]
groups = re.findall("-*\d+", body)
data = []
bytes = []
for capture in groups:
n = round(int(capture)/400)
if n<0:
data.append(int((abs(n)-1)/2))
#data.append(2)
if (len(data)/8)%1 != 0:
print("Invalid data detected, incorrect amount of bits ({0})".format(len(data)))
sys.exit()
else:
cycles = int(len(data)/8)
for i in range(cycles):
start, stop = 8*(i), 8*(i+1)
slice = data[start:stop]
bytes.append(slice)
for byte in bytes:
print(','.join(str(e) for e in byte))
The output of which looks like this:
1,0,0,0,0,0,0,0
0,0,0,0,1,0,0,0
0,0,0,0,0,0,0,0
0,0,0,0,0,0,1,0
1,1,1,1,1,1,0,1
1,1,1,1,1,1,1,1
etc.
I then recorded data from a variety of different transmissions and only changed one setting at a time to work out which settings correspond to which bits.
It seems that only 12 bits are used in normal operation and 12 more are used for error detection.
Using this chart I can create my own signals to be sent to the aircon in Part 3.