From de85804806fac860b17e2aa12dbb385c9214fbfe Mon Sep 17 00:00:00 2001 From: crankyoldgit Date: Wed, 17 Nov 2021 17:06:32 +1000 Subject: [PATCH] Basic support for Airton Protocol * `sendAirton()` & `decodeAirton()` added. * Unit test coverage added. For #1670 --- src/IRrecv.cpp | 4 +++ src/IRrecv.h | 5 +++ src/IRremoteESP8266.h | 16 ++++++++-- src/IRsend.cpp | 6 ++++ src/IRsend.h | 4 +++ src/IRtext.cpp | 1 + src/ir_Airton.cpp | 70 +++++++++++++++++++++++++++++++++++++++++ src/locale/defaults.h | 3 ++ test/ir_Airton_test.cpp | 62 ++++++++++++++++++++++++++++++++++++ 9 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/ir_Airton.cpp create mode 100644 test/ir_Airton_test.cpp diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 43cc6025b..4970dcf0b 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1043,6 +1043,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Rhoss decode"); if (decodeRhoss(results, offset)) return true; #endif // DECODE_RHOSS +#if DECODE_AIRTON + DPRINTLN("Attempting Airton decode"); + if (decodeAirton(results, offset)) return true; +#endif // DECODE_AIRTON // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index 13aadb657..f4932b5a6 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -774,6 +774,11 @@ class IRrecv { bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kRhossBits, const bool strict = true); #endif // DECODE_RHOSS +#if DECODE_AIRTON + bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kAirtonBits, + const bool strict = true); +#endif // DECODE_AIRTON }; #endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 29e51c610..00c37b552 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -798,12 +798,19 @@ #endif // SEND_ARRIS #ifndef DECODE_RHOSS -#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ +#define DECODE_RHOSS _IR_ENABLE_DEFAULT_ #endif // DECODE_RHOSS #ifndef SEND_RHOSS -#define SEND_RHOSS _IR_ENABLE_DEFAULT_ +#define SEND_RHOSS _IR_ENABLE_DEFAULT_ #endif // SEND_RHOSS +#ifndef DECODE_AIRTON +#define DECODE_AIRTON _IR_ENABLE_DEFAULT_ +#endif // DECODE_AIRTON +#ifndef SEND_AIRTON +#define SEND_AIRTON _IR_ENABLE_DEFAULT_ +#endif // SEND_AIRTON + #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 || \ @@ -967,14 +974,17 @@ enum decode_type_t { BOSE, ARRIS, RHOSS, + AIRTON, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = RHOSS, + kLastDecodeType = AIRTON, }; // Message lengths & required repeat values const uint16_t kNoRepeat = 0; const uint16_t kSingleRepeat = 1; +const uint16_t kAirtonBits = 56; +const uint16_t kAirtonDefaultRepeat = kNoRepeat; const uint16_t kAirwellBits = 34; const uint16_t kAirwellMinRepeats = 2; const uint16_t kAiwaRcT501Bits = 15; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 62556b7e9..e20b97045 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -666,6 +666,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case MIDEA: case PANASONIC: return 48; + case AIRTON: case ECOCLIM: case MAGIQUEST: case VESTEL_AC: @@ -784,6 +785,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, uint16_t min_repeat __attribute__((unused)) = std::max(IRsend::minRepeats(type), repeat); switch (type) { +#if SEND_AIRTON + case AIRTON: + sendAirton(data, nbits, min_repeat); + break; +#endif // SEND_AIRTON #if SEND_AIRWELL case AIRWELL: sendAirwell(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index 1854c8166..d55ce0238 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -766,6 +766,10 @@ class IRsend { const uint16_t nbytes = kRhossStateLength, const uint16_t repeat = kRhossDefaultRepeat); #endif // SEND_RHOSS +#if SEND_AIRTON + void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits, + const uint16_t repeat = kAirtonDefaultRepeat); +#endif // SEND_AIRTON protected: #ifdef UNIT_TEST diff --git a/src/IRtext.cpp b/src/IRtext.cpp index ef9f4eb3c..a580bff9f 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -381,6 +381,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_BOSE "\x0" D_STR_ARRIS "\x0" D_STR_RHOSS "\x0" + D_STR_AIRTON "\x0" ///< New protocol strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/src/ir_Airton.cpp b/src/ir_Airton.cpp new file mode 100644 index 000000000..87757dad5 --- /dev/null +++ b/src/ir_Airton.cpp @@ -0,0 +1,70 @@ +// Copyright 2021 David Conran (crankyoldgit) +/// @file +/// @brief Support for Airton protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670 + +// Supports: +// Brand: Airton, Model: SMVH09B-2A2A3NH ref. 409730 A/C +// Brand: Airton, Model: RD1A1 remote + +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +const uint16_t kAirtonHdrMark = 6630; +const uint16_t kAirtonBitMark = 400; +const uint16_t kAirtonHdrSpace = 3350; +const uint16_t kAirtonOneSpace = 1260; +const uint16_t kAirtonZeroSpace = 430; +const uint16_t kAirtonFreq = 38000; // Hz. (Just a guess) + +#if SEND_AIRTON +// Function should be safe up to 64 bits. +/// Send a Airton formatted message. +/// Status: BETA / Probably works. Needs to be tested with a real device. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kAirtonBits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendAirton(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kAirtonHdrMark, kAirtonHdrSpace, + kAirtonBitMark, kAirtonOneSpace, + kAirtonBitMark, kAirtonZeroSpace, + kAirtonBitMark, kDefaultMessageGap, + data, nbits, kAirtonFreq, false, repeat, kDutyDefault); +} +#endif // SEND_AIRTON + +#if DECODE_AIRTON +/// Decode the supplied Airton message. +/// Status: STABLE / Expected to work. LSBF ordering confirmed via temperature. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @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::decodeAirton(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHeader + kFooter - offset) + return false; // Too short a message to match. + if (strict && nbits != kAirtonBits) + return false; + + // Header + Data + Footer + if (!matchGeneric(&(results->rawbuf[offset]), &(results->value), + results->rawlen - offset, nbits, + kAirtonHdrMark, kAirtonHdrSpace, + kAirtonBitMark, kAirtonOneSpace, + kAirtonBitMark, kAirtonZeroSpace, + kAirtonBitMark, kDefaultMessageGap, + true, kUseDefTol, kMarkExcess, false)) return false; + + // Success + results->decode_type = decode_type_t::AIRTON; + results->bits = nbits; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_AIRTON diff --git a/src/locale/defaults.h b/src/locale/defaults.h index b6754133f..0fcc04791 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -669,6 +669,9 @@ #endif // D_STR_DG11J191 // Protocols Names +#ifndef D_STR_AIRTON +#define D_STR_AIRTON "AIRTON" +#endif // D_STR_AIRTON #ifndef D_STR_AIRWELL #define D_STR_AIRWELL "AIRWELL" #endif // D_STR_AIRWELL diff --git a/test/ir_Airton_test.cpp b/test/ir_Airton_test.cpp new file mode 100644 index 000000000..e4e66ee13 --- /dev/null +++ b/test/ir_Airton_test.cpp @@ -0,0 +1,62 @@ +// Copyright 2021 crankyoldgit + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeAirton(). + +TEST(TestDecodeAirton, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + const uint16_t rawData[115] = { + 6632, 3352, + 404, 1266, 404, 1264, 406, 430, 406, 430, 400, 1264, 406, 430, 402, 1264, + 408, 1262, 406, 1264, 404, 430, 402, 434, 402, 432, 402, 1264, 406, 430, + 404, 432, 400, 456, 376, 432, 402, 430, 402, 1264, 404, 1264, 404, 432, + 402, 434, 398, 434, 402, 430, 404, 1264, 404, 432, 402, 430, 404, 1264, + 406, 430, 402, 432, 400, 434, 402, 430, 402, 430, 404, 432, 402, 430, + 402, 432, 402, 432, 402, 430, 402, 432, 402, 430, 402, 434, 400, 432, + 402, 1264, 404, 430, 404, 1264, 404, 432, 402, 454, 378, 432, 402, 430, + 404, 1264, 404, 1264, 404, 1264, 378, 1292, 404, 432, 402, 1264, 404, 432, + 402}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 115, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRTON, irsend.capture.decode_type); + ASSERT_EQ(kAirtonBits, irsend.capture.bits); + EXPECT_EQ(0x5E1400090C11D3, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeAirton, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendAirton(0x5E1400090C11D3); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::AIRTON, irsend.capture.decode_type); + ASSERT_EQ(kAirtonBits, irsend.capture.bits); + EXPECT_EQ(0x5E1400090C11D3, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("AIRTON", typeToString(decode_type_t::AIRTON)); + ASSERT_EQ(decode_type_t::AIRTON, strToDecodeType("AIRTON")); + ASSERT_FALSE(hasACState(decode_type_t::AIRTON)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRTON)); + ASSERT_EQ(kAirtonBits, IRsend::defaultBits(decode_type_t::AIRTON)); + ASSERT_EQ(kAirtonDefaultRepeat, IRsend::minRepeats(decode_type_t::AIRTON)); +}