Skip to content

Commit

Permalink
HAIER_AC160: Add basic support for Haier 160bit protocol. (#1805)
Browse files Browse the repository at this point in the history
* Add `sendHaierAC160()` & `decodeHaierAC160()` routines
* Update supported models.
* Add & update unit tests to cover new code.
* Fix some missing precompiler logic.

For #1804
  • Loading branch information
crankyoldgit committed May 17, 2022
1 parent 3c6b057 commit b185e32
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 8 deletions.
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Daikin 200-bit decode");
if (decodeDaikin200(results, offset)) return true;
#endif // DECODE_DAIKIN200
#if DECODE_HAIER_AC160
DPRINTLN("Attempting Haier AC 160 bit decode");
if (decodeHaierAC160(results, offset)) return true;
#endif // DECODE_HAIER_AC160
// Typically new protocols are added above this line.
}
#if DECODE_HASH
Expand Down
9 changes: 8 additions & 1 deletion src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,8 @@ class IRrecv {
const uint16_t nbits = kGreeBits,
const bool strict = true);
#endif
#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02)
#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \
DECODE_HAIER_AC176)
bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kHaierACBits,
const bool strict = true);
Expand All @@ -596,6 +597,12 @@ class IRrecv {
const uint16_t nbits = kHaierACYRW02Bits,
const bool strict = true);
#endif
#if DECODE_HAIER_AC160
bool decodeHaierAC160(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kHaierAC160Bits,
const bool strict = true);
#endif // DECODE_HAIER_AC160
#if DECODE_HAIER_AC176
bool decodeHaierAC176(decode_results *results,
uint16_t offset = kStartOffset,
Expand Down
15 changes: 13 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,13 @@
#define SEND_DAIKIN200 _IR_ENABLE_DEFAULT_
#endif // SEND_DAIKIN200

#ifndef DECODE_HAIER_AC160
#define DECODE_HAIER_AC160 _IR_ENABLE_DEFAULT_
#endif // DECODE_HAIER_AC160
#ifndef SEND_HAIER_AC160
#define SEND_HAIER_AC160 _IR_ENABLE_DEFAULT_
#endif // SEND_HAIER_AC160

#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
Expand All @@ -884,7 +891,7 @@
DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \
DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \
DECODE_KELON168 || DECODE_HITACHI_AC296 || \
DECODE_DAIKIN200 || \
DECODE_DAIKIN200 || SEND_HAIER_AC160 || \
false)
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
// you might also want to add the protocol to hasACState function
Expand Down Expand Up @@ -1039,8 +1046,9 @@ enum decode_type_t {
KELON168,
HITACHI_AC296,
DAIKIN200,
HAIER_AC160, // 115
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = DAIKIN200,
kLastDecodeType = HAIER_AC160,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -1139,6 +1147,9 @@ const uint16_t kHaierAcDefaultRepeat = kNoRepeat;
const uint16_t kHaierACYRW02StateLength = 14;
const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8;
const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat;
const uint16_t kHaierAC160StateLength = 20;
const uint16_t kHaierAC160Bits = kHaierAC160StateLength * 8;
const uint16_t kHaierAc160DefaultRepeat = kNoRepeat;
const uint16_t kHaierAC176StateLength = 22;
const uint16_t kHaierAC176Bits = kHaierAC176StateLength * 8;
const uint16_t kHaierAc176DefaultRepeat = kNoRepeat;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kHaierACBits;
case HAIER_AC_YRW02:
return kHaierACYRW02Bits;
case HAIER_AC160:
return kHaierAC160Bits;
case HAIER_AC176:
return kHaierAC176Bits;
case HITACHI_AC:
Expand Down Expand Up @@ -1198,6 +1200,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendHaierACYRW02(state, nbytes);
break;
#endif // SEND_HAIER_AC_YRW02
#if SEND_HAIER_AC160
case HAIER_AC160:
sendHaierAC160(state, nbytes);
break;
#endif // SEND_HAIER_AC160
#if SEND_HAIER_AC176
case HAIER_AC176:
sendHaierAC176(state, nbytes);
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,11 @@ class IRsend {
const uint16_t nbytes = kHaierACYRW02StateLength,
const uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif // SEND_HAIER_AC_YRW02
#if SEND_HAIER_AC160
void sendHaierAC160(const unsigned char data[],
const uint16_t nbytes = kHaierAC160StateLength,
const uint16_t repeat = kHaierAc160DefaultRepeat);
#endif // SEND_HAIER_AC160
#if SEND_HAIER_AC176
void sendHaierAC176(const unsigned char data[],
const uint16_t nbytes = kHaierAC176StateLength,
Expand Down
1 change: 1 addition & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
D_STR_KELON168 "\x0"
D_STR_HITACHI_AC296 "\x0"
D_STR_DAIKIN200 "\x0"
D_STR_HAIER_AC160 "\x0"
///< New protocol strings should be added just above this line.
"\x0" ///< This string requires double null termination.
};
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ bool hasACState(const decode_type_t protocol) {
case GREE:
case HAIER_AC:
case HAIER_AC_YRW02:
case HAIER_AC160:
case HAIER_AC176:
case HITACHI_AC:
case HITACHI_AC1:
Expand Down
52 changes: 49 additions & 3 deletions src/ir_Haier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ using irutils::minsToString;
_.x##Mins = mins % 60;\
} while (0)

#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176)
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC160 || \
SEND_HAIER_AC176)
/// Send a Haier A/C formatted message. (HSU07-HEA03 remote)
/// Status: STABLE / Known to be working.
/// @param[in] data The message to be sent.
Expand Down Expand Up @@ -93,6 +94,18 @@ void IRsend::sendHaierAC176(const unsigned char data[], const uint16_t nbytes,
}
#endif // SEND_HAIER_AC176

#if SEND_HAIER_AC160
/// Send a Haier 160 bit remote A/C formatted message.
/// Status: STABLE / Known to be working.
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendHaierAC160(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes >= kHaierAC160StateLength) sendHaierAC(data, nbytes, repeat);
}
#endif // SEND_HAIER_AC160

