diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index d21c2a0a4..b1bb6dddf 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -1169,6 +1169,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting GORENJE decode"); if (decodeGorenje(results, offset)) return true; #endif // DECODE_GORENJE +#if DECODE_WOWWEE + DPRINTLN("Attempting WOWWEE decode"); + if (decodeWowwee(results, offset)) return true; +#endif // DECODE_WOWWEE // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index 2fc751dff..f3d33575c 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -865,6 +865,12 @@ class IRrecv { const uint16_t nbits = kBosch144Bits, const bool strict = true); #endif // DECODE_BOSCH144 +#if DECODE_WOWWEE + bool decodeWowwee(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kWowweeBits, + const bool strict = true); +#endif // DECODE_WOWWEE }; #endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index aee9b831a..b58369fab 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -931,6 +931,13 @@ #define SEND_GORENJE _IR_ENABLE_DEFAULT_ #endif // SEND_GORENJE +#ifndef DECODE_WOWWEE +#define DECODE_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // DECODE_WOWWEE +#ifndef SEND_WOWWEE +#define SEND_WOWWEE _IR_ENABLE_DEFAULT_ +#endif // SEND_WOWWEE + #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 || \ @@ -1112,8 +1119,9 @@ enum decode_type_t { SANYO_AC152, DAIKIN312, GORENJE, + WOWWEE, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = GORENJE, + kLastDecodeType = WOWWEE, }; // Message lengths & required repeat values @@ -1387,6 +1395,8 @@ const uint16_t kWhirlpoolAcStateLength = 21; const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8; const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; const uint16_t kWhynterBits = 32; +const uint16_t kWowweeBits = 11; +const uint16_t kWowweeDefaultRepeat = kNoRepeat; const uint8_t kVestelAcBits = 56; const uint16_t kXmpBits = 64; const uint16_t kZepealBits = 16; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index ff10066ce..53aaaf1d5 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -605,6 +605,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { case MULTIBRACKETS: case GORENJE: return 8; + case WOWWEE: + return 11; case RC5: case SYMPHONY: return 12; @@ -1120,6 +1122,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendWhynter(data, nbits, min_repeat); break; #endif +#if SEND_WOWWEE + case WOWWEE: + sendWowwee(data, nbits, min_repeat); + break; +#endif // SEND_WOWWEE #if SEND_XMP case XMP: sendXmp(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index 9f18ae07a..48b82f062 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -870,6 +870,10 @@ class IRsend { const uint16_t nbytes = kBosch144StateLength, const uint16_t repeat = kNoRepeat); #endif // SEND_BOSCH144 +#if SEND_WOWWEE + void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits, + const uint16_t repeat = kWowweeDefaultRepeat); +#endif // SEND_WOWWEE protected: #ifdef UNIT_TEST diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 8ae921fab..e21e5a3f8 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -549,6 +549,8 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0" COND(DECODE_GORENJE || SEND_GORENJE, D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0" + COND(DECODE_WOWWEE || SEND_WOWWEE, + D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0" ///< New protocol (macro) strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/src/ir_Wowwee.cpp b/src/ir_Wowwee.cpp new file mode 100644 index 000000000..d999f0c4b --- /dev/null +++ b/src/ir_Wowwee.cpp @@ -0,0 +1,91 @@ +// Copyright 2022 David Conran + +/// @file +/// @brief Support for WowWee RoboRapter protocol +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues1938 + +// Supports: +// Brand: WowWee, Model: RoboRapter-X + +// WowWee RoboRapter-X messages +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1938#issuecomment-1367968228 +// +// Button Code +// ====== ===== +// Left 0x180 +// Forward 0x186 +// Backward 0x187 +// Right 0x188 +// Stop 0x18E +// Head Counterclockwise 0x191 +// Tail Left 0x192 +// Tail Right 0x193 +// Head Clockwise 0x194 +// Guard Mode 0x1B0 +// Roam 0x1B1 +// Cautious Mood 0x1B2 +// Playful Mood 0x1B3 +// Hunting Mood 0x1B4 +// Demo 0x1D0 +// Bite 0x1D1 + +#include +#include "IRrecv.h" +#include "IRsend.h" +#include "IRutils.h" + +// Constants +const uint16_t kWowweeHdrMark = 6684; +const uint16_t kWowweeHdrSpace = 723; +const uint16_t kWowweeBitMark = 912; +const uint16_t kWowweeOneSpace = 3259; +const uint16_t kWowweeZeroSpace = kWowweeHdrSpace; +const uint16_t kWowweeFreq = 38000; // Hz. (Just a guess) + + +#if SEND_WOWWEE +/// Send a WowWee formatted message. +/// Status: STABLE / Confirmed working with real device. +/// @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::sendWowwee(uint64_t data, uint16_t nbits, uint16_t repeat) { + sendGeneric(kWowweeHdrMark, kWowweeHdrSpace, + kWowweeBitMark, kWowweeOneSpace, + kWowweeBitMark, kWowweeZeroSpace, + kWowweeBitMark, kDefaultMessageGap, data, + nbits, kWowweeFreq, true, repeat, 33); +} +#endif // SEND_WOWWEE + +#if DECODE_WOWWEE +/// Decode the supplied WowWee message. +/// Status: STABLE / Confirmed working with real device. +/// @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::decodeWowwee(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + if (strict && nbits != kWowweeBits) + return false; // We expect Wowwee to be a certain sized message. + + uint64_t data = 0; + + // Match Header + Data + Footer + if (!matchGeneric(results->rawbuf + offset, &data, + results->rawlen - offset, nbits, + kWowweeHdrMark, kWowweeHdrSpace, + kWowweeBitMark, kWowweeOneSpace, + kWowweeBitMark, kWowweeZeroSpace, + kWowweeBitMark, kDefaultMessageGap, true)) return false; + // Success + results->bits = nbits; + results->value = data; + results->decode_type = WOWWEE; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_WOWWEE diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 5a54baeff..e54c3a888 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -1105,6 +1105,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_WHYNTER #define D_STR_WHYNTER "WHYNTER" #endif // D_STR_WHYNTER +#ifndef D_STR_WOWWEE +#define D_STR_WOWWEE "WOWWEE" +#endif // D_STR_WOWWEE #ifndef D_STR_XMP #define D_STR_XMP "XMP" #endif // D_STR_XMP diff --git a/test/ir_Wowwee_test.cpp b/test/ir_Wowwee_test.cpp new file mode 100644 index 000000000..e74f61149 --- /dev/null +++ b/test/ir_Wowwee_test.cpp @@ -0,0 +1,103 @@ +// Copyright 2022 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("WOWWEE", typeToString(decode_type_t::WOWWEE)); + ASSERT_EQ(decode_type_t::WOWWEE, strToDecodeType("WOWWEE")); + ASSERT_FALSE(hasACState(decode_type_t::WOWWEE)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::WOWWEE)); + ASSERT_EQ(kWowweeBits, IRsend::defaultBits(decode_type_t::WOWWEE)); + ASSERT_EQ(kWowweeDefaultRepeat, IRsend::minRepeats(decode_type_t::WOWWEE)); +} + +// Tests for sendWowwee(). +// Test sending typical data only. +TEST(TestSendWowwee, SendDataOnly) { + IRsendTest irsend(kGpioUnused); + irsend.begin(); + + irsend.reset(); + irsend.sendWowwee(0x186); // Nikai TV Power Off. + EXPECT_EQ( + "f38000d33" + "m6684s723" + "m912s723m912s723m912s3259m912s3259m912s723m912s723m912s723m912s723" + "m912s3259m912s3259m912s723m912s100000", + irsend.outputStr()); + + irsend.reset(); +} + +// Tests for decodeWowwee(). + +// Decode normal Wowwee messages. +TEST(TestDecodeWowwee, RealDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1938#issue-1513240242 + const uint16_t rawForward[25] = { + 6684, 740, 918, 724, 942, 724, 918, 3250, 870, 3268, 872, 770, 940, 690, + 942, 688, 942, 738, 942, 3250, 868, 3268, 872, 732, 918 + }; // UNKNOWN 7469BF81 + irsend.reset(); + irsend.sendRaw(rawForward, 25, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::WOWWEE, irsend.capture.decode_type); + EXPECT_EQ(kWowweeBits, irsend.capture.bits); + EXPECT_EQ(0x186, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x0, irsend.capture.address); + + // Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1938#issue-1513240242 + const uint16_t rawLeft[25] = { + 6630, 764, 868, 762, 892, 788, 866, 3324, 792, 3348, 818, 760, 866, 788, + 894, 772, 892, 750, 870, 786, 920, 750, 864, 776, 868 + }; // UNKNOWN 28A1120F + irsend.reset(); + irsend.sendRaw(rawLeft, 25, 38); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::WOWWEE, irsend.capture.decode_type); + EXPECT_EQ(kWowweeBits, irsend.capture.bits); + EXPECT_EQ(0x180, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x0, irsend.capture.address); +} + +// Decode normal repeated Wowwee messages. +TEST(TestDecodeWowwee, SyntheticDecode) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + + // Normal Wowwee 11-bit message. + irsend.reset(); + irsend.sendWowwee(0x186); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::WOWWEE, irsend.capture.decode_type); + EXPECT_EQ(kWowweeBits, irsend.capture.bits); + EXPECT_EQ(0x186, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x0, irsend.capture.address); + + // Normal Wowwee 11-bit message. + irsend.reset(); + irsend.sendWowwee(0x180); + irsend.makeDecodeResult(); + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::WOWWEE, irsend.capture.decode_type); + EXPECT_EQ(kWowweeBits, irsend.capture.bits); + EXPECT_EQ(0x180, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.command); + EXPECT_EQ(0x0, irsend.capture.address); +}