Hello,
This is a request to add support for Delonghi PAC A95
https://www.del…onghi.com/en-int/products/comfort/air-conditioning/portable-air-conditioners/pinguino-air-to-air-pac-a95
I have reverse engineered the IR protocol below which I believe is the most intensive work of the project, but writing code is not my forte.
The protocol is 64bit.
```text
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| FIXED HEADER | TEMPERATURE | FAN |CF|ON| MODE | B| S| 0 0|TO| ON TIME HOUR | 0 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| ON TIME MIN | 0 0| OFF TIMER | CHECKSUM |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
FIXED HEADER: (bits 63-56)
11001010
TEMPERATURE: (bits 55-51) / reversed 5bit
10000 - lowest temperature (18C / 64F)
11110 - highest C temperature (32C)
11011 - highest F temperature (90F)
FAN: (bits 50-49)
00 - AUTO
10 - HIGH
01 - MID
11 - LOW
CF: (bit 48) / select temperature scale C or F
0 - C
1 - F
ON: (bit 47)
0 - OFF
1 - ON
MODE: (bits 46-44)
000 - AC (cool)
100 - DEH (dehumidify)
010 - FAN
001 - SMART
B: (bit 43) / boost
0 - OFF
1 - ON
S: (bit 42) / sleep
0 - OFF
1 - ON
TO & ON TIMER HOUR & ON TIME MIN: (bits 39-24) / all unused bits are 0
TO: (bit 39) / turn on timer on or off
0 - OFF
1 - ON
ON TIME HOUR: (bits 38-34) / reversed 5bit : hours delta - 0 to 23 hours between current time and timer on time
ON TIME MIN: (bits 31-26) / reversed 6bit : minutes delta - 0 to 59 minutes between current time and timer on time
E.g let's assume the current time is 19:15 and we want to set the timer on time to 19:16
This means the on timer should be activated within the next 1 min, so the delta time is 00:01
This gives the following bits for HOUR & MIN
10000000 10000000
^^^^^ ^^^^^^
hours min
00001 - 1 hour
00010 - 2 hour
000001 - 1 min
000011 - 3 min
OFF TIMER: (bit 23-8) / I assumed it works the same as the on timer, but I have not actually valided it.
If the timers are not used (which I'm not planning to do) all bits are 0.
CHECKSUM: (bit 7-0) / reversed
This is the CheckSum8 Modulo 256 of the 7 reversed bytes.
There are some constraints between the different mode off operation.
MODE DEH (100)
-> FAN AUTO (00)
-> TEMP all bits 00000
MODE FAN (010)
-> can never be FAN AUTO (00) only LOW, MID or HIGH
-> TEMP all bits 00000
MODE SMART (001)
-> FAN AUTO (00)
-> TEMP all bits 01100
BOOST ON
-> FAN bits remain what they were
-> TEMP bits remain what they were
```
Information collected via auto_analyse_raw_data.py
Raw: (131) {8984, 4200, 608, 1516, 608, 1516, 612, 472, 556, 528, 560, 1564, 556, 528, 560, 1564, 564, 528, 552, 1572, 556, 1568, 556, 528, 552, 1572, 556, 1568, 560, 1564, 552, 1572, 556, 1576, 552, 1568, 560, 528, 560, 524, 556, 528, 552, 532, 560, 528, 552, 532, 556, 532, 560, 1564, 560, 528, 552, 1568, 560, 1564, 564, 524, 556, 528, 560, 524, 556, 536, 556, 1568, 560, 524, 556, 1568, 560, 1564, 584, 500, 588, 496, 584, 500, 592, 500, 588, 496, 584, 500, 592, 496, 584, 500, 588, 496, 584, 500, 592, 492, 584, 508, 584, 500, 588, 496, 584, 500, 592, 496, 584, 500, 580, 504, 584, 500, 580, 508, 584, 1544, 584, 500, 588, 496, 584, 1540, 588, 500, 580, 1540, 588, 1536, 588, 500, 592};
```
$ ./auto_analyse_raw_data.py -f ../raw.txt -g -n TestExample
Found 131 timing entries.
Potential Mark Candidates:
[8992, 620]
Potential Space Candidates:
[4188, 1516, 528]
Guessing encoding type:
Looks like it uses space encoding. Yay!
Guessing key value:
kTestExampleHdrMark = 8992
kTestExampleHdrSpace = 4188
kTestExampleBitMark = 609
kTestExampleOneSpace = 1511
kTestExampleZeroSpace = 477
Decoding protocol based on analysis so far:
kTestExampleHdrMark+kTestExampleHdrSpace+1100101010000000100000000000000000000000000000000000000010101010
Bits: 64
Hex: 0xCA808000000000AA (MSB first)
0x5500000000010153 (LSB first)
Dec: 14591803530168762538 (MSB first)
6124895493223940435 (LSB first)
Bin: 0b1100101010000000100000000000000000000000000000000000000010101010 (MSB first)
0b0101010100000000000000000000000000000000000000010000000101010011 (LSB first)
Total Nr. of suspected bits: 64
```
Output from auto_analyse_raw_data.py -g -n TestExample
```c
// Copyright 2019 David Conran (crankyoldgit)
// Support for TestExample protocol
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// WARNING: This probably isn't directly usable. It's a guide only.
// See https://github.com/crankyoldgit/IRremoteESP8266/wiki/Adding-support-for-a-new-IR-protocol
// for details of how to include this in the library.
const uint16_t kTestExampleHdrMark = 8992;
const uint16_t kTestExampleBitMark = 609;
const uint16_t kTestExampleHdrSpace = 4188;
const uint16_t kTestExampleOneSpace = 1511;
const uint16_t kTestExampleZeroSpace = 477;
const uint16_t kTestExampleFreq = 38000; // Hz. (Guessing the most common frequency.)
const uint16_t kTestExampleBits = 64; // Move to IRremoteESP8266.h
const uint16_t kTestExampleOverhead = 3;
#if SEND_TESTEXAMPLE
// Function should be safe up to 64 bits.
void IRsend::sendTestExample(const uint64_t data, const uint16_t nbits, const uint16_t repeat) {
enableIROut(kTestExampleFreq);
for (uint16_t r = 0; r <= repeat; r++) {
uint64_t send_data = data;
// Header
mark(kTestExampleHdrMark);
space(kTestExampleHdrSpace);
// Data Section #1
// e.g. data = 0xCA808000000000AA, nbits = 64
sendData(kTestExampleBitMark, kTestExampleOneSpace, kTestExampleBitMark, kTestExampleZeroSpace, send_data, 64, true);
send_data >>= 64;
// Footer
mark(kTestExampleBitMark);
space(kDefaultMessageGap); // A 100% made up guess of the gap between messages.
}
}
#endif // SEND_TESTEXAMPLE
#if DECODE_TESTEXAMPLE
// Function should be safe up to 64 bits.
bool IRrecv::decodeTestExample(decode_results *results, const uint16_t nbits, const bool strict) {
if (results->rawlen < 2 * nbits + kTestExampleOverhead)
return false; // Too short a message to match.
if (strict && nbits != kTestExampleBits)
return false;
uint16_t offset = kStartOffset;
uint64_t data = 0;
match_result_t data_result;
// Header
if (!matchMark(results->rawbuf[offset++], kTestExampleHdrMark))
return false;
if (!matchSpace(results->rawbuf[offset++], kTestExampleHdrSpace))
return false;
// Data Section #1
// e.g. data_result.data = 0xCA808000000000AA, nbits = 64
data_result = matchData(&(results->rawbuf[offset]), 64,
kTestExampleBitMark, kTestExampleOneSpace,
kTestExampleBitMark, kTestExampleZeroSpace);
offset += data_result.used;
if (data_result.success == false) return false; // Fail
data <<= 64; // Make room for the new bits of data.
data |= data_result.data;
// Footer
if (!matchMark(results->rawbuf[offset++], kTestExampleBitMark))
return false;
// Success
results->decode_type = decode_type_t::TESTEXAMPLE;
results->bits = nbits;
results->value = data;
results->command = 0;
results->address = 0;
return true;
}
#endif // DECODE_TESTEXAMPLE
```
Below is python code that I added to ./auto_analyse_raw_data.py to validate the checksum.
```python
import array
from textwrap import wrap
# Call as message.display_checksum(binary_value)
def display_checksum(self, binary_str):
payload = binary_str[:-8]
checksum = binary_str[-8:]
rev_checksum = checksum[::-1]
array = wrap(payload, 8)
rev_array = [x[::-1] for x in array]
self.output.write(" Hex: ")
for i in rev_array:
self.output.write("%s" %
(("{0:0%dX}" % (len(i) / 4)).format(int(i, 2))))
self.output.write(" + %s (Swap)\n" %
(("{0:0%dX}" % (len(rev_checksum) / 4)).format(int(rev_checksum, 2))))
inbytes = [int(x, 2) for x in rev_array]
cal_checksum = 0
for byte in inbytes:
cal_checksum += byte
self.output.write(" Calculated checksum: %s \n" %
('{:X}'.format(cal_checksum % 256)))
```
Finally I have attached a selection of 90 raw samples.
[raw.txt](https://github.com/crankyoldgit/IRremoteESP8266/files/4563210/raw.txt)