/// Class constructor
/// @param[in] pin GPIO to be used when sending.
/// @param[in] inverted Is the output signal to be inverted?
Expand Down Expand Up @@ -568,7 +581,7 @@ void IRHaierAC176::checksum(void) {
/// @return true, if the state has a valid checksum. Otherwise, false.
bool IRHaierAC176::validChecksum(const uint8_t state[], const uint16_t length) {
if (length < 2) return false; // 1 byte of data can't have a checksum.
if (length < kHaierAC176StateLength) { // Is it too short?
if (length < kHaierAC160StateLength) { // Is it too short?
// Then it is just a checksum of the whole thing.
return (state[length - 1] == sumBytes(state, length - 1));
} else { // It is long enough for two checksums.
Expand Down Expand Up @@ -1323,7 +1336,8 @@ bool IRHaierACYRW02::validChecksum(const uint8_t state[],
}
// End of IRHaierACYRW02 class.

#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02)
#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \
DECODE_HAIER_AC176)
/// Decode the supplied Haier HSU07-HEA03 remote message.
/// Status: STABLE / Known to be working.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
Expand Down Expand Up @@ -1435,3 +1449,35 @@ bool IRrecv::decodeHaierAC176(decode_results* results, uint16_t offset,
return true;
}
#endif // DECODE_HAIER_AC176

#if DECODE_HAIER_AC160
/// Decode the supplied Haier 160 bit remote A/C message.
/// Status: STABLE / Known to be working.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// result.
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
/// @param[in] nbits The number of data bits to expect.
/// @param[in] strict Flag indicating if we should perform strict matching.
/// @return A boolean. True if it can decode it, false if it can't.
bool IRrecv::decodeHaierAC160(decode_results* results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict) {
if (nbits != kHaierAC160Bits)
return false; // Not strictly a HAIER_AC160 message.
}

// The protocol is almost exactly the same as HAIER_AC
if (!decodeHaierAC(results, offset, nbits, false)) return false;

// Compliance
if (strict) {
if (!IRHaierAC176::validChecksum(results->state, nbits / 8)) return false;
}

// Success
// It looks correct, but we haven't check the checksum etc.
results->decode_type = HAIER_AC160;
return true;
}
#endif // DECODE_HAIER_AC160
2 changes: 2 additions & 0 deletions src/ir_Haier.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
/// @see https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/485
/// @see https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1804

// Supports:
// Brand: Haier, Model: HSU07-HEA03 remote (HAIER_AC)
Expand All @@ -17,6 +18,7 @@
// Brand: Mabe, Model: MMI18HDBWCA6MI8 A/C (HAIER_AC176)
// Brand: Mabe, Model: V12843 HJ200223 remote (HAIER_AC176)
// Brand: Daichi, Model: D-H A/C (HAIER_AC176)
// Brand: Haier, Model: KFR-26GW/83@UI-Ge A/C (HAIER_AC160)

#ifndef IR_HAIER_H_
#define IR_HAIER_H_
Expand Down
7 changes: 5 additions & 2 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -797,10 +797,13 @@ D_STR_INDIRECT " " D_STR_MODE
#define D_STR_HAIER_AC "HAIER_AC"
#endif // D_STR_HAIER_AC
#ifndef D_STR_HAIER_AC_YRW02
#define D_STR_HAIER_AC_YRW02 "HAIER_AC_YRW02"
#define D_STR_HAIER_AC_YRW02 D_STR_HAIER_AC "_YRW02"
#endif // D_STR_HAIER_AC_YRW02
#ifndef D_STR_HAIER_AC160
#define D_STR_HAIER_AC160 D_STR_HAIER_AC "160"
#endif // D_STR_HAIER_AC160
#ifndef D_STR_HAIER_AC176
#define D_STR_HAIER_AC176 "HAIER_AC176"
#define D_STR_HAIER_AC176 D_STR_HAIER_AC "176"
#endif // D_STR_HAIER_AC176
#ifndef D_STR_HITACHI_AC
#define D_STR_HITACHI_AC "HITACHI_AC"
Expand Down
83 changes: 83 additions & 0 deletions test/ir_Haier_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,13 @@ TEST(TestUtils, Housekeeping) {
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::HAIER_AC176));
ASSERT_EQ(kHaierAC176Bits, IRsend::defaultBits(decode_type_t::HAIER_AC176));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HAIER_AC176));

