From 51b6230b8b46764d18774547088e8ecd20c3770d Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Sat, 4 Sep 2021 10:16:00 +1000 Subject: [PATCH 1/3] Experimental support for Arris protocol. * Add `sendArris()` & `decodeArris()`. * Uses Manchester Encoding. * Add send & decode unit tests. Fixes #1595 --- src/IRrecv.cpp | 37 +++++++-- src/IRrecv.h | 4 + src/IRremoteESP8266.h | 11 ++- src/IRsend.cpp | 8 +- src/IRsend.h | 4 + src/IRtext.cpp | 1 + src/ir_Arris.cpp | 79 +++++++++++++++++++ src/locale/defaults.h | 3 + test/ir_Arris_test.cpp | 175 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 315 insertions(+), 7 deletions(-) create mode 100644 src/ir_Arris.cpp create mode 100644 test/ir_Arris_test.cpp diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 9c7519cbd..4b208c031 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1036,6 +1036,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Bose decode"); if (decodeBose(results, offset)) return true; #endif // DECODE_BOSE +#if DECODE_ARRIS + DPRINTLN("Attempting Arris decode"); + if (decodeArris(results, offset)) return true; +#endif // DECODE_ARRIS // Typically new protocols are added above this line. } #if DECODE_HASH @@ -1811,6 +1815,7 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, const int16_t excess, const bool MSBfirst, const bool GEThomas) { + DPRINTLN("DEBUG: Entered matchManchesterData"); uint16_t offset = 0; uint64_t data = 0; uint16_t nr_half_periods = 0; @@ -1824,7 +1829,10 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, uint16_t min_remaining = nbits; // Check if there is enough capture buffer to possibly have the message. - if (remaining < min_remaining) return 0; // Nope, so abort. + if (remaining < min_remaining) { + DPRINTLN("DEBUG: Ran out of capture buffer!"); + return 0; // Nope, so abort. + } // Convert to ticks. Optimisation: Saves on math/extra instructions later. uint16_t bank = starting_balance / kRawTick; @@ -1847,22 +1855,39 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, while ((offset < remaining || bank) && nr_half_periods < expected_half_periods) { // Get the next entry if we haven't anything existing to process. + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); if (!bank) bank = *(data_ptr + offset++); + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); // Check if we don't have a short interval. - if (!match(bank, half_period, tolerance, excess)) return 0; // Not valid. + DPRINTLN("DEBUG: Checking for short interval"); + if (!match(bank, half_period, tolerance, excess)) { + DPRINTLN("DEBUG: It is. Exiting"); + return 0; // Not valid. + } // We've succeeded in matching half a period, so count it. nr_half_periods++; + DPRINT("DEBUG: Half Periods = "); + DPRINTLN(nr_half_periods); // We've now used up our bank, so refill it with the next item, unless we // are at the end of the capture buffer. // If we are assume a single half period of "space". - if (offset < remaining) + if (offset < remaining) { + DPRINT("DEBUG: Offset = "); + DPRINTLN(offset); bank = *(data_ptr + offset++); - else if (offset == remaining) + } else if (offset == remaining) { bank = raw_half_period; - else + } else { return 0; // We are out of buffer, so abort! + } + DPRINT("DEBUG: Bank = "); + DPRINTLN(bank * kRawTick); // Shift the data along and add our new bit. + DPRINT("DEBUG: Adding bit: "); + DPRINTLN((currentBit ? "1" : "0")); data <<= 1; data |= currentBit; @@ -1870,10 +1895,12 @@ uint16_t IRrecv::matchManchesterData(volatile const uint16_t *data_ptr, if (match(bank, half_period * 2, tolerance, excess)) { // It is, so flip the bit we need to append, and remove a half_period of // time from the bank. + DPRINTLN("DEBUG: long interval detected"); currentBit = !currentBit; bank -= raw_half_period; } else if (match(bank, half_period, tolerance, excess)) { // It is a short interval, so eat up all the time and move on. + DPRINTLN("DEBUG: short interval detected"); bank = 0; } else if (nr_half_periods == expected_half_periods - 1 && matchAtLeast(bank, half_period, tolerance, excess)) { diff --git a/src/IRrecv.h b/src/IRrecv.h index 1a883d509..4c81a87dc 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -287,6 +287,10 @@ class IRrecv { bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kArgoBits, const bool strict = true); #endif // DECODE_ARGO +#if DECODE_ARRIS + bool decodeArris(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kArrisBits, const bool strict = true); +#endif // DECODE_ARRIS #if DECODE_SONY bool decodeSony(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kSonyMinBits, diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 35b45d958..f49d313c6 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -790,6 +790,13 @@ #define SEND_BOSE _IR_ENABLE_DEFAULT_ #endif // SEND_BOSE +#ifndef DECODE_ARRIS +#define DECODE_ARRIS _IR_ENABLE_DEFAULT_ +#endif // DECODE_ARRIS +#ifndef SEND_ARRIS +#define SEND_ARRIS _IR_ENABLE_DEFAULT_ +#endif // SEND_ARRIS + #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 || \ @@ -951,8 +958,9 @@ enum decode_type_t { TROTEC_3550, SANYO_AC88, // 105 BOSE, + ARRIS, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = BOSE, + kLastDecodeType = ARRIS, }; // Message lengths & required repeat values @@ -970,6 +978,7 @@ const uint16_t kAmcorDefaultRepeat = kSingleRepeat; const uint16_t kArgoStateLength = 12; const uint16_t kArgoBits = kArgoStateLength * 8; const uint16_t kArgoDefaultRepeat = kNoRepeat; +const uint16_t kArrisBits = 32; const uint16_t kCoolixBits = 24; const uint16_t kCoolixDefaultRepeat = kSingleRepeat; const uint16_t kCarrierAcBits = 32; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 4814bd431..8b79707c7 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -637,6 +637,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case LG: case LG2: return 28; + case ARRIS: case CARRIER_AC: case ELITESCREENS: case EPSON: @@ -790,7 +791,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, case AIWA_RC_T501: sendAiwaRCT501(data, nbits, min_repeat); break; -#endif +#endif // SEND_AIWA_RC_T501 +#if SEND_ARRIS + case ARRIS: + sendArris(data, nbits, min_repeat); + break; +#endif // SEND_ARRIS #if SEND_BOSE case BOSE: sendBose(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index c20fc64bc..5394a4d14 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -737,6 +737,10 @@ class IRsend { void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, const uint16_t repeat = kNoRepeat); #endif // SEND_BOSE +#if SEND_ARRIS + void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_ARRIS protected: #ifdef UNIT_TEST diff --git a/src/IRtext.cpp b/src/IRtext.cpp index a7bd7de29..008b102b5 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -293,5 +293,6 @@ const PROGMEM char *kAllProtocolNamesStr = D_STR_TROTEC_3550 "\x0" D_STR_SANYO_AC88 "\x0" D_STR_BOSE "\x0" + D_STR_ARRIS "\x0" ///< New protocol strings should be added just above this line. "\x0"; ///< This string requires double null termination. diff --git a/src/ir_Arris.cpp b/src/ir_Arris.cpp new file mode 100644 index 000000000..fbb23e9f6 --- /dev/null +++ b/src/ir_Arris.cpp @@ -0,0 +1,79 @@ +// Copyright 2021 David Conran +#include "IRrecv.h" +#include "IRsend.h" + +/// @file +/// @brief Arris "Manchester code" based protocol. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595 + +// Supports: +// Brand: Arris, Model: VIP1113M Set-top box + +const uint8_t kArrisOverhead = 2; +const uint16_t kArrisHalfClockPeriod = 320; // uSeconds +const uint16_t kArrisHdrMark = 8 * kArrisHalfClockPeriod; // uSeconds +const uint16_t kArrisHdrSpace = 6 * kArrisHalfClockPeriod; // uSeconds +const uint16_t kArrisGapSpace = 18.5 * kArrisHalfClockPeriod; // uSeconds + +#if SEND_ARRIS +/// Send an Arris Manchester Code formatted message. +/// Status: ALPHA / Untested. +/// @param[in] data The message to be sent. +/// @param[in] nbits The number of bits of the message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595 +void IRsend::sendArris(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + enableIROut(38); + for (uint16_t r = 0; r <= repeat; r++) { + // Header (part 1) + mark(kArrisHdrMark); + space(kArrisHdrSpace); + // Header (part 2) + Data + Footer + sendManchester(kArrisHalfClockPeriod * 2, 0, kArrisHalfClockPeriod, + 0, kArrisGapSpace, data, nbits); + } +} +#endif // SEND_ARRIS + +#if DECODE_ARRIS +/// Decode the supplied Arris "Manchester code" message. +/// +/// Status: ALPHA / Untested. +/// @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. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595 +bool IRrecv::decodeArris(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < nbits + kArrisOverhead - offset) + return false; // Too short a message to match. + + // Compliance + if (strict && nbits != kArrisBits) + return false; // Doesn't match our protocol defn. + + // Header (part 1) + if (!matchMark(results->rawbuf[offset++], kArrisHdrMark)) return false; + if (!matchSpace(results->rawbuf[offset++], kArrisHdrSpace)) return false; + + // Header (part 2) + Data + if (!matchManchester(results->rawbuf + offset, &results->value, + results->rawlen - offset, nbits, + kArrisHalfClockPeriod * 2, 0, + kArrisHalfClockPeriod, 0, 0, + false, kUseDefTol, kMarkExcess, true, false)) + return false; + + // Success + results->decode_type = decode_type_t::ARRIS; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_ARRIS diff --git a/src/locale/defaults.h b/src/locale/defaults.h index b55b87abc..bae02265a 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -508,6 +508,9 @@ #ifndef D_STR_ARGO #define D_STR_ARGO "ARGO" #endif // D_STR_ARGO +#ifndef D_STR_ARRIS +#define D_STR_ARRIS "ARRIS" +#endif // D_STR_ARRIS #ifndef D_STR_BOSE #define D_STR_BOSE "BOSE" #endif // D_STR_BOSE diff --git a/test/ir_Arris_test.cpp b/test/ir_Arris_test.cpp new file mode 100644 index 000000000..5b887b903 --- /dev/null +++ b/test/ir_Arris_test.cpp @@ -0,0 +1,175 @@ +// Copyright 2021 David Conran + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeArris(). + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1595 +// Data from: +// https://github.com/crankyoldgit/IRremoteESP8266/files/7100289/raw_data.txt +TEST(TestDecodeArris, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + const uint16_t rawData_1[59] = { + 2584, 1896, 666, 306, 338, 300, 336, 304, 668, 610, 332, 306, 338, 300, + 334, 304, 332, 312, 332, 306, 340, 300, 334, 304, 330, 308, 338, 302, 334, + 304, 330, 308, 336, 308, 336, 302, 332, 306, 330, 310, 674, 606, 336, 302, + 332, 306, 338, 306, 668, 612, 668, 306, 338, 304, 332, 308, 336, 608, + 334}; // UNKNOWN EDF1C0D0 + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData_1, 59, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decodeArris(&irsend.capture)); + ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); + EXPECT_EQ(kArrisBits, irsend.capture.bits); + EXPECT_EQ(0x1000085E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + irsend.reset(); + const uint16_t rawData_2[115] = { + 2584, 1898, 664, 308, 338, 302, 332, 306, 668, 614, 330, 308, 336, 302, + 332, 306, 340, 304, 330, 310, 336, 304, 332, 306, 338, 300, 334, 304, 330, + 308, 336, 302, 332, 312, 334, 306, 330, 308, 336, 302, 670, 610, 332, 306, + 330, 310, 336, 308, 674, 606, 664, 312, 334, 306, 338, 302, 334, 612, 330, + 5930, + 2584, 1898, 664, 308, 336, 302, 332, 306, 666, 614, 338, 300, 336, 304, + 332, 310, 674, 610, 332, 334, 312, 328, 308, 332, 304, 336, 310, 330, 306, + 332, 302, 314, 330, 336, 308, 330, 306, 334, 640, 612, 330, 308, 336, 302, + 332, 312, 672, 608, 672, 608, 672, 304, 330, 614, 330 + }; // UNKNOWN E6A77D83 + irsend.sendRaw(rawData_2, 115, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); + EXPECT_EQ(kArrisBits, irsend.capture.bits); + EXPECT_EQ(0x1000085E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + const uint16_t rawData_3[51] = { + 2584, 1896, 666, 308, 338, 328, 306, 332, 640, 612, 332, 336, 310, 300, + 334, 304, 678, 606, 336, 330, 306, 334, 310, 300, 334, 304, 332, 308, 338, + 302, 334, 310, 672, 304, 332, 614, 668, 612, 330, 336, 638, 620, 670, 610, + 670, 304, 330, 310, 336, 610, 672}; // UNKNOWN 4CA048A1 + irsend.reset(); + irsend.sendRaw(rawData_3, 51, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); + EXPECT_EQ(kArrisBits, irsend.capture.bits); + EXPECT_EQ(0x1080695D, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeArris, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendArris(0x1000085E); + irsend.makeDecodeResult(); + + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); + EXPECT_EQ(kArrisBits, irsend.capture.bits); + EXPECT_EQ(0x1000085E, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + EXPECT_EQ( + "f38000d50" +// const uint16_t rawData_1[59] = { +// 2584, 1896, + "m2560s1920" +// 666, 306, + "m640s320" +// 338, 300, + "m320s320" +// 336, 304, + "m320s320" +// 668, 610, + "m640s640" +// 332, 306, + "m320s320" +// 338, 300, + "m320s320" +// 334, 304, + "m320s320" +// 332, 312, + "m320s320" +// 332, 306, + "m320s320" +// 340, 300, + "m320s320" +// 334, 304, + "m320s320" +// 330, 308, + "m320s320" +// 338, 302, + "m320s320" +// 334, 304, + "m320s320" +// 330, 308, + "m320s320" +// 336, 308, + "m320s320" +// 336, 302, + "m320s320" +// 332, 306, + "m320s320" +// 330, 310, + "m320s320" +// 674, 606, + "m640s640" +// 336, 302, + "m320s320" +// 332, 306, + "m320s320" +// 338, 306, + "m320s320" +// 668, 612, + "m640s640" +// 668, 306, + "m640s320" +// 338, 304, + "m320s320" +// 332, 308, + "m320s320" +// 336, 608, + "m320s640" +// 334}; // UNKNOWN EDF1C0D0 + "m320s5920", irsend.outputStr()); + + irsend.reset(); + irsend.sendArris(0x1080695D); + irsend.makeDecodeResult(); + + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); + EXPECT_EQ(kArrisBits, irsend.capture.bits); + EXPECT_EQ(0x1080695D, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("ARRIS", typeToString(decode_type_t::ARRIS)); + ASSERT_EQ(decode_type_t::ARRIS, strToDecodeType("ARRIS")); + ASSERT_FALSE(hasACState(decode_type_t::ARRIS)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::ARRIS)); + ASSERT_EQ(kArrisBits, IRsend::defaultBits(decode_type_t::ARRIS)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::ARRIS)); +} From 3a500449e16a5f71905feffe43b73d2b0cebb774 Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Tue, 7 Sep 2021 09:15:42 +1000 Subject: [PATCH 2/3] Arris: Update gap & add some encoding tools. * Change the inter-message gap per observations. * Add helper routines: - `toggleArrisRelease()` for changing mesg status between a press & a release of a button. - `encodeArris()` to construct a valid ARRIS mesg from a `command` and a `release` status. * Update & add Unit Tests accordingly. * Update status of send & decode as they've been confirmed working. * Extend decodeArris to provide release status (via `address`) and the likely `command`. Fixes #1595 --- src/IRsend.h | 2 ++ src/ir_Arris.cpp | 47 ++++++++++++++++++++++++++++++++++++------ test/ir_Arris_test.cpp | 32 ++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/IRsend.h b/src/IRsend.h index 5394a4d14..82887a6a9 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -740,6 +740,8 @@ class IRsend { #if SEND_ARRIS void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits, const uint16_t repeat = kNoRepeat); + static uint32_t toggleArrisRelease(const uint32_t data); + static uint32_t encodeArris(const uint32_t command, const bool release); #endif // SEND_ARRIS protected: diff --git a/src/ir_Arris.cpp b/src/ir_Arris.cpp index fbb23e9f6..0aaf1a29c 100644 --- a/src/ir_Arris.cpp +++ b/src/ir_Arris.cpp @@ -1,6 +1,7 @@ // Copyright 2021 David Conran #include "IRrecv.h" #include "IRsend.h" +#include "IRutils.h" /// @file /// @brief Arris "Manchester code" based protocol. @@ -8,16 +9,24 @@ // Supports: // Brand: Arris, Model: VIP1113M Set-top box +// Brand: Arris, Model: 120A V1.0 A18 remote const uint8_t kArrisOverhead = 2; const uint16_t kArrisHalfClockPeriod = 320; // uSeconds const uint16_t kArrisHdrMark = 8 * kArrisHalfClockPeriod; // uSeconds const uint16_t kArrisHdrSpace = 6 * kArrisHalfClockPeriod; // uSeconds -const uint16_t kArrisGapSpace = 18.5 * kArrisHalfClockPeriod; // uSeconds +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595#issuecomment-913755841 +// aka. 77184 uSeconds. +const uint32_t kArrisGapSpace = 102144 - ((8 + 6 + kArrisBits * 2) * + kArrisHalfClockPeriod); // uSeconds +const uint32_t kArrisReleaseToggle = 0x800008; +const uint8_t kArrisChecksumSize = 4; +const uint8_t kArrisCommandSize = 19; +const uint8_t kArrisReleaseBit = kArrisChecksumSize + kArrisCommandSize; #if SEND_ARRIS /// Send an Arris Manchester Code formatted message. -/// Status: ALPHA / Untested. +/// Status: STABLE / Confirmed working. /// @param[in] data The message to be sent. /// @param[in] nbits The number of bits of the message to be sent. /// @param[in] repeat The number of times the command is to be repeated. @@ -34,12 +43,33 @@ void IRsend::sendArris(const uint64_t data, const uint16_t nbits, 0, kArrisGapSpace, data, nbits); } } + +/// Flip the toggle button release bits of an Arris message. +/// Used to indicate a change of remote button's state. e.g. Press vs. Release. +/// @param[in] data The existing Arris message. +/// @return A data message suitable for use in sendArris() with the release bits +/// flipped. +uint32_t IRsend::toggleArrisRelease(const uint32_t data) { + return data ^ kArrisReleaseToggle; +} + +/// Construct a raw 32-bit Arris message code from the supplied command & +/// release setting. +/// @param[in] command The command code. +/// @param[in] release The button/command action: press (false), release (true) +/// @return A raw 32-bit Arris message code suitable for sendArris() etc. +/// @note Sequence of bits = header + release + command + checksum. +uint32_t IRsend::encodeArris(const uint32_t command, const bool release) { + uint32_t result = 0x10000000; + irutils::setBits(&result, kArrisChecksumSize, kArrisCommandSize, command); + irutils::setBit(&result, kArrisReleaseBit, release); + return result + irutils::sumNibbles(result); +} #endif // SEND_ARRIS #if DECODE_ARRIS /// Decode the supplied Arris "Manchester code" message. -/// -/// Status: ALPHA / Untested. +/// 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 @@ -72,8 +102,13 @@ bool IRrecv::decodeArris(decode_results *results, uint16_t offset, // Success results->decode_type = decode_type_t::ARRIS; results->bits = nbits; - results->address = 0; - results->command = 0; + // Set the address as the Release Bit for something useful. + results->address = static_cast(GETBIT32(results->value, + kArrisReleaseBit)); + // The last 4 bits are likely a checksum value, so skip those. Everything else + // after the release bit. e.g. Bits 10-28 + results->command = GETBITS32(results->value, kArrisChecksumSize, + kArrisCommandSize); return true; } #endif // DECODE_ARRIS diff --git a/test/ir_Arris_test.cpp b/test/ir_Arris_test.cpp index 5b887b903..2a219b6b2 100644 --- a/test/ir_Arris_test.cpp +++ b/test/ir_Arris_test.cpp @@ -28,12 +28,12 @@ TEST(TestDecodeArris, RealExample) { irsend.sendRaw(rawData_1, 59, 38); irsend.makeDecodeResult(); - ASSERT_TRUE(irrecv.decodeArris(&irsend.capture)); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); EXPECT_EQ(kArrisBits, irsend.capture.bits); EXPECT_EQ(0x1000085E, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x85, irsend.capture.command); irsend.reset(); const uint16_t rawData_2[115] = { @@ -55,7 +55,7 @@ TEST(TestDecodeArris, RealExample) { EXPECT_EQ(kArrisBits, irsend.capture.bits); EXPECT_EQ(0x1000085E, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x85, irsend.capture.command); const uint16_t rawData_3[51] = { 2584, 1896, 666, 308, 338, 328, 306, 332, 640, 612, 332, 336, 310, 300, @@ -70,8 +70,8 @@ TEST(TestDecodeArris, RealExample) { ASSERT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); EXPECT_EQ(kArrisBits, irsend.capture.bits); EXPECT_EQ(0x1080695D, irsend.capture.value); - EXPECT_EQ(0x0, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x1, irsend.capture.address); + EXPECT_EQ(0x695, irsend.capture.command); } TEST(TestDecodeArris, SyntheticExample) { @@ -87,7 +87,7 @@ TEST(TestDecodeArris, SyntheticExample) { EXPECT_EQ(kArrisBits, irsend.capture.bits); EXPECT_EQ(0x1000085E, irsend.capture.value); EXPECT_EQ(0x0, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x85, irsend.capture.command); EXPECT_EQ( "f38000d50" @@ -151,7 +151,7 @@ TEST(TestDecodeArris, SyntheticExample) { // 336, 608, "m320s640" // 334}; // UNKNOWN EDF1C0D0 - "m320s5920", irsend.outputStr()); + "m320s77184", irsend.outputStr()); irsend.reset(); irsend.sendArris(0x1080695D); @@ -161,8 +161,8 @@ TEST(TestDecodeArris, SyntheticExample) { EXPECT_EQ(decode_type_t::ARRIS, irsend.capture.decode_type); EXPECT_EQ(kArrisBits, irsend.capture.bits); EXPECT_EQ(0x1080695D, irsend.capture.value); - EXPECT_EQ(0x0, irsend.capture.address); - EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x1, irsend.capture.address); + EXPECT_EQ(0x695, irsend.capture.command); } TEST(TestUtils, Housekeeping) { @@ -173,3 +173,17 @@ TEST(TestUtils, Housekeeping) { ASSERT_EQ(kArrisBits, IRsend::defaultBits(decode_type_t::ARRIS)); ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::ARRIS)); } + +TEST(TestSendArris, ReleaseToggle) { + EXPECT_EQ(0x10800F5D, IRsend::toggleArrisRelease(0x10000F55)); + EXPECT_EQ(0x10000F55, IRsend::toggleArrisRelease(0x10800F5D)); + EXPECT_EQ( + 0x10800F5D, + IRsend::toggleArrisRelease(IRsend::toggleArrisRelease(0x10800F5D))); +} + +TEST(TestSendArris, encodeArris) { + EXPECT_EQ(0x10800F5D, IRsend::encodeArris(0xF5, true)); + EXPECT_EQ(0x10000F55, IRsend::encodeArris(0xF5, false)); + EXPECT_EQ(0x1080695D, IRsend::encodeArris(0x695, true)); +} From 92a4684cfbb71690c89dddf21e6829f627318c0a Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Thu, 9 Sep 2021 10:01:52 +1000 Subject: [PATCH 3/3] Arris: Add checksum validation to decode. * Minor code style cleanup & potential optimisation. Fixes #1595 --- src/ir_Arris.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ir_Arris.cpp b/src/ir_Arris.cpp index 0aaf1a29c..5b39808c8 100644 --- a/src/ir_Arris.cpp +++ b/src/ir_Arris.cpp @@ -24,6 +24,8 @@ const uint8_t kArrisChecksumSize = 4; const uint8_t kArrisCommandSize = 19; const uint8_t kArrisReleaseBit = kArrisChecksumSize + kArrisCommandSize; +using irutils::sumNibbles; + #if SEND_ARRIS /// Send an Arris Manchester Code formatted message. /// Status: STABLE / Confirmed working. @@ -63,7 +65,7 @@ uint32_t IRsend::encodeArris(const uint32_t command, const bool release) { uint32_t result = 0x10000000; irutils::setBits(&result, kArrisChecksumSize, kArrisCommandSize, command); irutils::setBit(&result, kArrisReleaseBit, release); - return result + irutils::sumNibbles(result); + return result + sumNibbles(result); } #endif // SEND_ARRIS @@ -92,23 +94,30 @@ bool IRrecv::decodeArris(decode_results *results, uint16_t offset, if (!matchSpace(results->rawbuf[offset++], kArrisHdrSpace)) return false; // Header (part 2) + Data - if (!matchManchester(results->rawbuf + offset, &results->value, + uint64_t data = 0; + if (!matchManchester(results->rawbuf + offset, &data, results->rawlen - offset, nbits, kArrisHalfClockPeriod * 2, 0, kArrisHalfClockPeriod, 0, 0, false, kUseDefTol, kMarkExcess, true, false)) return false; + // Compliance + if (strict) + // Validate the checksum. + if (GETBITS32(data, 0, kArrisChecksumSize) != + sumNibbles(data >> kArrisChecksumSize)) + return false; + // Success results->decode_type = decode_type_t::ARRIS; results->bits = nbits; + results->value = data; // Set the address as the Release Bit for something useful. - results->address = static_cast(GETBIT32(results->value, - kArrisReleaseBit)); + results->address = static_cast(GETBIT32(data, kArrisReleaseBit)); // The last 4 bits are likely a checksum value, so skip those. Everything else // after the release bit. e.g. Bits 10-28 - results->command = GETBITS32(results->value, kArrisChecksumSize, - kArrisCommandSize); + results->command = GETBITS32(data, kArrisChecksumSize, kArrisCommandSize); return true; } #endif // DECODE_ARRIS