Irremoteesp8266: Request to add new protocol - Delonghi PAC A95

Created on 1 May 2020  路  17Comments  路  Source: crankyoldgit/IRremoteESP8266

Hello,

This is a request to add support for Delonghi PAC A95
https://www.delonghi.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.

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|     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

// 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.

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

enhancement

All 17 comments

Hi, thanks for the data & analysis. It looks well done, Congrats!
It should make this a simple & quick process.

I've created branch: https://github.com/crankyoldgit/IRremoteESP8266/tree/DelonghiAc to track the progress of adding it to the library.
At the moment, it should be able to send and decode the protocol in basic form (i.e. no settings)
You should be able to use the numbers generated via this for analysis.
i.e. I've changed the bit ordering to LSBF as you seem to indicate everything is reversed.
e.g. Temp integers should now be in the "logical" order, and the checksum is now at the start of the value, and the "fixed" bit is now at the end.

I'll work on adding detailed support shortly.

Hi,

Thanks for the quick patch. I just tested below code and I can confirm the airco turned on/off with the with the expected temp/mode.

void airco_on(){
  irsend.sendDelonghiAc(0x5500000000010153);
  server.send(200, "text/plain", "AIRCO on");
}

void airco_off(){
  irsend.sendDelonghiAc(0x5400000000000153);
  server.send(200, "text/plain", "AIRCO off");
}

Thanks for the feedback/testing!

Branch updated with support for everything but the timers. I'll look at adding them soon.

Please test and let me know etc.

Hi,

I confirm that following items are working as expected.

IRDelonghiAc ac(kIrLed);
ac.on();
ac.send();
ac.toString()
ac.setBoost(1);
ac.setBoost(0);
temp = ac.getTemp();
temp++;
ac.setTemp(temp);
ac.setFan(kDelonghiAcFanHigh);
ac.setFan(kDelonghiAcFanLow);
ac.begin();
ac.stateReset();
ac.off();

Excellent. Thanks for testing it. I'm glad nothing broken was found.

I will see about testing the On Timer later.

For now I tested a few more things and I found 2 unexpected, but otherwise not blocking, behaviors.

1) Switching from Mode Auto (Smart) to Mode Cool always sets temperature to 17C

Steps to reproduce.

In Mode Cool
Set temp to e.g. 22C
Switch to Mode Auto, this hides the temperature on the AC display/remote.
Switch back to Mode Cool.
Result: the AC display will now show a temperature of 17C.
Expected Result: the AC display should show a temperature of 22C.

The temperature can be increased from 17C to 18C without any problems.
Using the original AC temp up|down buttons does not allow setting a temperature below 18C.

2) Setting Fan Mode to Auto while in Mode Fan sets Fan to high.

Steps to reproduce.

In Mode Fan
Set Fan to Low or Medium.
Set Fan to Auto.
Result: Fan is set to High.
Expected Result: Fan mode should remain Low or Medium.

Using the FAN button when running in Mode Fan on the AC only cycles through low-medium-high.

As I said both issues or not hindering operation.

Thanks for the feedback & testing. Appreciated.

  1. Switching from Mode Auto (Smart) to Mode Cool always sets temperature to 17C

Fixed/Done.

  1. Setting Fan Mode to Auto while in Mode Fan sets Fan to high.

Fixed/Done.

Please note, it's not the library's primary purpose to fully emulate the IR remote control operation of these units. Some of that logic/responsibility has to fall on the user of the library.
i.e. Some people don't need/want that extra control and the code space required to implement it. They can optionally add it if they need it.

Hi,

I can confirm that the on timer is working and the 2 previously reported oddities are also resolved.
Yes I fully understand that some of the logic has to be in the end-user application itself. I was just referencing to how the original remote is operating.

Many thanks again for adding the requested support in such a short time to the library.

Any chance you can confirm the off timer bits?

As I expected the off timer is working exactly the same as the on timer but on bit positions 23 to 8.

Sending off time 15:30 at actual time 14:18 gives following result.
Delta time "1:12" gives following bits 11000000 00110000
Sending off time 12:38 at actual time 14:24 gives following result.
Delta time "22:14" gives following bits 10110100 01110000

OFF TIMER: (bits 23-8)
bit 23:
0 - OFF
1 - ON
bits 22-18 (5bits) / reversed : hours delta
bits 15-10 (6bits) / reversed : minutes delta

Ahh. This why I asked. I wasn't sure if the Timer bit before which turned it on or off was for both timers or if there was enable/disable control for each one.
I'll add support for it shortly.

Can you provide me with a value capture (i.e. from IRrecvDumpV2 etc) for a message with the off-time set please? Pref, with a description of what it should be. ;-)

Off Timer added.

Below the output from setting off timer to 8:51 when the time on the remote displayed16:05.

IRrecvDumpV2 is now running and waiting for IR input on Pin 14
Timestamp : 000055.256
Library   : v2.7.6

Protocol  : DELONGHI_AC
Code      : 0xB12E210000000F53 (64 Bits)
Mesg Desc.: Power: Off, Mode: 0 (Cool), Fan: 0 (Auto), Temp: 32C, Turbo: Off, Sleep: Off, On Timer: Off, Off Timer: 16:46
uint16_t rawData[131] = {8984, 4184,  554, 1568,  558, 1564,  562, 522,  556, 526,  552, 1570,  556, 526,  562, 1560,  554, 534,  556, 1566,  558, 1562,  552, 1570,  556, 1566,  560, 524,  554, 528,  560, 522,  556, 532,  556, 526,  562, 522,  556, 526,  552, 530,  558, 526,  552, 530,  558, 526,  554, 534,  554, 528,  560, 524,  554, 528,  560, 522,  556, 528,  562, 522,  556, 528,  562, 526,  552, 532,  556, 526,  554, 530,  558, 526,  552, 530,  558, 524,  554, 528,  560, 528,  560, 1562,  554, 530,  558, 524,  554, 528,  560, 522,  556, 1566,  558, 524,  554, 534,  554, 528,  560, 1562,  552, 1568,  556, 1564,  560, 522,  556, 1566,  560, 524,  554, 534,  554, 1566,  560, 522,  554, 528,  562, 522,  554, 1566,  558, 1562,  552, 530,  558, 1566,  558};  // DELONGHI_AC B12E210000000F53
uint64_t data = 0xB12E210000000F53;

Thanks for that. Phew, for a minute there I thought something was horribly wrong with the "16:46" result, until I did the maths.

So it all looks good then to me.

FYI, the changes mentioned above have now been included in the latest release (v2.7.7) of the library.

Thanks. This would greatly facilitate integration into esphome without having to manually overwrite the src.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MehranMazhar picture MehranMazhar  路  5Comments

direk picture direk  路  6Comments

AsimZulfiqar67 picture AsimZulfiqar67  路  6Comments

Shalabyer picture Shalabyer  路  7Comments

C0rn3j picture C0rn3j  路  5Comments