diff --git a/SupportedProtocols.md b/SupportedProtocols.md index c242980f..5267fe1a 100644 --- a/SupportedProtocols.md +++ b/SupportedProtocols.md @@ -50,6 +50,7 @@ | [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | HC3000 Projector
KM14A 0179213 remote
MS-GK24VA A/C
TV | | Yes | | [Mitsubishi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.cpp) | **[Mitsubishi Electric](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Mitsubishi.h)** | 001CP T7WE10714 remote
KPOA remote
MSH-A24WV / MUH-A24WV A/C
PEAD-RP71JAA Ducted A/C | | Yes | | [MitsubishiHeavy](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.cpp) | **[Mitsubishi Heavy Industries](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_MitsubishiHeavy.h)** | RKX502A001C remote
RLA502A700B remote
SRKxxZJ-S A/C
SRKxxZM-S A/C
SRKxxZMXA-S A/C | | Yes | +| [Multibrackets](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Multibrackets.cpp) | **Multibrackets** | Motorized Swing mount large - 4500 | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Aloka](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | SleepyLights LED Lamp | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Toshiba](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | 42TL838 LCD TV | | - | | [NEC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.cpp) | **[Yamaha](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_NEC.h)** | RAV561 remote
RXV585B A/V Receiver | | - | @@ -134,6 +135,7 @@ - MITSUBISHI_AC - MITSUBISHI_HEAVY_152 - MITSUBISHI_HEAVY_88 +- MULTIBRACKETS - MWM - NEC - NEC_LIKE diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 63526ec2..7d82c3a9 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -797,6 +797,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, DPRINTLN("Attempting Doshisha decode"); if (decodeDoshisha(results, offset)) return true; #endif // DECODE_DOSHISHA +#if DECODE_MULTIBRACKETS + DPRINTLN("Attempting Multibrackets decode"); + if (decodeMultibrackets(results, offset)) return true; +#endif // DECODE_MULTIBRACKETS // Typically new protocols are added above this line. } #if DECODE_HASH diff --git a/src/IRrecv.h b/src/IRrecv.h index 0168225e..a5c52c15 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -599,10 +599,16 @@ class IRrecv { const bool strict = true); #endif // DECODE_DELONGHI_AC #if DECODE_DOSHISHA -bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, - const uint16_t nbits = kDoshishaBits, - const bool strict = true); + bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset, + const uint16_t nbits = kDoshishaBits, + const bool strict = true); #endif // DECODE_DOSHISHA +#if DECODE_MULTIBRACKETS + bool decodeMultibrackets(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kMultibracketsBits, + const bool strict = true); +#endif // DECODE_MULTIBRACKETS }; #endif // IRRECV_H_ diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index dcef3b78..d663490b 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -614,6 +614,12 @@ #define SEND_DOSHISHA _IR_ENABLE_DEFAULT_ #endif // SEND_DOSHISHA +#ifndef DECODE_MULTIBRACKETS +#define DECODE_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // DECODE_MULTIBRACKETS +#ifndef SEND_MULTIBRACKETS +#define SEND_MULTIBRACKETS _IR_ENABLE_DEFAULT_ +#endif // SEND_MULTIBRACKETS #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 || \ @@ -743,8 +749,9 @@ enum decode_type_t { AIRWELL, DELONGHI_AC, // 80 DOSHISHA, + MULTIBRACKETS, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = DOSHISHA, + kLastDecodeType = MULTIBRACKETS, }; // Message lengths & required repeat values @@ -871,6 +878,8 @@ const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; const uint16_t kMitsubishiHeavy152StateLength = 19; const uint16_t kMitsubishiHeavy152Bits = kMitsubishiHeavy152StateLength * 8; const uint16_t kMitsubishiHeavy152MinRepeat = kNoRepeat; +const uint16_t kMultibracketsBits = 8; +const uint16_t kMultibracketsDefaultRepeat = kSingleRepeat; const uint16_t kNikaiBits = 24; const uint16_t kNECBits = 32; const uint16_t kNeoclimaStateLength = 12; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index c8a7d7c0..35efb523 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -607,6 +607,7 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { case MITSUBISHI: case MITSUBISHI2: case MITSUBISHI_AC: + case MULTIBRACKETS: case SHERWOOD: case SYMPHONY: case TOSHIBA_AC: @@ -634,6 +635,8 @@ uint16_t IRsend::minRepeats(const decode_type_t protocol) { // int16_t: The number of bits. uint16_t IRsend::defaultBits(const decode_type_t protocol) { switch (protocol) { + case MULTIBRACKETS: + return 8; case SYMPHONY: return 11; case RC5: @@ -896,6 +899,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data, sendMitsubishi2(data, nbits, min_repeat); break; #endif +#if SEND_MULTIBRACKETS + case MULTIBRACKETS: + sendMultibrackets(data, nbits, min_repeat); + break; +#endif #if SEND_NIKAI case NIKAI: sendNikai(data, nbits, min_repeat); diff --git a/src/IRsend.h b/src/IRsend.h index 09a17de6..b4d710bf 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -582,6 +582,11 @@ class IRsend { const uint16_t repeat = kNoRepeat); uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0); #endif // SEND_DOSHISHA +#if SEND_MULTIBRACKETS + void sendMultibrackets(const uint64_t data, + const uint16_t nbits = kMultibracketsBits, + const uint16_t repeat = kMultibracketsDefaultRepeat); +#endif protected: #ifdef UNIT_TEST diff --git a/src/IRtext.cpp b/src/IRtext.cpp index 05806d6d..d3c3973e 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -249,5 +249,6 @@ const PROGMEM char *kAllProtocolNamesStr = D_STR_AIRWELL "\x0" D_STR_DELONGHI_AC "\x0" D_STR_DOSHISHA "\x0" + D_STR_MULTIBRACKETS "\x0" // New protocol strings should be added just above this line. "\x0"; // This string requires double null termination. diff --git a/src/ir_Multibrackets.cpp b/src/ir_Multibrackets.cpp new file mode 100644 index 00000000..0a0dfbe7 --- /dev/null +++ b/src/ir_Multibrackets.cpp @@ -0,0 +1,128 @@ +// Copyright 2020 David Conran + +#include "IRrecv.h" +#include "IRsend.h" + +// Multibrackets protocol. +// +// Supports: +// Brand: Multibrackets, Model: Motorized Swing mount large - 4500 + +const uint16_t kMultibracketsTick = 5000; // uSeconds +const uint16_t kMultibracketsHdrMark = 3 * kMultibracketsTick; // uSeconds +const uint16_t kMultibracketsFooterSpace = 6 * kMultibracketsTick; // uSeconds +const uint8_t kMultibracketsTolerance = 5; // Percent +const uint16_t kMultibracketsFreq = 38000; // Hertz + +#if SEND_MULTIBRACKETS +// Send a Miltibrackets formatted message. +// +// Args: +// data: The message to be sent. +// nbits: The number of bits of the message to be sent. +// Typically kMultibracketsBits. +// repeat: The number of times the command is to be repeated. +// +// Status: BETA / Appears to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1103 +// http://info.multibrackets.com/data/common/manuals/4500_code.pdf +void IRsend::sendMultibrackets(uint64_t data, uint16_t nbits, uint16_t repeat) { + enableIROut(kMultibracketsFreq); + for (uint16_t r = 0; r <= repeat; r++) { + uint16_t bits = nbits; + // Header + mark(kMultibracketsHdrMark); + // Data + // Send 0's until we get down to a bit size we can actually manage. + while (bits > sizeof(data) * 8) { + space(kMultibracketsTick); + bits--; + } + // Send the supplied data. + for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1) + if (data & mask) // Send a 1 + mark(kMultibracketsTick); + else // Send a 0 + space(kMultibracketsTick); + // Footer + space(kMultibracketsFooterSpace); + } +} +#endif // SEND_MULTIBRACKETS + +#if DECODE_MULTIBRACKETS +// Decode the Multibrackets message. +// +// Args: +// results: Ptr to the data to decode and where to store the decode result. +// offset: The starting index to use when attempting to decode the raw data. +// Typically/Defaults to kStartOffset. +// nbits: The number of data bits to expect. Typically kMultibracketsBits. +// strict: Flag indicating if we should perform strict matching. +// Returns: +// boolean: True if it can decode it, false if it can't. +// +// Status: BETA / Appears to be working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/1103 +// http://info.multibrackets.com/data/common/manuals/4500_code.pdf +bool IRrecv::decodeMultibrackets(decode_results *results, uint16_t offset, + const uint16_t nbits, const bool strict) { + // Compliance + if (strict && nbits != kMultibracketsBits) + return false; // Doesn't match our protocol defn. + + // Check there is enough unprocessed buffer left. + if (results->rawlen < offset) return false; + + // Header + int32_t remaining = *(results->rawbuf + offset); + if (!matchAtLeast(remaining, kMultibracketsHdrMark, kMultibracketsTolerance)) + return false; + remaining -= (kMultibracketsHdrMark / kRawTick); // Remove the header. + + // We are done with the header. Onto the data. + bool bit = true; + uint16_t bitsSoFar = 0; + uint64_t data = 0; + // Keep going till we run out of message or expected bits. + while (offset <= results->rawlen && bitsSoFar < nbits) { + // Have we finished processing this rawbuf value yet? + if (remaining <= 0) { // No more possible "bits" left in this value. + // Invert the bit for next time, and move along the rawbuf. + bit = !bit; + offset++; + // Load the next data point if there is one. + if (offset <= results->rawlen) remaining = *(results->rawbuf + offset); + } else { // Look for more bits in this entry. + if (matchAtLeast(remaining, kMultibracketsTick, + kMultibracketsTolerance)) { // There is! + data <<= 1; + data += bit; + bitsSoFar++; + } + remaining -= (kMultibracketsTick / kRawTick); // Remove the "bit". + } + } + + // Compliance + if (bitsSoFar != nbits) return false; + + // Footer + if (results->rawlen <= offset && !matchAtLeast(*(results->rawbuf + offset), + kMultibracketsFooterSpace, + kMultibracketsTolerance)) + return false; + + // Success + results->decode_type = decode_type_t::MULTIBRACKETS; + results->value = data; + results->bits = nbits; + results->address = 0; + results->command = 0; + return true; +} +#endif // DECODE_MULTIBRACKETS diff --git a/src/locale/defaults.h b/src/locale/defaults.h index a04f06e5..184ccf03 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -598,6 +598,9 @@ #ifndef D_STR_MITSUBISHI_HEAVY_88 #define D_STR_MITSUBISHI_HEAVY_88 "MITSUBISHI_HEAVY_88" #endif // D_STR_MITSUBISHI_HEAVY_88 +#ifndef D_STR_MULTIBRACKETS +#define D_STR_MULTIBRACKETS "MULTIBRACKETS" +#endif // D_STR_MULTIBRACKETS #ifndef D_STR_MWM #define D_STR_MWM "MWM" #endif // D_STR_MWM diff --git a/test/ir_Multibrackets_test.cpp b/test/ir_Multibrackets_test.cpp new file mode 100644 index 00000000..7b58b333 --- /dev/null +++ b/test/ir_Multibrackets_test.cpp @@ -0,0 +1,98 @@ +// Copyright 2020 David Conran + +#include "IRac.h" +#include "IRrecv.h" +#include "IRrecv_test.h" +#include "IRsend.h" +#include "IRsend_test.h" +#include "gtest/gtest.h" + +// Tests for decodeMultibrackets(). + +// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1103 + +TEST(TestDecodeMultibrackets, RealExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // The 1 + ok keypress: + uint16_t rawData_1[7] = {20100, 20472, 15092, 30704, 20102, 20472, 15086}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData_1, 7, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::MULTIBRACKETS, irsend.capture.decode_type); + ASSERT_EQ(kMultibracketsBits, irsend.capture.bits); + EXPECT_EQ(0x87, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + // ok keypress. + const uint16_t rawData_2[11] = { + 25124, 5108, 5038, 5110, 5034, 40940, 25132, 5108, 5036, 5110, 5036}; + + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData_2, 11, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::MULTIBRACKETS, irsend.capture.decode_type); + ASSERT_EQ(kMultibracketsBits, irsend.capture.bits); + EXPECT_EQ(0xD4, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +} + +TEST(TestDecodeMultibrackets, SyntheticExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + irsend.begin(); + irsend.reset(); + irsend.sendMultibrackets(0x87); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + EXPECT_EQ(decode_type_t::MULTIBRACKETS, irsend.capture.decode_type); + EXPECT_EQ(kMultibracketsBits, irsend.capture.bits); + EXPECT_EQ(0x87, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); + + // Real data is: + // uint16_t rawData[7] = {20100, 20472, 15092, 30704, 20102, 20472, 15086}; + + EXPECT_EQ( + "f38000d50m20000s20000m15000s30000m20000s20000m15000s30000", + irsend.outputStr()); +} + +TEST(TestUtils, Housekeeping) { + ASSERT_EQ("MULTIBRACKETS", typeToString(decode_type_t::MULTIBRACKETS)); + ASSERT_EQ(decode_type_t::MULTIBRACKETS, strToDecodeType("MULTIBRACKETS")); + ASSERT_FALSE(hasACState(decode_type_t::MULTIBRACKETS)); + ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::MULTIBRACKETS)); + ASSERT_EQ(kMultibracketsBits, + IRsend::defaultBits(decode_type_t::MULTIBRACKETS)); + ASSERT_EQ(kMultibracketsDefaultRepeat, + IRsend::minRepeats(decode_type_t::MULTIBRACKETS)); +} + +TEST(TestDecodeMultibrackets, ShortNoRepeatExample) { + IRsendTest irsend(kGpioUnused); + IRrecv irrecv(kGpioUnused); + // The 1 + ok keypress: (edited to be bare minimum) + uint16_t rawData[3] = {20100, 20472, 15092}; + irsend.begin(); + irsend.reset(); + irsend.sendRaw(rawData, 3, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(decode_type_t::MULTIBRACKETS, irsend.capture.decode_type); + ASSERT_EQ(kMultibracketsBits, irsend.capture.bits); + EXPECT_EQ(0x87, irsend.capture.value); + EXPECT_EQ(0x0, irsend.capture.address); + EXPECT_EQ(0x0, irsend.capture.command); +}