Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental Bose remote support #1579

Merged
merged 5 commits into from
Aug 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_
11 changes: 10 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 || \
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
69 changes: 69 additions & 0 deletions src/ir_Bose.cpp
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
108 changes: 108 additions & 0 deletions test/ir_Bose_test.cpp
Original file line number Diff line number Diff line change
@@ -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);
}