ASSERT_EQ("HAIER_AC160", typeToString(decode_type_t::HAIER_AC160));
ASSERT_EQ(decode_type_t::HAIER_AC160, strToDecodeType("HAIER_AC160"));
ASSERT_TRUE(hasACState(decode_type_t::HAIER_AC160));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HAIER_AC160));
ASSERT_EQ(kHaierAC160Bits, IRsend::defaultBits(decode_type_t::HAIER_AC160));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::HAIER_AC160));
}

TEST(TestHaierAC176Class, BuildKnownState) {
Expand Down Expand Up @@ -1535,3 +1542,79 @@ TEST(TestHaierAC176Class, Models) {
"Timer Mode: 0 (N/A), On Timer: Off, Off Timer: Off, Lock: Off",
ac.toString());
}

TEST(TestDecodeHaierAC160, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();

irsend.reset();
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1804#issue-1236115063
const uint16_t rawData[325] = {
3078, 3002,
3058, 4338,
590, 1612, 588, 516, 584, 1588, 586, 542, 560, 540, 560, 1590, 584, 1618,
584, 542, 558, 1592, 584, 542, 582, 1594, 584, 542, 582, 1592, 560, 1642,
560, 516, 558, 544, 558, 542, 558, 542, 582, 518, 558, 542, 558, 516, 558,
542, 558, 542, 582, 520, 558, 542, 558, 542, 564, 510, 608, 492, 558, 542,
582, 520, 558, 542, 558, 544, 582, 492, 608, 1594, 558, 544, 558, 542,
582, 494, 608, 494, 558, 544, 558, 542, 558, 544, 558, 1616, 582, 1620,
560, 542, 558, 542, 582, 494, 558, 542, 558, 542, 558, 544, 556, 542, 582,
520, 580, 494, 582, 520, 582, 520, 558, 542, 558, 542, 582, 520, 582, 492,
608, 1592, 582, 520, 558, 544, 558, 516, 584, 518, 558, 542, 582, 520,
580, 520, 582, 520, 580, 492, 584, 516, 556, 544, 582, 518, 580, 520, 580,
520, 580, 494, 606, 494, 580, 520, 580, 520, 582, 520, 580, 520, 556, 518,
608, 492, 580, 520, 556, 544, 580, 520, 580, 520, 580, 494, 606, 494, 580,
520, 580, 520, 580, 520, 580, 520, 580, 494, 606, 496, 578, 522, 580, 520,
580, 520, 580, 520, 580, 494, 606, 494, 580, 520, 580, 520, 580, 1594,
608, 494, 580, 1620, 580, 522, 580, 520, 580, 494, 578, 1620, 582, 520,
580, 1596, 582, 1620, 582, 1594, 608, 1594, 582, 522, 580, 1594, 582,
1620, 582, 520, 580, 1596, 582, 520, 580, 1620, 582, 494, 606, 496, 580,
522, 580, 522, 580, 520, 580, 520, 580, 496, 604, 496, 580, 520, 580,
1620, 582, 1594, 582, 522, 578, 522, 580, 522, 578, 522, 580, 496, 580,
522, 578, 522, 578, 522, 578, 524, 578, 522, 578, 496, 578, 522, 578, 522,
578, 546, 554, 522, 578, 522, 578, 520, 556, 546, 554, 546, 554, 546, 556,
544, 556, 546, 554, 520, 554, 544, 556, 1620, 582, 544, 556, 1594, 580,
546, 556, 1594, 580}; // UNKNOWN B6B57D85
const uint8_t expectedState[kHaierAC160StateLength] = {
0xA6, 0xAC, 0x00, 0x00, 0x40, 0x60, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x05, 0x17, 0xB5, 0x00, 0x60, 0x00, 0x00, 0x15};

irsend.sendRaw(rawData, 325, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HAIER_AC160, irsend.capture.decode_type);
ASSERT_EQ(kHaierAC160Bits, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t result, prev;
ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
}

// Decoding a message we entirely constructed based solely on a given state.
TEST(TestDecodeHaierAC160, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();

const uint8_t expectedState[kHaierAC160StateLength] = {
0xA6, 0xAC, 0x00, 0x00, 0x40, 0x60, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x05, 0x17, 0xB5, 0x00, 0x60, 0x00, 0x00, 0x15};

irsend.reset();
irsend.sendHaierAC160(expectedState);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HAIER_AC160, irsend.capture.decode_type);
ASSERT_EQ(kHaierAC160Bits, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t result, prev;
ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
}

0 comments on commit b185e32

Please sign in to comment.