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);
+}