From ba26c7282baa356e9e559db6ffee40f6c06d02b9 Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Wed, 22 Jun 2022 22:02:12 +1000 Subject: [PATCH 1/2] Experimental basic support for Sanyo AC 152 bit protocol. * LSBF order determined by sequencing a temperature range. * add `sendSanyoAc152()` & `decodeSanyoAc152()` routines * Fix some comments. * Unit test coverage added for new protocol. * Update supported devices info. For #1826 --- src/IRrecv.cpp | 4 +++ src/IRrecv.h | 6 ++++ src/IRremoteESP8266.h | 15 +++++++-- src/IRsend.cpp | 7 ++++ src/IRsend.h | 5 +++ src/IRtext.cpp | 1 + src/IRutils.cpp | 1 + src/ir_Sanyo.cpp | 68 +++++++++++++++++++++++++++++++++++++- src/ir_Sanyo.h | 2 ++ src/locale/defaults.h | 9 +++-- test/ir_Sanyo_test.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 187 insertions(+), 6 deletions(-) diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 1799b6b41..7a3d17efd 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1132,6 +1132,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting TCL AC 96-bit decode"); if (decodeTcl96Ac(results, offset)) return true; #endif // DECODE_TCL96AC +#if DECODE_SANYO_AC152 + DPRINTLN("Attempting Sanyo AC 152-bit decode"); + if (decodeSanyoAc152(results, offset)) return true; +#endif // DECODE_SANYO_AC152 // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index d3d91ee17..6887815d8 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -325,6 +325,12 @@ class IRrecv { const uint16_t nbits = kSanyoAc88Bits, const bool strict = true); #endif // DECODE_SANYO_AC88 +#if DECODE_SANYO_AC152 + bool decodeSanyoAc152(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kSanyoAc152Bits, + const bool strict = true); +#endif // DECODE_SANYO_AC152 #if DECODE_MITSUBISHI bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kMitsubishiBits, diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 73de8c1d0..24259ef9e 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -238,6 +238,13 @@ #define SEND_SANYO_AC88 _IR_ENABLE_DEFAULT_ #endif // SEND_SANYO_AC88 +#ifndef DECODE_SANYO_AC152 +#define DECODE_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // DECODE_SANYO_AC152 +#ifndef SEND_SANYO_AC152 +#define SEND_SANYO_AC152 _IR_ENABLE_DEFAULT_ +#endif // SEND_SANYO_AC152 + #ifndef DECODE_MITSUBISHI #define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_ #endif // DECODE_MITSUBISHI @@ -927,7 +934,7 @@ DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ - DECODE_BOSCH144 || \ + DECODE_BOSCH144 || DECODE_SANYO_AC152 || \ 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 @@ -1088,8 +1095,9 @@ enum decode_type_t { CLIMABUTLER, TCL96AC, BOSCH144, // 120 + SANYO_AC152, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = BOSCH144, + kLastDecodeType = SANYO_AC152, }; // Message lengths & required repeat values @@ -1299,6 +1307,9 @@ const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8; const uint16_t kSanyoAc88StateLength = 11; const uint16_t kSanyoAc88Bits = kSanyoAc88StateLength * 8; const uint16_t kSanyoAc88MinRepeat = 2; +const uint16_t kSanyoAc152StateLength = 19; +const uint16_t kSanyoAc152Bits = kSanyoAc152StateLength * 8; +const uint16_t kSanyoAc152MinRepeat = kNoRepeat; const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index a3c37faf9..e8db0b805 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -768,6 +768,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kSanyoAcBits; case SANYO_AC88: return kSanyoAc88Bits; + case SANYO_AC152: + return kSanyoAc152Bits; case SHARP_AC: return kSharpAcBits; case TCL96AC: @@ -1353,6 +1355,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendSanyoAc88(state, nbytes); break; #endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + case SANYO_AC152: + sendSanyoAc152(state, nbytes); + break; +#endif // SEND_SANYO_AC152 #if SEND_SHARP_AC case SHARP_AC: sendSharpAc(state, nbytes); diff --git a/src/IRsend.h b/src/IRsend.h index 79f1d6688..4e57af0bd 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -346,6 +346,11 @@ class IRsend { const uint16_t nbytes = kSanyoAc88StateLength, const uint16_t repeat = kSanyoAc88MinRepeat); #endif // SEND_SANYO_AC88 +#if SEND_SANYO_AC152 + void sendSanyoAc152(const uint8_t *data, + const uint16_t nbytes = kSanyoAc152StateLength, + const uint16_t repeat = kSanyoAc152MinRepeat); +#endif // SEND_SANYO_AC152 #if SEND_DISH // sendDISH() should typically be called with repeat=3 as DISH devices // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) diff --git a/src/IRtext.cpp b/src/IRtext.cpp index e1b905666..7464af505 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -404,6 +404,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_CLIMABUTLER "\x0" D_STR_TCL96AC "\x0" D_STR_BOSCH144 "\x0" + D_STR_SANYO_AC152 "\x0" ///< New protocol strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/src/IRutils.cpp b/src/IRutils.cpp index bb5e48b4a..bd406179b 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -213,6 +213,7 @@ bool hasACState(const decode_type_t protocol) { case SAMSUNG_AC: case SANYO_AC: case SANYO_AC88: + case SANYO_AC152: case SHARP_AC: case TCL96AC: case TCL112AC: diff --git a/src/ir_Sanyo.cpp b/src/ir_Sanyo.cpp index ab3a4ec93..4a38e8e09 100644 --- a/src/ir_Sanyo.cpp +++ b/src/ir_Sanyo.cpp @@ -77,6 +77,14 @@ const uint32_t kSanyoAc88Gap = 3675; ///< uSeconds const uint16_t kSanyoAc88Freq = 38000; ///< Hz. (Guess only) const uint8_t kSanyoAc88ExtraTolerance = 5; /// (%) Extra tolerance to use. +const uint16_t kSanyoAc152HdrMark = 3300; ///< uSeconds +const uint16_t kSanyoAc152BitMark = 440; ///< uSeconds +const uint16_t kSanyoAc152HdrSpace = 1725; ///< uSeconds +const uint16_t kSanyoAc152OneSpace = 1290; ///< uSeconds +const uint16_t kSanyoAc152ZeroSpace = 405; ///< uSeconds +const uint16_t kSanyoAc152Freq = 38000; ///< Hz. (Guess only) +const uint8_t kSanyoAc152ExtraTolerance = 13; /// (%) Extra tolerance to use. + #if SEND_SANYO /// Construct a Sanyo LC7461 message. /// @param[in] address The 13 bit value of the address(Custom) portion of the @@ -687,7 +695,7 @@ void IRsend::sendSanyoAc88(const uint8_t data[], const uint16_t nbytes, #endif // SEND_SANYO_AC88 #if DECODE_SANYO_AC88 -/// Decode the supplied SanyoAc message. +/// Decode the supplied SanyoAc88 message. /// Status: ALPHA / Untested. /// @param[in,out] results Ptr to the data to decode & where to store the decode /// @warning data's bit order may change. It is not yet confirmed. @@ -976,3 +984,61 @@ String IRSanyoAc88::toString(void) const { result += addLabeledString(minsToString(getClock()), kClockStr); return result; } + +#if SEND_SANYO_AC152 +/// Send a SanyoAc152 formatted message. +/// Status: BETA / Probably works. +/// @param[in] data An array of bytes containing the IR command. +/// @warning data's bit order may change. It is not yet confirmed. +/// @param[in] nbytes Nr. of bytes of data in the array. +/// @param[in] repeat Nr. of times the message is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1826 +void IRsend::sendSanyoAc152(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // (Header + Data + Footer) per repeat + sendGeneric(kSanyoAc152HdrMark, kSanyoAc152HdrSpace, + kSanyoAc152BitMark, kSanyoAc152OneSpace, + kSanyoAc152BitMark, kSanyoAc152ZeroSpace, + kSanyoAc152BitMark, kDefaultMessageGap, + data, nbytes, kSanyoAc152Freq, false, repeat, kDutyDefault); + space(kDefaultMessageGap); // Make a guess at a post message gap. +} +#endif // SEND_SANYO_AC152 + +#if DECODE_SANYO_AC152 +/// Decode the supplied SanyoAc152 message. +/// Status: BETA / Probably works. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @warning data's bit order may change. It is not yet confirmed. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 +bool IRrecv::decodeSanyoAc152(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kSanyoAc152Bits) + return false; + + // Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kSanyoAc152HdrMark, kSanyoAc152HdrSpace, + kSanyoAc152BitMark, kSanyoAc152OneSpace, + kSanyoAc152BitMark, kSanyoAc152ZeroSpace, + kSanyoAc152BitMark, + kDefaultMessageGap, // Just a guess. + false, _tolerance + kSanyoAc152ExtraTolerance, + kMarkExcess, false)) + return false; // No match! + + // Success + results->decode_type = decode_type_t::SANYO_AC152; + results->bits = nbits; + // No need to record the state as we stored it as we decoded it. + // As we use result->state, we don't record value, address, or command as it + // is a union data type. + return true; +} +#endif // DECODE_SANYO_AC152 diff --git a/src/ir_Sanyo.h b/src/ir_Sanyo.h index 66376328f..f92d0ab63 100644 --- a/src/ir_Sanyo.h +++ b/src/ir_Sanyo.h @@ -13,6 +13,7 @@ /// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1503 /// @see https://docs.google.com/spreadsheets/d/1weUmGAsEpfX38gg5rlDN69Uchnbr6gQl9FqHffLBIRk/edit#gid=0 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1826 // Supports: // Brand: Sanyo, Model: SA 8650B - disabled @@ -21,6 +22,7 @@ // Brand: Sanyo, Model: RCS-2HS4E remote (SANYO_AC) // Brand: Sanyo, Model: SAP-K242AH A/C (SANYO_AC) // Brand: Sanyo, Model: RCS-2S4E remote (SANYO_AC) +// Brand: Sanyo, Model: RCS-4MHVPIS4EE remote (SANYO_AC152) #ifndef IR_SANYO_H_ #define IR_SANYO_H_ diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 70f99e69f..2430d6caa 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -980,13 +980,16 @@ D_STR_INDIRECT " " D_STR_MODE #define D_STR_SANYO "SANYO" #endif // D_STR_SANYO #ifndef D_STR_SANYO_AC -#define D_STR_SANYO_AC "SANYO_AC" +#define D_STR_SANYO_AC D_STR_SANYO "_AC" #endif // D_STR_SANYO_AC #ifndef D_STR_SANYO_AC88 -#define D_STR_SANYO_AC88 "SANYO_AC88" +#define D_STR_SANYO_AC88 D_STR_SANYO_AC "88" #endif // D_STR_SANYO_AC88 +#ifndef D_STR_SANYO_AC152 +#define D_STR_SANYO_AC152 D_STR_SANYO_AC "152" +#endif // D_STR_SANYO_AC152 #ifndef D_STR_SANYO_LC7461 -#define D_STR_SANYO_LC7461 "SANYO_LC7461" +#define D_STR_SANYO_LC7461 D_STR_SANYO "_LC7461" #endif // D_STR_SANYO_LC7461 #ifndef D_STR_SHARP #define D_STR_SHARP "SHARP" diff --git a/test/ir_Sanyo_test.cpp b/test/ir_Sanyo_test.cpp index 6378e7679..bcfaef972 100644 --- a/test/ir_Sanyo_test.cpp +++ b/test/ir_Sanyo_test.cpp @@ -284,6 +284,14 @@ TEST(TestUtils, Housekeeping) { ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SANYO_AC88)); ASSERT_EQ(kSanyoAc88Bits, IRsend::defaultBits(decode_type_t::SANYO_AC88)); ASSERT_EQ(kSanyoAc88MinRepeat, IRsend::minRepeats(decode_type_t::SANYO_AC88)); + // Sanyo A/C 152 Bit. + ASSERT_EQ("SANYO_AC152", typeToString(decode_type_t::SANYO_AC152)); + ASSERT_EQ(decode_type_t::SANYO_AC152, strToDecodeType("SANYO_AC152")); + ASSERT_TRUE(hasACState(decode_type_t::SANYO_AC152)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::SANYO_AC152)); + ASSERT_EQ(kSanyoAc152Bits, IRsend::defaultBits(decode_type_t::SANYO_AC152)); + ASSERT_EQ(kSanyoAc152MinRepeat, + IRsend::minRepeats(decode_type_t::SANYO_AC152)); } TEST(TestDecodeSanyoAc, DecodeRealExamples) { @@ -820,3 +828,70 @@ TEST(TestSanyoAc88Class, Clock) { ac.setClock(25 * 60 + 61); EXPECT_EQ(23 * 60 + 59, ac.getClock()); } + +TEST(TestDecodeSanyoAc152, DecodeRealExamples) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // Ref: "16c" from https://github.com/crankyoldgit/IRremoteESP8266/issues/1826#issuecomment-1160708653 + const uint16_t rawData[307] = { + 3294, 1726, 420, 330, 458, 462, 382, 452, 438, 330, 456, 458, 384, 454, + 384, 1312, 422, 324, 512, 336, 454, 458, 384, 450, 438, 358, 436, 464, + 424, 326, 458, 476, 372, 458, 430, 328, 458, 464, 382, 1308, 424, 326, + 510, 1264, 424, 268, 520, 460, 380, 460, 436, 324, 462, 400, 436, 474, + 372, 456, 430, 342, 452, 450, 388, 446, 442, 1262, 422, 266, 520, 1314, + 372, 1306, 424, 1258, 370, 390, 448, 1314, 372, 1310, 426, 308, 522, 338, + 454, 470, 370, 454, 438, 330, 456, 468, 370, 456, 384, 464, 384, 1306, + 422, 328, 460, 472, 374, 448, 442, 1258, 426, 1256, 426, 268, 520, 464, + 382, 460, 430, 328, 508, 1264, 426, 262, 572, 1262, 424, 228, 604, 1262, + 372, 1312, 372, 1310, 426, 1256, 422, 1258, 424, 262, 524, 418, 428, 456, + 382, 1308, 372, 456, 386, 456, 382, 464, 378, 1308, 424, 360, 436, 454, + 430, 344, 450, 1306, 372, 1310, 424, 326, 510, 338, 452, 456, 384, 456, + 436, 328, 510, 1258, 372, 1310, 422, 338, 454, 466, 424, 328, 460, 1310, + 372, 1312, 424, 1258, 450, 262, 496, 1310, 372, 1310, 426, 1260, 424, 260, + 526, 442, 442, 1264, 426, 268, 520, 458, 380, 450, 386, 462, 436, 320, + 518, 1256, 372, 394, 446, 398, 494, 334, 506, 326, 510, 276, 518, 460, + 430, 332, 508, 326, 510, 356, 440, 448, 444, 264, 572, 336, 456, 408, 434, + 454, 438, 332, 506, 336, 454, 460, 384, 448, 444, 326, 510, 332, 456, 442, + 400, 456, 384, 456, 386, 452, 388, 454, 440, 326, 512, 272, 518, 450, 440, + 334, 454, 458, 440, 320, 516, 268, 570, 340, 452, 468, 424, 260, 574, 336, + 456, 394, 444, 458, 438, 328, 508, 1258, 370, 1312, 372, 1312, 424, 314, + 468, 1312, 450, 1232, 370, 1314, 420, 324, 514}; // UNKNOWN 584FBE80 + + const uint8_t expectedState[kSanyoAc152StateLength] = { + 0x40, 0x00, 0x14, 0x80, 0x6E, 0x80, 0x18, 0xEA, 0x23, 0x62, + 0x30, 0xEE, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x77}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 307, 38000); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(SANYO_AC152, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAc152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +TEST(TestDecodeSanyoAc152, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expectedState[kSanyoAc152StateLength] = { + 0x40, 0x00, 0x14, 0x80, 0x6E, 0x80, 0x18, 0xEA, 0x23, 0x62, + 0x30, 0xEE, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x77}; + irsend.begin(); + irsend.reset(); + irsend.sendSanyoAc152(expectedState); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(SANYO_AC152, irsend.capture.decode_type); + EXPECT_EQ(kSanyoAc152Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits); + EXPECT_FALSE(irsend.capture.repeat); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); +} From 7e392f33ceb9d8541a8421ad38f2386b2cad329c Mon Sep 17 00:00:00 2001 From: David Conran Date: Fri, 24 Jun 2022 05:08:17 +1000 Subject: [PATCH 2/2] Add supported AC model. --- src/ir_Sanyo.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ir_Sanyo.h b/src/ir_Sanyo.h index f92d0ab63..42575cb3b 100644 --- a/src/ir_Sanyo.h +++ b/src/ir_Sanyo.h @@ -23,6 +23,7 @@ // Brand: Sanyo, Model: SAP-K242AH A/C (SANYO_AC) // Brand: Sanyo, Model: RCS-2S4E remote (SANYO_AC) // Brand: Sanyo, Model: RCS-4MHVPIS4EE remote (SANYO_AC152) +// Brand: Sanyo, Model: SAP-KMRV124EHE A/C (SANYO_AC152) #ifndef IR_SANYO_H_ #define IR_SANYO_H_