From b1b9277f5f28808bc9ed139c4d3d2c4f9892e56c Mon Sep 17 00:00:00 2001 From: parsnip42 Date: Sat, 28 Aug 2021 06:55:05 +0100 Subject: [PATCH] Experimental Bose remote support (#1579) * Experimental Bose remote support * Unit tests. Co-authored-by: crankyoldgit --- src/IRrecv.cpp | 4 ++ src/IRrecv.h | 4 ++ src/IRremoteESP8266.h | 11 ++++- src/IRsend.cpp | 6 +++ src/IRsend.h | 4 ++ src/IRtext.cpp | 1 + src/ir_Bose.cpp | 69 +++++++++++++++++++++++++++ src/locale/defaults.h | 3 ++ test/ir_Bose_test.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/ir_Bose.cpp create mode 100644 test/ir_Bose_test.cpp diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 887d79c4f..9c7519cbd 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1032,6 +1032,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting SanyoAc88 decode"); if (decodeSanyoAc88(results, offset)) return true; #endif // DECODE_SANYO_AC88 +#if DECODE_BOSE + DPRINTLN("Attempting Bose decode"); + if (decodeBose(results, offset)) return true; +#endif // DECODE_BOSE // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index e748cde32..1a883d509 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -762,6 +762,10 @@ class IRrecv { bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kKelonBits, const bool strict = true); #endif // DECODE_KELON +#if DECODE_BOSE + bool decodeBose(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kBoseBits, const bool strict = true); +#endif // DECODE_BOSE }; #endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 977c964e6..df1d213a7 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -783,6 +783,13 @@ #define SEND_KELON _IR_ENABLE_DEFAULT_ #endif // SEND_KELON +#ifndef DECODE_BOSE +#define DECODE_BOSE _IR_ENABLE_DEFAULT_ +#endif // DECODE_BOSE +#ifndef SEND_BOSE +#define SEND_BOSE _IR_ENABLE_DEFAULT_ +#endif // SEND_BOSE + #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 || \ @@ -943,8 +950,9 @@ enum decode_type_t { KELON, TROTEC_3550, SANYO_AC88, // 105 + BOSE, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = SANYO_AC88, + kLastDecodeType = BOSE, }; // Message lengths & required repeat values @@ -1186,6 +1194,7 @@ const uint16_t kVoltasStateLength = 10; const uint16_t kMilesTag2ShotBits = 14; const uint16_t kMilesTag2MsgBits = 24; const uint16_t kMilesMinRepeat = 0; +const uint16_t kBoseBits = 16; // Legacy defines. (Deprecated) diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 28e6b634b..4814bd431 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -612,6 +612,7 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case DENON: case SHARP: return 15; + case BOSE: case DISH: case GICABLE: case JVC: @@ -790,6 +791,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendAiwaRCT501(data, nbits, min_repeat); break; #endif +#if SEND_BOSE + case BOSE: + sendBose(data, nbits, min_repeat); + break; +#endif // SEND_BOSE #if SEND_CARRIER_AC case CARRIER_AC: sendCarrierAC(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index e906178f9..c20fc64bc 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -733,6 +733,10 @@ class IRsend { void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits, const uint16_t repeat = kNoRepeat); #endif // SEND_KELON +#if SEND_BOSE + void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits, + const uint16_t repeat = kNoRepeat); +#endif // SEND_BOSE protected: #ifdef UNIT_TEST diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 486e57b0f..a7bd7de29 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -292,5 +292,6 @@ const PROGMEM char *kAllProtocolNamesStr = D_STR_KELON "\x0" D_STR_TROTEC_3550 "\x0" D_STR_SANYO_AC88 "\x0" + D_STR_BOSE "\x0" ///< New protocol strings should be added just above this line. "\x0"; ///< This string requires double null termination. diff --git a/src/ir_Bose.cpp b/src/ir_Bose.cpp new file mode 100644 index 000000000..a57d125b3 --- /dev/null +++ b/src/ir_Bose.cpp @@ -0,0 +1,69 @@ +// Copyright 2021 parsnip42 +// Copyright 2021 David Conran + +/// @file +/// @brief Support for Bose protocols. +/// @note Currently only tested against Bose TV Speaker. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1579 + +// Supports: +// Brand: Bose, Model: Bose TV Speaker + +#include "IRrecv.h" +#include "IRsend.h" + +const uint16_t kBoseHdrMark = 1100; +const uint16_t kBoseHdrSpace = 1350; +const uint16_t kBoseBitMark = 555; +const uint16_t kBoseOneSpace = 1435; +const uint16_t kBoseZeroSpace = 500; +const uint32_t kBoseGap = kDefaultMessageGap; +const uint16_t kBoseFreq = 38; + +#if SEND_BOSE +/// Send a Bose formatted message. +/// Status: STABLE / Known working. +/// @param[in] data The message to be sent. +/// @param[in] nbits The number of bits of message to be sent. +/// @param[in] repeat The number of times the command is to be repeated. +void IRsend::sendBose(const uint64_t data, const uint16_t nbits, + const uint16_t repeat) { + sendGeneric(kBoseHdrMark, kBoseHdrSpace, + kBoseBitMark, kBoseOneSpace, + kBoseBitMark, kBoseZeroSpace, + kBoseBitMark, kBoseGap, + data, nbits, kBoseFreq, false, + repeat, kDutyDefault); +} +#endif // SEND_BOSE + +#if DECODE_BOSE +/// Decode the supplied Bose formatted message. +/// Status: STABLE / Known working. +/// @param[in,out] results Ptr to the data to decode & where to store the 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. +bool IRrecv::decodeBose(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kBoseBits) return false; + + if (!matchGeneric(results->rawbuf + offset, &(results->value), + results->rawlen - offset, nbits, + kBoseHdrMark, kBoseHdrSpace, + kBoseBitMark, kBoseOneSpace, + kBoseBitMark, kBoseZeroSpace, + kBoseBitMark, kBoseGap, true, + kUseDefTol, 0, false)) { + return false; + } + + // + results->decode_type = decode_type_t::BOSE; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_BOSE diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 1e1896b87..b55b87abc 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_BOSE +#define D_STR_BOSE "BOSE" +#endif // D_STR_BOSE #ifndef D_STR_CARRIER_AC #define D_STR_CARRIER_AC "CARRIER_AC" #endif // D_STR_CARRIER_AC diff --git a/test/ir_Bose_test.cpp b/test/ir_Bose_test.cpp new file mode 100644 index 000000000..bf3e18c91 --- /dev/null +++ b/test/ir_Bose_test.cpp @@ -0,0 +1,108 @@ +// Copyright 2021 parsnip42 +// 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" + + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("BOSE", typeToString(decode_type_t::BOSE)); + ASSERT_EQ(decode_type_t::BOSE, strToDecodeType("BOSE")); + ASSERT_FALSE(hasACState(decode_type_t::BOSE)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::BOSE)); + ASSERT_EQ(kBoseBits, IRsend::defaultBits(decode_type_t::BOSE)); + ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::BOSE)); +} + +// Tests for sendBose(). + +// Test sending typical data only. +TEST(TestSendBose, SendDataOnly) { + IRsendTest irsend(kGpioUnused); + irsend.begin(); + irsend.sendBose(0xCD32); + EXPECT_EQ("f38000d50" + "m1100s1350m555s500m555s1435m555s500m555s500m555s1435m555s1435" + "m555s500m555s500m555s1435m555s500m555s1435m555s1435m555s500" + "m555s500m555s1435m555s1435m555s100000", + irsend.outputStr()); +} + +// Decode normal Bose messages. +TEST(TestDecodeBose, SyntheticSelfDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Synthesised 16-bit Bose message (TV Speaker Power On). + irsend.reset(); + irsend.sendBose(0xCD32); + irsend.makeDecodeResult(); + + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(BOSE, irsend.capture.decode_type); + EXPECT_EQ(kBoseBits, irsend.capture.bits); + EXPECT_EQ(0xCD32, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +// Decode normal Bose messages. +TEST(TestDecodeBose, RealMessageDecode1) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + + // Real-life Bose code from an actual capture/decode (TV Speaker Power On). + irsend.reset(); + + const uint16_t rawData_0[35] = { + 942, 1558, + 442, 558, 442, 1502, 494, 534, 466, 560, 440, 1530, 468, 1532, 466, + 558, 440, 504, 496, 1558, 440, 534, 466, 1556, 442, 1558, 440, 558, + 440, 534, 466, 1556, 442, 1558, 440 + }; + + irsend.sendRaw(rawData_0, 35, 38000); + irsend.makeDecodeResult(); + + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(BOSE, irsend.capture.decode_type); + EXPECT_EQ(kBoseBits, irsend.capture.bits); + EXPECT_EQ(0xCD32, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +// Decode normal Bose messages. +TEST(TestDecodeBose, RealMessageDecode2) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + + irsend.begin(); + + // Real-life Bose code from an actual capture/decode (TV Speaker Mute). + irsend.reset(); + + const uint16_t rawData_0[35] = { + 1024, 1504, + 496, 528, 472, 480, 520, 502, 496, 506, 494, 502, 496, 502, 498, + 502, 498, 1500, 498, 1502, 496, 1504, 496, 1502, 496, 1504, 494, + 1472, 524, 1504, 468, 1556, 442, 532, 468, + }; + + irsend.sendRaw(rawData_0, 35, 38000); + irsend.makeDecodeResult(); + + EXPECT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(BOSE, irsend.capture.decode_type); + EXPECT_EQ(kBoseBits, irsend.capture.bits); + EXPECT_EQ(0x7F80, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +}