Add support for Multibrackets protocol. (#1106)

* Basic send/decode support only. It's a very simple protocol.
* Unit tests etc.

Fixes #1103
This commit is contained in:
David Conran
2020-05-12 23:46:10 +10:00
committed by GitHub
parent 8a1a0055f1
commit 2799b843c5
10 changed files with 268 additions and 4 deletions

View File

@@ -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<BR>KM14A 0179213 remote<BR>MS-GK24VA A/C<BR>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<BR>KPOA remote<BR>MSH-A24WV / MUH-A24WV A/C<BR>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<BR>RLA502A700B remote<BR>SRKxxZJ-S A/C<BR>SRKxxZM-S A/C<BR>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<BR>RXV585B A/V Receiver | | - |
@@ -134,6 +135,7 @@
- MITSUBISHI_AC
- MITSUBISHI_HEAVY_152
- MITSUBISHI_HEAVY_88
- MULTIBRACKETS
- MWM
- NEC
- NEC_LIKE

View File

@@ -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

View File

@@ -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_

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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.

128
src/ir_Multibrackets.cpp Normal file
View File

@@ -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

View File

@@ -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

View File

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