Experimental basic support for Carrier 84-bit protocol. (#1945)

* Add `sendCarrierAC84()` and `decodeCarrierAC84()` routines.
* Create some unit tests to cover the new code.
* Hack in some support for non-byte-aligned > 64 bit protocols.
* Update supported devices.

For #1943
This commit is contained in:
David Conran
2023-01-18 17:02:36 +10:00
committed by GitHub
parent 406a817f33
commit e7824c4b6a
12 changed files with 272 additions and 7 deletions

View File

@@ -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

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,

View File

@@ -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.
};

View File

@@ -6,6 +6,7 @@
#endif
#define __STDC_LIMIT_MACROS
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <algorithm>
@@ -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<float>(results->bits) / 8.0);
output += F("uint8_t state[");
output += uint64ToString(nbytes);
output += F("] = {");

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -3,15 +3,16 @@
#ifndef TEST_IRRECV_TEST_H_
#define TEST_IRRECV_TEST_H_
#include <math.h>
#include <iostream>
#include <sstream>
#include <string>
#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_

View File

@@ -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));
}