diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index b1bb6ddd..c791c8cf 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1173,6 +1173,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting WOWWEE decode"); if (decodeWowwee(results, offset)) return true; #endif // DECODE_WOWWEE +#if DECODE_CARRIER_AC84 + DPRINTLN("Attempting Carrier A/C 84-bit decode"); + if (decodeCarrierAC84(results, offset)) return true; +#endif // DECODE_CARRIER_AC84 // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index f3d33575..c760eb8d 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -583,6 +583,12 @@ class IRrecv { const uint16_t nbits = kCarrierAc40Bits, const bool strict = true); #endif // DECODE_CARRIER_AC40 +#if DECODE_CARRIER_AC84 + bool decodeCarrierAC84(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kCarrierAc84Bits, + const bool strict = true); +#endif // DECODE_CARRIER_AC84 #if DECODE_CARRIER_AC64 bool decodeCarrierAC64(decode_results *results, uint16_t offset = kStartOffset, diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index b58369fa..e2b08983 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -938,6 +938,13 @@ #define SEND_WOWWEE _IR_ENABLE_DEFAULT_ #endif // SEND_WOWWEE +#ifndef DECODE_CARRIER_AC84 +#define DECODE_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // DECODE_CARRIER_AC84 +#ifndef SEND_CARRIER_AC84 +#define SEND_CARRIER_AC84 _IR_ENABLE_DEFAULT_ +#endif // SEND_CARRIER_AC84 + #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 || \ @@ -956,6 +963,7 @@ DECODE_KELON168 || DECODE_HITACHI_AC296 || DECODE_CARRIER_AC128 || \ DECODE_DAIKIN200 || DECODE_HAIER_AC160 || DECODE_TCL96AC || \ DECODE_BOSCH144 || DECODE_SANYO_AC152 || DECODE_DAIKIN312 || \ + DECODE_CARRIER_AC84 || \ 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 @@ -1120,8 +1128,9 @@ enum decode_type_t { DAIKIN312, GORENJE, WOWWEE, + CARRIER_AC84, // 125 // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = WOWWEE, + kLastDecodeType = CARRIER_AC84, }; // Message lengths & required repeat values @@ -1159,6 +1168,9 @@ const uint16_t kCarrierAc40Bits = 40; const uint16_t kCarrierAc40MinRepeat = 2; const uint16_t kCarrierAc64Bits = 64; const uint16_t kCarrierAc64MinRepeat = kNoRepeat; +const uint16_t kCarrierAc84StateLength = 11; +const uint16_t kCarrierAc84Bits = kCarrierAc84StateLength * 8 - 4; +const uint16_t kCarrierAc84MinRepeat = kNoRepeat; const uint16_t kCarrierAc128StateLength = 16; const uint16_t kCarrierAc128Bits = kCarrierAc128StateLength * 8; const uint16_t kCarrierAc128MinRepeat = kNoRepeat; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 53aaaf1d..629e4bdf 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -693,6 +693,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kBosch144Bits; case CORONA_AC: return kCoronaAcBits; + case CARRIER_AC84: + return kCarrierAc84Bits; case CARRIER_AC128: return kCarrierAc128Bits; case DAIKIN: @@ -1172,6 +1174,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendBosch144(state, nbytes); break; #endif // SEND_BOSCH144 +#if SEND_CARRIER_AC84 + case CARRIER_AC84: + sendCarrierAC84(state, nbytes); + break; +#endif // SEND_CARRIER_AC84 #if SEND_CARRIER_AC128 case CARRIER_AC128: sendCarrierAC128(state, nbytes); diff --git a/src/IRsend.h b/src/IRsend.h index 48b82f06..5169fa87 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -608,6 +608,11 @@ class IRsend { void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits, uint16_t repeat = kCarrierAc64MinRepeat); #endif +#if SEND_CARRIER_AC84 + void sendCarrierAC84(const uint8_t data[], + const uint16_t nbytes = kCarrierAc84StateLength, + const uint16_t repeat = kNoRepeat); +#endif // SEND_CARRIER_AC84 #if SEND_CARRIER_AC128 void sendCarrierAC128(const uint8_t data[], uint16_t nbytes = kCarrierAc128StateLength, diff --git a/src/IRtext.cpp b/src/IRtext.cpp index e21e5a3f..01cecace 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -551,6 +551,8 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" COND(DECODE_WOWWEE || SEND_WOWWEE, D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84, + D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0" ///< New protocol (macro) 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 2ca21684..7c3a3737 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -6,6 +6,7 @@ #endif #define __STDC_LIMIT_MACROS +#include #include #include #include @@ -173,6 +174,7 @@ bool hasACState(const decode_type_t protocol) { case AMCOR: case ARGO: case BOSCH144: + case CARRIER_AC84: case CARRIER_AC128: case CORONA_AC: case DAIKIN: @@ -304,7 +306,7 @@ String resultToSourceCode(const decode_results * const results) { if (results->decode_type != UNKNOWN) { if (hasState) { #if DECODE_AC - uint16_t nbytes = results->bits / 8; + uint16_t nbytes = ceil(static_cast(results->bits) / 8.0); output += F("uint8_t state["); output += uint64ToString(nbytes); output += F("] = {"); diff --git a/src/ir_Carrier.cpp b/src/ir_Carrier.cpp index 42b45f9a..ef51cc43 100644 --- a/src/ir_Carrier.cpp +++ b/src/ir_Carrier.cpp @@ -46,6 +46,15 @@ const uint16_t kCarrierAc64OneSpace = 1736; const uint16_t kCarrierAc64ZeroSpace = 615; const uint32_t kCarrierAc64Gap = kDefaultMessageGap; // A guess. +//< @see: https://github.com/crankyoldgit/IRremoteESP8266/issues/1943#issue-1519570772 +const uint16_t kCarrierAc84HdrMark = 5850; +const uint16_t kCarrierAc84Zero = 1175; +const uint16_t kCarrierAc84One = 430; +const uint16_t kCarrierAc84HdrSpace = kCarrierAc84Zero; +const uint32_t kCarrierAc84Gap = kDefaultMessageGap; // A guess. +const uint8_t kCarrierAc84ExtraBits = 4; +const uint8_t kCarrierAc84ExtraTolerance = 5; + const uint16_t kCarrierAc128HdrMark = 4600; const uint16_t kCarrierAc128HdrSpace = 2600; const uint16_t kCarrierAc128Hdr2Mark = 9300; @@ -645,3 +654,94 @@ bool IRrecv::decodeCarrierAC128(decode_results *results, uint16_t offset, return true; } #endif // DECODE_CARRIER_AC128 + +#if SEND_CARRIER_AC84 +/// Send a Carroer A/C 84 Bit formatted message. +/// Status: BETA / Untested but probably works. +/// @param[in] data The message to be sent. +/// @param[in] nbytes The byte size of the message being sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendCarrierAC84(const uint8_t data[], const uint16_t nbytes, + const uint16_t repeat) { + // Protocol uses a constant bit time encoding. + for (uint16_t r = 0; r <= repeat; r++) { + if (nbytes) { + // The least significant `kCarrierAc84ExtraBits` bits of the first byte + sendGeneric(kCarrierAc84HdrMark, kCarrierAc84HdrSpace, // Header + kCarrierAc84Zero, kCarrierAc84One, // Data + kCarrierAc84One, kCarrierAc84Zero, + 0, 0, // No footer + GETBITS64(data[0], 0, kCarrierAc84ExtraBits), + kCarrierAc84ExtraBits, + 38000, false, 0, 33); + // The rest of the data. + sendGeneric(0, 0, // No Header + kCarrierAc84Zero, kCarrierAc84One, // Data + kCarrierAc84One, kCarrierAc84Zero, + kCarrierAc84Zero, kDefaultMessageGap, // Footer + data + 1, nbytes - 1, 38000, false, 0, 33); + } + } +} +#endif // SEND_CARRIER_AC84 + +#if DECODE_CARRIER_AC84 +/// Decode the supplied Carroer A/C 84 Bit formatted message. +/// Status: STABLE / Confirmed 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::decodeCarrierAC84(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Check if we have enough data to even possibly match. + if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset) + return false; // Can't possibly be a valid Carrier message. + // Compliance check. + if (strict && nbits != kCarrierAc84Bits) return false; + + // This decoder expects to decode an unusual number of bits. Check before we + // start. + if (nbits % 8 != kCarrierAc84ExtraBits) return false; + + uint64_t data = 0; + uint16_t used = 0; + + // Header + Data (kCarrierAc84ExtraBits only) + used = matchGenericConstBitTime(results->rawbuf + offset, &data, + results->rawlen - offset, + kCarrierAc84ExtraBits, + // Header (None) + kCarrierAc84HdrMark, kCarrierAc84HdrSpace, + // Data + kCarrierAc84Zero, kCarrierAc84One, + // No Footer + 0, 0, + false, + _tolerance + kCarrierAc84ExtraTolerance, + kMarkExcess, false); + if (!used) return false; + // Stuff the captured data so far into the first byte of the state. + *results->state = data; + offset += used; + // Capture the rest of the data as normal as we should be on a byte boundary. + // Data + Footer + if (!matchGeneric(results->rawbuf + offset, results->state + 1, + results->rawlen - offset, nbits - kCarrierAc84ExtraBits, + 0, 0, // No Header expected. + kCarrierAc84Zero, kCarrierAc84One, // Data + kCarrierAc84One, kCarrierAc84Zero, + kCarrierAc84Zero, kDefaultMessageGap, true, + _tolerance + kCarrierAc84ExtraTolerance, + kMarkExcess, false)) return false; + + // Success + results->decode_type = decode_type_t::CARRIER_AC84; + results->bits = nbits; + results->repeat = false; + return true; +} +#endif // DECODE_CARRIER_AC84 diff --git a/src/ir_Carrier.h b/src/ir_Carrier.h index c642c51e..7554a45b 100644 --- a/src/ir_Carrier.h +++ b/src/ir_Carrier.h @@ -4,6 +4,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1127 /// @see https://docs.google.com/spreadsheets/d/1EZy78L0cn1KDIX1aKq2biptejFqCjD5HO3tLiRvXf48/edit#gid=0 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1797 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1943 // Supports: // Brand: Carrier/Surrey, Model: 42QG5A55970 remote @@ -13,6 +14,8 @@ // Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C // Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter // Brand: Carrier, Model: 40GKX0E2006 remote (CARRIER_AC128) +// Brand: Carrier, Model: 3021203 RR03-S-Remote (CARRIER_AC84) +// Brand: Carrier, Model: 342WM100CT A/C (CARRIER_AC84) #ifndef IR_CARRIER_H_ #define IR_CARRIER_H_ diff --git a/src/locale/defaults.h b/src/locale/defaults.h index e54c3a88..0a898427 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -769,6 +769,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_CARRIER_AC64 #define D_STR_CARRIER_AC64 D_STR_CARRIER_AC "64" #endif // D_STR_CARRIER_AC64 +#ifndef D_STR_CARRIER_AC84 +#define D_STR_CARRIER_AC84 D_STR_CARRIER_AC "84" +#endif // D_STR_CARRIER_AC84 #ifndef D_STR_CARRIER_AC128 #define D_STR_CARRIER_AC128 D_STR_CARRIER_AC "128" #endif // D_STR_CARRIER_AC128 diff --git a/test/IRrecv_test.h b/test/IRrecv_test.h index bb366c1e..42c3f0a1 100644 --- a/test/IRrecv_test.h +++ b/test/IRrecv_test.h @@ -3,15 +3,16 @@ #ifndef TEST_IRRECV_TEST_H_ #define TEST_IRRECV_TEST_H_ +#include #include #include #include #include "IRutils.h" -#define EXPECT_STATE_EQ(a, b, c) \ - for (uint8_t i = 0; i < c / 8; ++i) { \ - EXPECT_EQ(a[i], b[i]) << "Expected state " \ - "differs at i = " \ - << uint64ToString(i); \ +#define EXPECT_STATE_EQ(a, b, c) \ + for (uint8_t i = 0; i < ceil((c) / 8.0); ++i) { \ + EXPECT_EQ((a)[i], (b)[i]) << "Expected state " \ + "differs at i = " \ + << uint64ToString(i); \ } #endif // TEST_IRRECV_TEST_H_ diff --git a/test/ir_Carrier_test.cpp b/test/ir_Carrier_test.cpp index fa0f07b0..7920b83c 100644 --- a/test/ir_Carrier_test.cpp +++ b/test/ir_Carrier_test.cpp @@ -264,6 +264,16 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ(kCarrierAc64MinRepeat, IRsend::minRepeats(decode_type_t::CARRIER_AC64)); + // CARRIER_AC84 + ASSERT_EQ("CARRIER_AC84", typeToString(decode_type_t::CARRIER_AC84)); + ASSERT_EQ(decode_type_t::CARRIER_AC84, strToDecodeType("CARRIER_AC84")); + ASSERT_TRUE(hasACState(decode_type_t::CARRIER_AC84)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::CARRIER_AC84)); + ASSERT_EQ(kCarrierAc84Bits, + IRsend::defaultBits(decode_type_t::CARRIER_AC84)); + ASSERT_EQ(kNoRepeat, + IRsend::minRepeats(decode_type_t::CARRIER_AC84)); + // CARRIER_AC128 ASSERT_EQ("CARRIER_AC128", typeToString(decode_type_t::CARRIER_AC128)); ASSERT_EQ(decode_type_t::CARRIER_AC128, strToDecodeType("CARRIER_AC128")); @@ -693,3 +703,113 @@ TEST(TestDecodeCarrierAC128, SyntheticExample) { stdAc::state_t r, p; ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); } + +// Decode a "real" Carrier 84bit example message. +TEST(TestDecodeCarrierAC84, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expected_state[kCarrierAc84StateLength] = { + 0x03, 0x00, 0x93, 0x31, 0x00, 0x12, 0x00, 0x12, 0x54, 0x21, 0xE8}; + irsend.begin(); + + irsend.reset(); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1943#issue-1519570772 + const uint16_t rawData[171] = { + 5844, 1152, + 1154, 462, 1152, 462, 418, 1194, 418, 1194, 420, 1194, 416, 1198, + 418, 1196, 416, 1196, 418, 1194, 420, 1192, 420, 1194, 420, 1194, + 1152, 460, 1152, 460, 420, 1192, 418, 1194, 1154, 458, 418, 1194, + 420, 1192, 1154, 460, 1152, 458, 420, 1194, 418, 1194, 418, 1194, + 1152, 460, 1154, 458, 418, 1196, 416, 1194, 418, 1194, 418, 1196, + 420, 1194, 418, 1192, 420, 1194, 420, 1192, 418, 1196, 416, 1194, + 416, 1196, 1152, 462, 416, 1194, 422, 1192, 1154, 458, 420, 1192, + 418, 1194, 418, 1194, 418, 1196, 420, 1194, 418, 1192, 420, 1194, + 418, 1192, 420, 1194, 418, 1194, 420, 1194, 418, 1194, 1154, 458, + 418, 1194, 418, 1192, 1154, 458, 418, 1196, 416, 1194, 420, 1194, + 416, 1192, 418, 1194, 1152, 462, 416, 1196, 1152, 458, 418, 1196, + 1152, 458, 418, 1194, 1154, 460, 418, 1194, 418, 1196, 418, 1196, + 418, 1194, 1154, 460, 420, 1192, 418, 1194, 420, 1194, 418, 1194, + 418, 1196, 1152, 460, 418, 1194, 1154, 458, 1154, 460, 1152, 458, + 1152}; // UNKNOWN E366A1BC + + + irsend.sendRaw(rawData, 171, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC84, irsend.capture.decode_type); + EXPECT_EQ(kCarrierAc84Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + EXPECT_EQ( + "Protocol : CARRIER_AC84\n" + "Code : 0x03009331001200125421E8 (84 Bits)\n", + resultToHumanReadableBasic(&irsend.capture)); +} + +// Decode a synthetic Carrier AC 84-bit message. +TEST(TestDecodeCarrierAC84, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + irsend.reset(); + + const uint8_t expected_state[kCarrierAc84StateLength] = { + 0x0C, 0x00, 0xC9, 0x8C, 0x00, 0x48, 0x00, 0x48, 0x2A, 0x84, 0x17}; + irsend.sendCarrierAC84(expected_state); + irsend.makeDecodeResult(); + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(CARRIER_AC84, irsend.capture.decode_type); + ASSERT_EQ(kCarrierAc84Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + stdAc::state_t r, p; + ASSERT_FALSE(IRAcUtils::decodeToState(&irsend.capture, &r, &p)); +} + +// Decode a "real" troublesome Carrier 84bit example message. +TEST(TestDecodeCarrierAC84, RealExample2) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint8_t expected_state[kCarrierAc84StateLength] = { + 0x03, 0x00, 0x03, 0x32, 0x00, 0x12, 0x00, 0x12, 0x33, 0x11, 0xDC}; + irsend.begin(); + + irsend.reset(); + // Data from: + // https://github.com/crankyoldgit/IRremoteESP8266/issues/1943#issuecomment-1374434085 + const uint16_t rawData[171] = { + 5828, 1158, 1148, 464, 1148, 494, 384, 1200, 414, 1202, 412, 1202, 410, + 1204, 408, 1204, 408, 1206, 408, 1228, 386, 1202, 408, 1232, 386, 1202, + 1148, 464, 1146, 468, 412, 1200, 384, 1230, 412, 1200, 412, 1200, 410, + 1204, 412, 1200, 412, 1202, 1146, 468, 410, 1202, 410, 1204, 1146, 466, + 1146, 464, 410, 1230, 386, 1198, 414, 1200, 412, 1202, 410, 1202, 410, + 1200, 410, 1204, 408, 1204, 410, 1202, 412, 1226, 386, 1202, 1144, 468, + 408, 1204, 410, 1200, 1146, 468, 412, 1198, 410, 1206, 408, 1202, 410, + 1228, 384, 1228, 386, 1202, 410, 1202, 412, 1202, 408, 1202, 410, 1202, + 408, 1202, 410, 1204, 1146, 464, 412, 1202, 412, 1200, 1146, 468, 408, + 1202, 412, 1200, 382, 1232, 1146, 488, 1122, 466, 410, 1228, 386, 1200, + 1146, 492, 1118, 466, 412, 1202, 410, 1204, 1146, 464, 414, 1198, 410, + 1204, 410, 1202, 1150, 464, 410, 1202, 412, 1202, 410, 1226, 386, 1200, + 410, 1228, 1148, 440, 1148, 466, 1144, 468, 408, 1202, 1144, 470, 1144, + 492, 1120}; // UNKNOWN 4CC7BE54 + + irsend.sendRaw(rawData, 171, 38000); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(CARRIER_AC84, irsend.capture.decode_type); + EXPECT_EQ(kCarrierAc84Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected_state, irsend.capture.state, irsend.capture.bits); + EXPECT_EQ( + "", + IRAcUtils::resultAcToString(&irsend.capture)); + EXPECT_EQ( + "Protocol : CARRIER_AC84\n" + "Code : 0x03000332001200123311DC (84 Bits)\n", + resultToHumanReadableBasic(&irsend.capture)); +}