Add basic support for HITACHI_AC3 protocol. (#1063)

* Supports Hitachi PC-LH3B
* Add send & decode routines.
  - Support all known state/bit sizes captured so far.
* Update ancillary support routines.
* Add synthetic and real unit test cases.
* very basic IRHitachiAc3 class added.
  - Enough code to verify the checksum/integrity check for the protocol.
  - Add integrity check to `decodeHitachiAc3()`

For #1060
This commit is contained in:
David Conran
2020-03-24 13:29:13 +09:30
committed by GitHub
parent 6d265b567a
commit a31c6bdf88
11 changed files with 631 additions and 19 deletions

View File

@@ -1,6 +1,6 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Sat Mar 7 23:57:26 2020 --->
Last generated: Tue Mar 17 13:45:51 2020 --->
# IR Protocols supported by this library
| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
@@ -28,7 +28,7 @@
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C<BR>YAW1F remote | YAW1F<BR>YBOFB | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | YAW1F<BR>YBOFB | Yes |
| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C<BR>HSU07-HEA03 remote<BR>YR-W02 remote | | Yes |
| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>RAR-8P2 remote<BR>RAS-35THA6 remote<BR>RAS-AJ25H A/C<BR>Series VI A/C (Circa 2007) | | Yes |
| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>PC-LH3B (HITACHI_AC3)<BR>RAR-8P2 remote<BR>RAS-35THA6 remote<BR>RAS-AJ25H A/C<BR>Series VI A/C (Circa 2007) | | Yes |
| [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - |
| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes |
@@ -107,6 +107,7 @@
- HITACHI_AC
- HITACHI_AC1
- HITACHI_AC2
- HITACHI_AC3
- HITACHI_AC424
- INAX
- JVC

View File

@@ -1518,6 +1518,23 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kFujitsuAcStateLength);
break;
case HITACHI_AC3:
// HitachiAc3 has two distinct & different size states, so make a best
// guess which one we are being presented with based on the number of
// hexadecimal digits provided. i.e. Zero-pad if you need to to get
// the correct length/byte size.
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3MinStateLength));
// If we think it isn't a "short" message.
if (stateSize > kHitachiAc3MinStateLength)
// Then it probably the "normal" size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3StateLength));
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kHitachiAc3StateLength);
break;
case MWM:
// MWM has variable size states, so make a best guess
// which one we are being presented with based on the number of

View File

@@ -641,10 +641,28 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
if (decodeHaierACYRW02(results, offset)) return true;
#endif
#if DECODE_HITACHI_AC424
// HitachiAc424 should be checked before HitachiAC & HitachiAC2
// HitachiAc424 should be checked before HitachiAC, HitachiAC2,
// & HitachiAC184
DPRINTLN("Attempting Hitachi AC 424 decode");
if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true;
#endif // DECODE_HITACHI_AC2
#endif // DECODE_HITACHI_AC424
#if DECODE_MITSUBISHI136
// Needs to happen before HitachiAc3 decode.
DPRINTLN("Attempting Mitsubishi136 decode");
if (decodeMitsubishi136(results, offset)) return true;
#endif // DECODE_MITSUBISHI136
#if DECODE_HITACHI_AC3
// HitachiAc3 should be checked before HitachiAC & HitachiAC2
// Attempt normal before the short version.
DPRINTLN("Attempting Hitachi AC3 decode");
// Order these in decreasing bit size, as it is more optimal.
if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) ||
decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3MinBits))
return true;
#endif // DECODE_HITACHI_AC3
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode");
@@ -759,10 +777,6 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Daikin152 decode");
if (decodeDaikin152(results, offset)) return true;
#endif // DECODE_DAIKIN152
#if DECODE_MITSUBISHI136
DPRINTLN("Attempting Mitsubishi136 decode");
if (decodeMitsubishi136(results, offset)) return true;
#endif // DECODE_MITSUBISHI136
#if DECODE_SYMPHONY
DPRINTLN("Attempting Symphony decode");
if (decodeSymphony(results, offset)) return true;

View File

