Skip to content

Commit

Permalink
Experimental basic support for Carrier 84-bit protocol. (#1945)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
crankyoldgit authored Jan 18, 2023
1 parent 406a817 commit e7824c4
Show file tree
Hide file tree
Showing 12 changed files with 272 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 13 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 || \
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};
Expand Down
4 changes: 3 additions & 1 deletion src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#endif

#define __STDC_LIMIT_MACROS
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <algorithm>
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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("] = {");
Expand Down
100 changes: 100 additions & 0 deletions src/ir_Carrier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
3 changes: 3 additions & 0 deletions src/ir_Carrier.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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_
Expand Down
3 changes: 3 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 6 additions & 5 deletions test/IRrecv_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
Loading

0 comments on commit e7824c4

Please sign in to comment.