From 242a490fcdb7678b9bcabd4a094e872fac514a6c Mon Sep 17 00:00:00 2001 From: Thomas Koch Date: Sun, 24 May 2020 12:27:06 -0400 Subject: [PATCH 1/2] Debug decodeBoseWave - Debug decodeBoseWave() --- .gitignore | 3 +- IRremote.h | 14 +++- ir_BoseWave.cpp | 219 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 ir_BoseWave.cpp diff --git a/.gitignore b/.gitignore index e24b84cda..3fd5c4290 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.un~ *.sublime-project -*.sublime-workspace \ No newline at end of file +*.sublime-workspace +ir_BoseWave.cpp.bak diff --git a/IRremote.h b/IRremote.h index fe1a87029..acada93c1 100644 --- a/IRremote.h +++ b/IRremote.h @@ -79,6 +79,9 @@ #define DECODE_LEGO_PF 0 // NOT WRITTEN #define SEND_LEGO_PF 1 +#define DECODE_BOSEWAVE 1 +#define SEND_BOSEWAVE 1 + //------------------------------------------------------------------------------ // When sending a Pronto code we request to send either the "once" code // or the "repeat" code @@ -119,6 +122,7 @@ typedef DENON, PRONTO, LEGO_PF, + BOSEWAVE, } decode_type_t; @@ -251,6 +255,10 @@ class IRrecv # if DECODE_LEGO_PF bool decodeLegoPowerFunctions (decode_results *results) ; # endif +//...................................................................... +# if DECODE_BOSEWAVE + bool decodeBoseWave (decode_results *results) ; +# endif } ; //------------------------------------------------------------------------------ @@ -335,9 +343,13 @@ class IRsend # if SEND_PRONTO void sendPronto (char* code, bool repeat, bool fallback) ; # endif -//...................................................................... + //...................................................................... # if SEND_LEGO_PF void sendLegoPowerFunctions (uint16_t data, bool repeat = true) ; +# endif + //...................................................................... +# if SEND_BOSEWAVE + void sendBoseWave (unsigned char code) ; # endif } ; diff --git a/ir_BoseWave.cpp b/ir_BoseWave.cpp new file mode 100644 index 000000000..4fcc63291 --- /dev/null +++ b/ir_BoseWave.cpp @@ -0,0 +1,219 @@ +#include "IRremote.h" +#include "IRremoteInt.h" + +//============================================================================== +// BBBB OOO SSSS EEEEE +// B B O O S E +// BB B O O SSS EEEE +// B B O O S E +// BBBB OOO SSSS EEEEE +//============================================================================== +// +// Bose Wave Radio CD Remote Control +// |-------------------------------------| +// | On/Off Sleep VolUp | +// | Play/Pause Stop VolDown | +// | FM AM Aux | +// | Tune Down Tune Up Mute | +// | 1 2 3 | +// | 4 5 6 | +// |-------------------------------------| +// +// Support for Bose Wave Radio CD provided by https://github.com/uvotguy. +// +// This protocol was reverse engineered by capturing IR signals from a working +// remote. Multiple signals were captured on my oscilloscope, and the timing +// values were averaged. +// +// IR codes are 8 bits. Transmission starts with a header: a mark and a space. +// The header is followed by an 8-bit command, where a bit is a mark and a short +// space (1) or a long space (0). The command is followed by the complement of +// the command (8 bits). A transmission ends with a short mark. +// +// As seen on my trusty oscilloscope, there is no repeat code. Instead, when I +// press and hold a button on my remote, it sends a command, makes a 51.2ms space, +// and resends the command, etc, etc. +// +// It may be worth noting that these values do NOT match those in the LIRC +// remote database (http://lirc.sourceforge.net/remotes/bose/). + +#define CMD_ON_OFF 0xff +#define CMD_MUTE 0xfe +#define CMD_VOL_UP 0xfd +#define CMD_VOL_DOWN 0xfc +#define CMD_PRESET_6 0xfb +#define CMD_SLEEP 0xfa +#define CMD_FM 0xf9 +#define CMD_AUX 0xf8 +#define CMD_AM 0xf7 +#define CMD_PLAY_PAUSE 0xf6 +#define CMD_STOP 0xf5 +#define CMD_TUNE_UP 0xf4 +#define CMD_TUNE_DOWN 0xf3 +#define CMD_PRESET_1 0xf2 +#define CMD_PRESET_2 0xf1 +#define CMD_PRESET_3 0xf0 +#define CMD_PRESET_4 0xef +#define CMD_PRESET_5 0xee + +#define BOSEWAVE_BITS 8 +#define BOSEWAVE_HDR_MARK 1061 +#define BOSEWAVE_HDR_SPACE 1456 +#define BOSEWAVE_BIT_MARK 534 +#define BOSEWAVE_ONE_SPACE 468 +#define BOSEWAVE_ZERO_SPACE 1447 +#define BOSEWAVE_END_MARK 614 +#define BOSEWAVE_RPT_SPACE 51200 + +//+============================================================================= +#if SEND_BOSEWAVE +void IRsend::sendBoseWave (unsigned char code) +{ + unsigned int rawSignal[35]; + int index = 0; + // Header + rawSignal[index++] = BOSEWAVE_HDR_MARK; + rawSignal[index++] = BOSEWAVE_HDR_SPACE; + + // 8 bit command + for (unsigned char mask = 0x80; mask; mask >>= 1) { + rawSignal[index++] = BOSEWAVE_BIT_MARK; + if (code & mask) { + rawSignal[index++] = BOSEWAVE_ONE_SPACE; + } else { + rawSignal[index++] = BOSEWAVE_ZERO_SPACE; + } + } + + // 8 bit command complement + for (unsigned char mask = 0x80; mask; mask >>= 1) { + rawSignal[index++] = BOSEWAVE_BIT_MARK; + if (code & mask) { + rawSignal[index++] = BOSEWAVE_ZERO_SPACE; + } else { + rawSignal[index++] = BOSEWAVE_ONE_SPACE; + } + } + // End transmission + rawSignal[index++] = BOSEWAVE_END_MARK; + + // Transmit + this->sendRaw(rawSignal, 35, 38); +} +#endif + +//+============================================================================= +#if DECODE_BOSEWAVE +bool IRrecv::decodeBoseWave (decode_results *results) +{ + unsigned char command = 0; // Decoded command + unsigned char complement = 0; // Decoded command complement + + int index = 0; // Index in to results array + + DBG_PRINTLN("Decoding Bose Wave ..."); + + // Check we have enough data + if (irparams.rawlen < (2 * BOSEWAVE_BITS * 2) + 3) { + DBG_PRINT("\tInvalid data length found: "); + DBG_PRINTLN(results->rawlen); + return false ; + } + + // Check header "mark" + index = 1; + if (!MATCH_MARK(results->rawbuf[index], BOSEWAVE_HDR_MARK)) { + DBG_PRINT("\tInvalid Header Mark. Expecting "); + DBG_PRINT(BOSEWAVE_HDR_MARK); + DBG_PRINT(". Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + index++; + + // Check header "space" + if (!MATCH_SPACE(results->rawbuf[index], BOSEWAVE_HDR_SPACE)) { + DBG_PRINT("\tInvalid Header Space. Expecting "); + DBG_PRINT(BOSEWAVE_HDR_SPACE); + DBG_PRINT(". Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false; + } + index++; + + // Decode the data bits + for (int ii = 7; ii >= 0; ii--) { + // Check bit "mark". Mark is always the same length. + if (!MATCH_MARK(results->rawbuf[index], BOSEWAVE_BIT_MARK)) { + DBG_PRINT("\tInvalid command Mark. Expecting "); + DBG_PRINT(BOSEWAVE_BIT_MARK); + DBG_PRINT(". Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + index++; + + // Check bit "space" + if (MATCH_SPACE(results->rawbuf[index], BOSEWAVE_ONE_SPACE )) { + command |= (0x01 << ii); + } else if (MATCH_SPACE(results->rawbuf[index], BOSEWAVE_ZERO_SPACE)) { + // Nothing to do for zeroes. + } else { + DBG_PRINT("\tInvalid command Space. Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + index++; + } + + // Decode the command complement bits. We decode it here as the complement + // of the complement (0=1 and 1=0) so we can easily compare it to the command. + for (int ii = 7; ii >= 0; ii--) { + // Check bit "mark". Mark is always the same length. + if (!MATCH_MARK(results->rawbuf[index], BOSEWAVE_BIT_MARK)) { + DBG_PRINT("\tInvalid complement Mark. Expecting "); + DBG_PRINT(BOSEWAVE_BIT_MARK); + DBG_PRINT(". Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + index++; + + // Check bit "space" + if (MATCH_SPACE(results->rawbuf[index], BOSEWAVE_ONE_SPACE )) { + // Nothing to do. + } else if (MATCH_SPACE(results->rawbuf[index], BOSEWAVE_ZERO_SPACE)) { + complement |= (0x01 << ii); + } else { + DBG_PRINT("\tInvalid complement Space. Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + index++; + } + + if (command != complement) { + DBG_PRINT("\tComplement is not correct. Command=0x"); + DBG_PRINT(command, HEX); + DBG_PRINT(" Complement=0x"); + DBG_PRINTLN(complement, HEX); + return false ; + } else { + DBG_PRINTLN("\tValid command"); + } + + // Check end "mark" + if (MATCH_MARK(results->rawbuf[index], BOSEWAVE_END_MARK) == 0) { + DBG_PRINT("\tInvalid end Mark. Got "); + DBG_PRINTLN(results->rawbuf[index] * USECPERTICK); + return false ; + } + + // Success + results->bits = BOSEWAVE_BITS; + results->value = command; + results->decode_type = BOSEWAVE; + + return true; +} +#endif From 5deea03eefd43be9ed60d9fcf855431c5f09efeb Mon Sep 17 00:00:00 2001 From: Thomas Koch Date: Sun, 24 May 2020 15:06:10 -0400 Subject: [PATCH 2/2] Add Bose Wave Radio Example Sketches - Add BOSEWAVE to relevant send and receive example sketches. Add a test sketch specifically for a Bose Wave Radio. --- .gitignore | 3 +- .../BoseWaveSendDemo/BoseWaveSendDemo.ino | 109 ++++++++++++++++++ examples/IRrecvDump/IRrecvDump.ino | 3 + examples/IRrecvDumpV2/IRrecvDumpV2.ino | 1 + examples/IRremoteInfo/IRremoteInfo.ino | 1 + 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 examples/BoseWaveSendDemo/BoseWaveSendDemo.ino diff --git a/.gitignore b/.gitignore index 3fd5c4290..e24b84cda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ *.un~ *.sublime-project -*.sublime-workspace -ir_BoseWave.cpp.bak +*.sublime-workspace \ No newline at end of file diff --git a/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino b/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino new file mode 100644 index 000000000..e2f4bc004 --- /dev/null +++ b/examples/BoseWaveSendDemo/BoseWaveSendDemo.ino @@ -0,0 +1,109 @@ +#include +#include +#include + +/* + * Based on IRremote: IRsendDemo by Ken Shirriff + * + * Prompt user for a code to send. Make sure your 940-950nm IR LED is + * connected to the default digital output. Place your Bose Wave Radio + * CD in the line of sight of your LED, and send commands! + */ +void menu() { + Serial.println("0: On / Off"); + Serial.println("1: Volume Up"); + Serial.println("2: Volume Down"); + Serial.println("3: Tune Up"); + Serial.println("4: Tune Down"); + Serial.println("5: AM"); + Serial.println("6: FM"); + Serial.println("7: Preset 1"); + Serial.println("8: Preset 2"); + Serial.println("9: Preset 3"); + Serial.println("a: Preset 4"); + Serial.println("b: Preset 5"); + Serial.println("c: Preset 6"); + Serial.println("d: Mute"); + Serial.println("e: Play/Pause"); + Serial.println("f: Stop"); + Serial.println("g: Aux"); + Serial.println("h: Sleep"); +} + +IRsend irsend; +bool prompt; + +void setup() +{ + Serial.begin(9600); + prompt = true; +} + +void loop() { + if (prompt) { + menu(); + } + prompt = false; + + if (Serial.available()) { + int answer = Serial.read(); + if (answer == -1) { + delay(300); + } else if (answer == 48) { // 0 + irsend.sendBoseWave(0xFF); // On/Off + prompt = true; + } else if (answer == 49) { // 1 + irsend.sendBoseWave(0xFD); // Volume Up + prompt = true; + } else if (answer == 50) { // 2 + irsend.sendBoseWave(0xFC); // Volume Down + prompt = true; + } else if (answer == 51) { // 3 + irsend.sendBoseWave(0xF4); // Tune Up + prompt = true; + } else if (answer == 52) { // 4 + irsend.sendBoseWave(0xF3); // Tune Down + prompt = true; + } else if (answer == 53) { // 5 + irsend.sendBoseWave(0xF7); // AM + prompt = true; + } else if (answer == 54) { // 6 + irsend.sendBoseWave(0xF9); // FM + prompt = true; + } else if (answer == 55) { // 7 + irsend.sendBoseWave(0xF2); // Preset 1 + prompt = true; + } else if (answer == 56) { // 8 + irsend.sendBoseWave(0xF1); // Preset 2 + prompt = true; + } else if (answer == 57) { // 9 + irsend.sendBoseWave(0xF0); // Preset 3 + prompt = true; + } else if (answer == 97) { // a + irsend.sendBoseWave(0xEF); // Preset 4 + prompt = true; + } else if (answer == 98) { // b + irsend.sendBoseWave(0xEE); // Preset 5 + prompt = true; + } else if (answer == 99) { // c + irsend.sendBoseWave(0xFB); // Preset 6 + prompt = true; + } else if (answer == 100) { // d + irsend.sendBoseWave(0xFE); // Mute + prompt = true; + } else if (answer == 101) { // e + irsend.sendBoseWave(0xF6); // Pause + prompt = true; + } else if (answer == 102) { // f + irsend.sendBoseWave(0xF5); // Stop + prompt = true; + } else if (answer == 103) { // g + irsend.sendBoseWave(0xF8); // Aux + prompt = true; + } else if (answer == 104) { // h + irsend.sendBoseWave(0xFA); // Sleep + prompt = true; + } + delay(300); + } +} diff --git a/examples/IRrecvDump/IRrecvDump.ino b/examples/IRrecvDump/IRrecvDump.ino index fa25cd8ea..d2d10133c 100644 --- a/examples/IRrecvDump/IRrecvDump.ino +++ b/examples/IRrecvDump/IRrecvDump.ino @@ -65,6 +65,9 @@ void dump(decode_results *results) { else if (results->decode_type == WHYNTER) { Serial.print("Decoded Whynter: "); } + else if (results.decode_type == BOSEWAVE) { + Serial.print("Decoded Bose Wave Radio / CD: "); + } Serial.print(results->value, HEX); Serial.print(" ("); Serial.print(results->bits, DEC); diff --git a/examples/IRrecvDumpV2/IRrecvDumpV2.ino b/examples/IRrecvDumpV2/IRrecvDumpV2.ino index 725612735..af0bd4fe0 100644 --- a/examples/IRrecvDumpV2/IRrecvDumpV2.ino +++ b/examples/IRrecvDumpV2/IRrecvDumpV2.ino @@ -56,6 +56,7 @@ void encoding (decode_results *results) case AIWA_RC_T501: Serial.print("AIWA_RC_T501"); break ; case PANASONIC: Serial.print("PANASONIC"); break ; case DENON: Serial.print("Denon"); break ; + case BOSEWAVE: Serial.print("BOSEWAVE"); break ; } } diff --git a/examples/IRremoteInfo/IRremoteInfo.ino b/examples/IRremoteInfo/IRremoteInfo.ino index 2a269d948..75f521663 100644 --- a/examples/IRremoteInfo/IRremoteInfo.ino +++ b/examples/IRremoteInfo/IRremoteInfo.ino @@ -188,6 +188,7 @@ void dumpProtocols() { Serial.print(F("DISH: ")); printSendEnabled(SEND_DISH); printDecodeEnabled(DECODE_DISH); Serial.print(F("SHARP: ")); printSendEnabled(SEND_SHARP); printDecodeEnabled(DECODE_SHARP); Serial.print(F("DENON: ")); printSendEnabled(SEND_DENON); printDecodeEnabled(DECODE_DENON); + Serial.print(F("BOSEWAVE: ")); printSendEnabled(SEND_BOSEWAVE); printDecodeEnabled(DECODE_BOSEWAVE); Serial.print(F("PRONTO: ")); printSendEnabled(SEND_PRONTO); Serial.println(F("(Not Applicable)")); }