@@ -485,6 +485,12 @@ class IRrecv {
const uint16_t nbits = kHitachiAc1Bits,
const bool strict = true);
#endif
#if DECODE_HITACHI_AC3
bool decodeHitachiAc3(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kHitachiAc3Bits,
const bool strict = true);
#endif // DECODE_HITACHI_AC3
#if DECODE_HITACHI_AC424
bool decodeHitachiAc424(decode_results *results,
uint16_t offset = kStartOffset,

View File

@@ -411,6 +411,20 @@
#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC2
#ifndef DECODE_HITACHI_AC3
#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC3
#ifndef SEND_HITACHI_AC3
#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC3
#ifndef DECODE_HITACHI_AC424
#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC424
#ifndef SEND_HITACHI_AC424
#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC424
#ifndef DECODE_GICABLE
#define DECODE_GICABLE _IR_ENABLE_DEFAULT_
#endif // DECODE_GICABLE
@@ -558,13 +572,6 @@
#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_
#endif // SEND_DAIKIN152
#ifndef DECODE_HITACHI_AC424
#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC424
#ifndef SEND_HITACHI_AC424
#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC424
#ifndef DECODE_EPSON
#define DECODE_EPSON _IR_ENABLE_DEFAULT_
#endif // DECODE_EPSON
@@ -589,7 +596,7 @@
DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \
DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \
DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424)
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
@@ -703,8 +710,9 @@ enum decode_type_t {
SONY_38K,
EPSON, // 75
SYMPHONY,
HITACHI_AC3,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = SYMPHONY,
kLastDecodeType = HITACHI_AC3,
};
// Message lengths & required repeat values
@@ -782,6 +790,10 @@ const uint16_t kHitachiAc1StateLength = 13;
const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8;
const uint16_t kHitachiAc2StateLength = 53;
const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8;
const uint16_t kHitachiAc3StateLength = 27;
const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8;
const uint16_t kHitachiAc3MinStateLength = 15;
const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8;
const uint16_t kHitachiAc424StateLength = 53;
const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8;
const uint16_t kInaxBits = 24;

View File

