Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HAIER_AC160: Add basic support for Haier 160bit protocol. #1805

Merged
merged 1 commit into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
}