@@ -618,6 +618,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kHitachiAc1Bits;
case HITACHI_AC2:
return kHitachiAc2Bits;
case HITACHI_AC3:
return kHitachiAc3Bits;
case HITACHI_AC424:
return kHitachiAc424Bits;
case KELVINATOR:
@@ -959,6 +961,11 @@ bool IRsend::send(const decode_type_t type, const unsigned char *state,
sendHitachiAC2(state, nbytes);
break;
#endif // SEND_HITACHI_AC2
#if SEND_HITACHI_AC3
case HITACHI_AC3:
sendHitachiAc3(state, nbytes);
break;
#endif // SEND_HITACHI_AC3
#if SEND_HITACHI_AC424
case HITACHI_AC424:
sendHitachiAc424(state, nbytes);

View File

@@ -471,6 +471,12 @@ class IRsend {
const uint16_t nbytes = kHitachiAc2StateLength,
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif
#if SEND_HITACHI_AC3
void sendHitachiAc3(const unsigned char data[],
const uint16_t nbytes, // No default as there as so many
// different sizes
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif // SEND_HITACHI_AC3
#if SEND_HITACHI_AC424
void sendHitachiAc424(const unsigned char data[],
const uint16_t nbytes = kHitachiAc424StateLength,

View File

@@ -147,6 +147,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::HITACHI_AC1;
else if (!strcasecmp(str, "HITACHI_AC2"))
return decode_type_t::HITACHI_AC2;
else if (!strcasecmp(str, "HITACHI_AC3"))
return decode_type_t::HITACHI_AC3;
else if (!strcasecmp(str, "HITACHI_AC424"))
return decode_type_t::HITACHI_AC424;
else if (!strcasecmp(str, "INAX"))
@@ -347,6 +349,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
case HITACHI_AC2:
result = F("HITACHI_AC2");
break;
case HITACHI_AC3:
result = F("HITACHI_AC3");
break;
case HITACHI_AC424:
result = F("HITACHI_AC424");
break;
@@ -530,6 +535,7 @@ bool hasACState(const decode_type_t protocol) {
case HITACHI_AC:
case HITACHI_AC1:
case HITACHI_AC2:
case HITACHI_AC3:
case HITACHI_AC424:
case KELVINATOR:
case MITSUBISHI136:

View File

@@ -37,6 +37,14 @@ const uint16_t kHitachiAc424BitMark = 463;
const uint16_t kHitachiAc424OneSpace = 1208;
const uint16_t kHitachiAc424ZeroSpace = 372;
// Support for HitachiAc3 protocol
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
const uint16_t kHitachiAc3HdrMark = 3400; // Header
const uint16_t kHitachiAc3HdrSpace = 1660; // Header
const uint16_t kHitachiAc3BitMark = 460;
const uint16_t kHitachiAc3OneSpace = 1250;
const uint16_t kHitachiAc3ZeroSpace = 410;
using irutils::addBoolToString;
using irutils::addIntToString;
using irutils::addLabeledString;
@@ -800,3 +808,142 @@ String IRHitachiAc424::toString(void) {
result += ')';
return result;
}
#if SEND_HITACHI_AC3
// Send HITACHI_AC3 messages
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences.
// There are five(5) typical sizes:
// * kHitachiAc3MinStateLength (Cancel Timer)
// * kHitachiAc3MinStateLength + 2 (Change Temp)
// * kHitachiAc3StateLength - 6 (Change Mode)
// * kHitachiAc3StateLength- 4 (Normal)
// * kHitachiAc3StateLength (Set Timer)
//
// Args:
// data: An array of bytes containing the IR command.
// It is assumed to be in LSBF order for this code.
// nbytes: Nr. of bytes of data in the array.
// repeat: Nr. of times the message is to be repeated.
//
// Status: BETA / Probably working fine.
void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
// Header + Data + Footer
sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
kHitachiAc3BitMark, kHitachiAc3OneSpace,
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
kHitachiAc3BitMark, kHitachiAcMinGap,
data, nbytes, // Bytes
kHitachiAcFreq, false, repeat, kDutyDefault);
}
#endif // SEND_HITACHI_AC3
// Class for handling the remote control on a Hitachi_AC3 53 A/C message
IRHitachiAc3::IRHitachiAc3(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
// Reset to auto fan, cooling, 23° Celcius
void IRHitachiAc3::stateReset(void) {
for (uint8_t i = 0; i < kHitachiAc3StateLength; i++)
remote_state[i] = 0x00;
remote_state[0] = 0x01;
remote_state[1] = 0x10;
remote_state[3] = 0x40;
remote_state[5] = 0xFF;
remote_state[7] = 0xE8;
remote_state[9] = 0x89;
remote_state[11] = 0x0B;
remote_state[13] = 0x3F;
remote_state[15] = 0x15;
remote_state[21] = 0x4B;
remote_state[23] = 0x18;
setInvertedStates();
}
void IRHitachiAc3::setInvertedStates(const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
remote_state[i + 1] = ~remote_state[i];
}
bool IRHitachiAc3::hasInvertedStates(const uint8_t state[],
const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
if ((state[i + 1] ^ state[i]) != 0xFF) return false;
return true;
}
void IRHitachiAc3::begin(void) { _irsend.begin(); }
uint8_t *IRHitachiAc3::getRaw(void) {
setInvertedStates();
return remote_state;
}
void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) {
memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength));
}
#if DECODE_HITACHI_AC3
// Decode the supplied HitachiAc3 A/C message.
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences and multiple lengths.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// offset: The starting index to use when attempting to decode the raw data.
// Typically/Defaults to kStartOffset.
// nbits: The number of data bits to expect. Typically kHitachiAc3Bits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Probably works fine.
//
// Supported devices:
// Hitachi PC-LH3B
//
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset,
const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
return false; // Too short a message to match.
if (strict) {
// Check the requested bit length.
switch (nbits) {
case kHitachiAc3MinBits: // Cancel Timer (Min Size)
case kHitachiAc3MinBits + 2 * 8: // Change Temp
case kHitachiAc3Bits - 6 * 8: // Change Mode
case kHitachiAc3Bits - 4 * 8: // Normal
case kHitachiAc3Bits: // Set Temp (Max Size)
break;
default: return false;
}
}
// Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
kHitachiAc3BitMark, kHitachiAc3OneSpace,
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
kHitachiAc3BitMark, kHitachiAcMinGap, true,
kUseDefTol, 0, false))
return false; // We failed to find any data.
// Compliance
if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8))
return false;
// Success
results->decode_type = decode_type_t::HITACHI_AC3;
results->bits = nbits;
return true;
}
#endif // DECODE_HITACHI_AC3

View File

@@ -1,6 +1,6 @@
// Hitachi A/C
//
// Copyright 2018-2019 David Conran
// Copyright 2018-2020 David Conran
// Supports:
// Brand: Hitachi, Model: RAS-35THA6 remote
@@ -8,6 +8,7 @@
// Brand: Hitachi, Model: Series VI A/C (Circa 2007)
// Brand: Hitachi, Model: RAR-8P2 remote
// Brand: Hitachi, Model: RAS-AJ25H A/C
// Brand: Hitachi, Model: PC-LH3B (HITACHI_AC3)
#ifndef IR_HITACHI_H_
#define IR_HITACHI_H_
@@ -175,4 +176,32 @@ class IRHitachiAc424 {
uint8_t _previoustemp;
};
class IRHitachiAc3 {
public:
explicit IRHitachiAc3(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset(void);
#if SEND_HITACHI_AC3
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_HITACHI_AC3
void begin(void);
uint8_t getMode(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAc3StateLength);
static bool hasInvertedStates(const uint8_t state[], const uint16_t length);
#ifndef UNIT_TEST
private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// The state of the IR remote in IR code form.
uint8_t remote_state[kHitachiAc3StateLength];
void setInvertedStates(const uint16_t length = kHitachiAc3StateLength);
};
#endif // IR_HITACHI_H_

View File

@@ -816,6 +816,11 @@ TEST(TestUtils, Housekeeping) {
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC2));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC2));
ASSERT_EQ("HITACHI_AC3", typeToString(decode_type_t::HITACHI_AC3));
ASSERT_EQ(decode_type_t::HITACHI_AC3, strToDecodeType("HITACHI_AC3"));
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC3));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC3));
ASSERT_EQ("HITACHI_AC424", typeToString(decode_type_t::HITACHI_AC424));
ASSERT_EQ(decode_type_t::HITACHI_AC424, strToDecodeType("HITACHI_AC424"));
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC424));
@@ -1113,7 +1118,7 @@ TEST(TestIRHitachiAc424Class, HumanReadable) {
}
TEST(TestIRHitachiAc424Class, toCommon) {
IRHitachiAc424 ac(0);
IRHitachiAc424 ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kHitachiAc424Cool);
ac.setTemp(20);
@@ -1139,3 +1144,365 @@ TEST(TestIRHitachiAc424Class, toCommon) {
ASSERT_EQ(-1, ac.toCommon().sleep);
ASSERT_EQ(-1, ac.toCommon().clock);
}
TEST(TestDecodeHitachiAc3, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
uint8_t expected[kHitachiAc3StateLength - 4] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01,
0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F};
irsend.reset();
irsend.sendHitachiAc3(expected, kHitachiAc3StateLength - 4);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc3Bits - 32, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
// Decode a 'real' HitachiAc3 message.
TEST(TestDecodeHitachiAc3, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
uint8_t expected[kHitachiAc3StateLength - 4] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01,
0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F};
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-597519432
uint16_t rawData[371] = {
// Power Off
3422, 1660, 464, 1264, 438, 426, 438, 402, 486, 426, 440, 402, 462, 402,
488, 428, 438, 402, 460, 404, 488, 426, 440, 424, 436, 428, 462, 1240,
466, 398, 462, 404, 486, 426, 438, 402, 464, 400, 490, 400, 464, 426, 438,
402, 488, 428, 436, 428, 462, 376, 486, 404, 474, 416, 438, 400, 488, 402,
462, 426, 438, 426, 458, 1246, 464, 426, 436, 1244, 488, 1266, 434, 1268,
436, 1242, 486, 1242, 462, 1240, 464, 426, 462, 1266, 438, 1242, 462,
1240, 514, 1214, 466, 1236, 462, 1266, 464, 1264, 438, 1266, 438, 1240,
488, 400, 466, 424, 440, 402, 486, 404, 462, 402, 464, 402, 488, 426, 438,
402, 462, 402, 488, 1264, 438, 1242, 460, 428, 462, 428, 436, 1264, 440,
1240, 488, 1240, 464, 1240, 460, 402, 490, 424, 440, 1264, 438, 1242, 512,
402, 438, 426, 466, 398, 464, 1266, 440, 400, 462, 402, 488, 1264, 438,
402, 462, 402, 482, 432, 438, 1264, 436, 404, 488, 1240, 462, 1264, 438,
402, 486, 1246, 456, 1266, 438, 1238, 488, 402, 462, 1240, 462, 402, 512,
402, 438, 428, 438, 426, 462, 430, 434, 428, 438, 402, 512, 376, 462,
1240, 464, 1264, 458, 1270, 436, 1242, 462, 1240, 486, 1242, 460, 1242,
462, 1242, 486, 1240, 464, 1240, 464, 1238, 492, 1264, 436, 1266, 440,
400, 488, 426, 436, 430, 434, 430, 460, 404, 462, 426, 438, 402, 488, 426,
436, 1264, 466, 1238, 462, 1242, 462, 1266, 438, 1238, 490, 1264, 438,
426, 438, 1240, 486, 406, 462, 402, 462, 400, 488, 428, 436, 426, 438,
402, 484, 1268, 438, 426, 436, 1242, 488, 1266, 436, 402, 462, 402, 502,
386, 464, 1240, 464, 1240, 488, 426, 438, 426, 438, 402, 488, 1240, 462,
1266, 438, 1242, 486, 428, 440, 424, 438, 1266, 460, 1268, 438, 1264, 438,
404, 486, 428, 438, 426, 438, 402, 490, 400, 462, 426, 436, 402, 490, 426,
438, 1240, 462, 1268, 460, 1268, 434, 1266, 436, 1240, 514, 1238, 438,
1240, 488, 1240, 460, 406, 464, 426, 436, 402, 488, 402, 462, 402, 462,
1264, 462, 404, 462, 1266, 434, 1268, 464, 1264, 438, 1242, 464, 1238,
488, 1266, 438, 402, 462, 1268, 458, 430, 410};
irsend.reset();
irsend.sendRaw(rawData, 371, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc3Bits - 32, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
TEST(TestDecodeHitachiAc3, SyntheticTempChangeExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint16_t expectedLength = kHitachiAc3MinStateLength + 2;
uint8_t expected[expectedLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE3, 0x1C, 0x89, 0x76, 0x08,
0xF7, 0x3F, 0xC0, 0x15, 0xEA};
irsend.reset();
irsend.sendHitachiAc3(expected, expectedLength);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedLength * 8, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
// Decode a 'real' Temp Change HitachiAc3 message.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-597592537
TEST(TestDecodeHitachiAc3, RealTempChangeExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3MinStateLength + 2] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE3, 0x1C, 0x89, 0x76, 0x08,
0xF7, 0x3F, 0xC0, 0x15, 0xEA};
const uint16_t expectedBits = kHitachiAc3MinBits + 2 * 8;
const uint16_t rawData[275] = {
// Change Temp
3422, 1636, 490, 1242, 484, 380, 488, 400, 466, 426, 432, 406, 490, 400,
464, 426, 462, 398, 466, 376, 492, 400, 458, 404, 490, 400, 466, 1262,
434, 430, 464, 400, 490, 400, 458, 380, 490, 374, 490, 426, 432, 430, 464,
400, 464, 426, 460, 402, 468, 372, 490, 400, 460, 404, 490, 374, 490, 400,
484, 378, 490, 398, 464, 1240, 490, 398, 466, 1238, 464, 1266, 434, 1268,
466, 1238, 466, 1240, 484, 1218, 490, 400, 460, 1246, 484, 1242, 466,
1214, 488, 1240, 486, 1218, 490, 1214, 488, 1266, 434, 1242, 490, 1214,
490, 424, 460, 404, 464, 374, 492, 424, 434, 430, 466, 400, 464, 426, 464,
376, 492, 1236, 464, 1240, 488, 402, 466, 374, 490, 400, 458, 1244, 490,
1238, 496, 1234, 436, 404, 488, 400, 464, 1240, 484, 1244, 464, 1238, 466,
426, 434, 430, 466, 400, 464, 1264, 434, 406, 490, 374, 490, 1264, 458,
404, 466, 400, 466, 424, 466, 1238, 464, 400, 466, 1238, 486, 1216, 490,
400, 464, 1240, 486, 1240, 466, 1238, 460, 432, 432, 430, 468, 374, 488,
426, 462, 1242, 464, 400, 466, 426, 434, 404, 490, 374, 490, 1240, 484,
1244, 466, 1240, 462, 428, 436, 1242, 490, 1212, 490, 1264, 466, 1236,
466, 1214, 490, 1240, 488, 1240, 466, 1236, 466, 1264, 434, 1268, 464,
400, 494, 400, 430, 406, 488, 376, 488, 428, 432, 432, 462, 402, 462, 426,
458, 1220, 488, 1214, 490, 1240, 484, 406, 466, 1212, 490, 426, 462, 1216,
488, 400, 464, 402, 488, 402, 462, 374, 492, 1262, 460, 380, 488, 1240,
464, 428, 462, 1214, 492, 1236, 462, 1216, 490};
irsend.reset();
irsend.sendRaw(rawData, 275, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits);
}
// Decode a 'real' Cancel Timer HitachiAc3 message.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576
TEST(TestDecodeHitachiAc3, RealCancelTimerExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3MinStateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE2, 0x1D, 0x89, 0x76, 0x0D,
0xF2, 0x3F, 0xC0};
const uint16_t expectedBits = kHitachiAc3MinBits;
const uint16_t rawData[243] = {
// Cancel Timer
3368, 1688, 460, 1244, 456, 434, 458, 406, 456, 408, 480, 410, 458, 406,
458, 406, 484, 406, 458, 406, 458, 432, 428, 464, 456, 380, 458, 1242,
458, 432, 456, 406, 486, 378, 430, 458, 456, 408, 458, 406, 456, 434, 456,
410, 454, 408, 454, 434, 460, 404, 458, 406, 454, 436, 456, 408, 458, 406,
458, 432, 456, 434, 430, 1246, 484, 404, 484, 1218, 458, 1244, 484, 1244,
460, 1244, 456, 1270, 430, 1274, 460, 404, 484, 1218, 486, 1244, 456,
1244, 456, 1248, 454, 1272, 460, 1268, 432, 1246, 484, 1244, 458, 1244,
462, 404, 456, 434, 458, 432, 432, 408, 456, 434, 460, 404, 460, 404, 484,
406, 432, 432, 458, 1298, 402, 432, 460, 404, 460, 404, 482, 1244, 460,
1270, 462, 1214, 456, 1272, 460, 404, 456, 1246, 486, 1244, 458, 1244,
458, 406, 454, 460, 434, 404, 460, 1268, 430, 460, 432, 406, 458, 1244,
456, 434, 458, 406, 456, 432, 460, 1268, 432, 406, 458, 1244, 484, 1272,
432, 430, 432, 1298, 378, 1298, 458, 1244, 484, 406, 460, 1242, 458, 406,
458, 1246, 456, 1272, 458, 406, 458, 432, 458, 404, 460, 404, 456, 408,
484, 1272, 432, 432, 432, 406, 454, 1272, 458, 1246, 458, 1244, 484, 1268,
436, 1242, 458, 1298, 402, 1274, 456, 1244, 458, 1244, 486, 1242, 458,
408, 486, 378, 454, 460, 434, 406, 458, 406, 484, 406, 458, 406, 458, 408,
452, 1302, 434, 1244, 430};
irsend.reset();
irsend.sendRaw(rawData, 243, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits);
}
TEST(TestDecodeHitachiAc3, SyntheticCancelTimerExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3MinStateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE2, 0x1D, 0x89, 0x76, 0x0D,
0xF2, 0x3F, 0xC0};
irsend.reset();
irsend.sendHitachiAc3(expected, kHitachiAc3MinStateLength);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc3MinBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
// Decode a 'real' Set Timer HitachiAc3 message.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576
TEST(TestDecodeHitachiAc3, RealSetTimerExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B,
0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18,
0xE7, 0x00, 0xFF};
const uint16_t expectedBits = kHitachiAc3Bits;
const uint16_t rawData[435] = {
// Set Timer
3364, 1668, 486, 1244, 458, 406, 456, 436, 430, 434, 430, 434, 456, 408,
484, 432, 428, 432, 414, 450, 458, 432, 432, 432, 430, 436, 458, 1270,
430, 408, 456, 408, 454, 462, 404, 460, 428, 408, 486, 404, 456, 408, 458,
406, 484, 406, 456, 434, 432, 408, 484, 406, 454, 436, 404, 432, 486, 430,
430, 408, 456, 432, 458, 1270, 406, 434, 456, 1246, 484, 1244, 456, 1274,
432, 1244, 486, 1268, 432, 1244, 454, 412, 454, 1272, 430, 1272, 456,
1274, 456, 1246, 456, 1244, 458, 1270, 458, 1246, 456, 1254, 450, 1246,
484, 408, 456, 434, 430, 408, 486, 430, 404, 460, 430, 408, 486, 404, 458,
406, 458, 406, 486, 404, 458, 432, 430, 1272, 456, 408, 458, 1244, 458,
1244, 456, 1274, 456, 1246, 456, 1270, 460, 1250, 426, 460, 404, 1270,
484, 432, 404, 462, 402, 460, 458, 1244, 456, 386, 452, 460, 458, 1244,
458, 406, 458, 406, 486, 430, 416, 1260, 458, 406, 484, 1270, 406, 1296,
430, 410, 482, 1246, 456, 1246, 456, 1246, 456, 434, 458, 1244, 456, 1272,
458, 408, 458, 1244, 456, 410, 484, 404, 454, 410, 456, 408, 484, 406,
458, 432, 430, 1246, 484, 406, 454, 1248, 458, 1244, 486, 1248, 428, 1270,
456, 1266, 464, 1244, 458, 1246, 456, 1246, 484, 1270, 430, 1246, 456,
434, 430, 460, 406, 432, 430, 432, 484, 432, 406, 432, 458, 406, 484, 432,
432, 1266, 434, 1274, 430, 1298, 428, 408, 456, 1246, 486, 430, 432, 1270,
404, 434, 484, 432, 430, 408, 456, 406, 460, 1268, 458, 408, 456, 1246,
486, 406, 456, 1244, 456, 1248, 456, 1300, 430, 408, 456, 408, 484, 432,
430, 434, 428, 434, 460, 430, 404, 434, 458, 406, 486, 1242, 458, 1270,
430, 1246, 484, 1244, 456, 1270, 432, 1248, 484, 1244, 456, 1246, 456,
408, 486, 404, 456, 408, 456, 432, 462, 430, 406, 458, 432, 432, 428,
436, 456, 1246, 456, 1246, 484, 1244, 454, 1268, 438, 1244, 488, 1266,
430, 1246, 458, 1244, 486, 1244, 430, 1298, 430, 434, 460, 1242, 458, 430,
430, 408, 486, 1242, 456, 434, 404, 462, 456, 430, 432, 1246, 456, 408,
486, 1270, 430, 1246, 460, 404, 482, 1246, 458, 406, 456, 408, 484, 404,
458, 1270, 430, 1274, 458, 432, 430, 408, 456, 406, 486, 1244, 458, 1242,
458, 1246, 484, 406, 458, 432, 430, 1246, 486, 1242, 456, 1272, 430, 434,
458, 406, 458, 406, 458, 406, 486, 404, 458, 406, 458, 430, 430, 460, 430,
1270, 430, 1248, 484, 1244, 456, 1244, 458, 1246, 484, 1244, 456, 1246,
460, 1244, 458}; // UNKNOWN D3A5A0BA
irsend.reset();
irsend.sendRaw(rawData, 435, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits);
}
TEST(TestDecodeHitachiAc3, SyntheticSetTimerExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B,
0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18,
0xE7, 0x00, 0xFF};
irsend.reset();
irsend.sendHitachiAc3(expected, kHitachiAc3StateLength);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc3Bits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
// Decode a 'real' Change Mode HitachiAc3 message.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-598631576
TEST(TestDecodeHitachiAc3, RealChangeModeExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3StateLength - 6] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE5, 0x1A, 0x89, 0x76, 0x04,
0xFB, 0x3F, 0xC0, 0x1B, 0xE4, 0x14, 0xEB, 0x02, 0xFD};
const uint16_t expectedBits = kHitachiAc3Bits - 6 * 8;
const uint16_t rawData[339] = {
// Change Mode
3364, 1666, 488, 1268, 404, 434, 454, 434, 460, 404, 458, 406, 458, 406,
484, 432, 432, 430, 432, 408, 484, 406, 458, 432, 432, 406, 486, 1242,
458, 430, 430, 408, 454, 436, 456, 408, 458, 406, 484, 432, 428, 434, 430,
408, 484, 406, 458, 430, 432, 408, 484, 430, 430, 432, 432, 408, 482, 408,
456, 408, 458, 432, 458, 1246, 458, 430, 430, 1246, 486, 1242, 458, 1270,
432, 1246, 486, 1242, 458, 1268, 432, 434, 430, 1272, 456, 1246, 458,
1270, 460, 1244, 458, 1246, 456, 1272, 460, 1242, 456, 1270, 434, 1246,
486, 430, 430, 434, 432, 406, 484, 430, 432, 432, 430, 408, 484, 432, 432,
432, 430, 1246, 484, 406, 458, 1246, 456, 408, 486, 404, 458, 1268, 434,
1270, 430, 1274, 456, 408, 458, 1270, 460, 404, 458, 1244, 458, 1246, 486,
430, 430, 432, 460, 378, 488, 1240, 460, 404, 458, 408, 486, 1242, 458,
432, 430, 434, 460, 430, 432, 1246, 458, 406, 488, 1240, 430, 1272, 458,
406, 486, 1242, 430, 1298, 430, 1274, 428, 432, 430, 436, 456, 408, 482,
1248, 458, 406, 430, 462, 456, 404, 458, 432, 430, 408, 486, 1266, 436,
1242, 458, 406, 486, 1242, 456, 1246, 458, 1244, 488, 1240, 458, 1244,
458, 1272, 460, 1242, 456, 1246, 458, 1246, 486, 1242, 458, 1270, 432,
406, 458, 458, 434, 430, 432, 406, 486, 406, 456, 408, 458, 432, 484, 406,
430, 1272, 460, 1216, 486, 1242, 456, 1246, 458, 406, 486, 1268, 432,
1244, 458, 406, 486, 404, 460, 432, 430, 406, 488, 402, 458, 1272, 428,
434, 460, 404, 460, 1242, 458, 1246, 480, 1244, 462, 428, 432, 432, 460,
1244, 458, 432, 430, 1246, 488, 402, 456, 408, 458, 406, 486, 1268, 432,
1246, 460, 430, 460, 1244, 458, 406, 458, 1244, 486, 1242, 458, 1244, 458,
432, 462, 1242, 456, 408, 456, 406, 486, 428, 434, 406, 458, 406, 456,
434, 458, 1244, 460, 430, 462, 1240, 458, 1244, 460, 1244, 486, 1244, 458,
1242, 488, 1214, 460}; // UNKNOWN C1EA1036
irsend.reset();
irsend.sendRaw(rawData, 339, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, expectedBits);
}
TEST(TestDecodeHitachiAc3, SyntheticChangeModeExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
const uint8_t expected[kHitachiAc3StateLength - 6] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE5, 0x1A, 0x89, 0x76, 0x04,
0xFB, 0x3F, 0xC0, 0x1B, 0xE4, 0x14, 0xEB, 0x02, 0xFD};
const uint16_t expectedBits = kHitachiAc3Bits - 6 * 8;
irsend.reset();
irsend.sendHitachiAc3(expected, kHitachiAc3StateLength - 6);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC3, irsend.capture.decode_type);
ASSERT_EQ(expectedBits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}
TEST(TestHitachiAc3Class, hasInvertedStates) {
const uint8_t good_state[kHitachiAc3StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B,
0xF4, 0x3F, 0xC0, 0x15, 0xEA, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18,
0xE7, 0x00, 0xFF};
// bad_state[kHitachiAc3MinStateLength + 1] has been modified to be different.
// i.e. Anything larger than kHitachiAc3MinStateLength should fail.
// kHitachiAc3MinStateLength or shorter should pass.
const uint8_t bad_state[kHitachiAc3StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE8, 0x17, 0x89, 0x76, 0x0B,
0xF4, 0x3F, 0xC0, 0x15, 0xE0, 0x00, 0xFF, 0x00, 0xFF, 0x4B, 0xB4, 0x18,
0xE7, 0x00, 0xFF};
EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(good_state,
kHitachiAc3StateLength));
for (uint8_t len = kHitachiAc3StateLength;
len > kHitachiAc3MinStateLength;
len -= 2) {
EXPECT_FALSE(IRHitachiAc3::hasInvertedStates(bad_state, len));
}
EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(bad_state,
kHitachiAc3MinStateLength));
EXPECT_TRUE(IRHitachiAc3::hasInvertedStates(bad_state,
kHitachiAc3MinStateLength - 2));
}