mirror of
https://github.com/crankyoldgit/IRremoteESP8266.git
synced 2026-01-12 00:05:10 +08:00
Add support for Metz protocol. (#1242)
* Add `sendMetz()` & `decodeMetz()`. * Add `encodeMetz()` to handle creation of valid Metz codes from Address & Command bits (inc toggle bit) * Unit test coverage based on supplied data. Note: Protocol is a simple non-A/C one for controlling TVs etc. Fixes #1241
This commit is contained in:
@@ -864,6 +864,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
|
||||
DPRINTLN("Attempting Voltas decode");
|
||||
if (decodeVoltas(results)) return true;
|
||||
#endif // DECODE_VOLTAS
|
||||
#if DECODE_METZ
|
||||
DPRINTLN("Attempting Metz decode");
|
||||
if (decodeMetz(results, offset)) return true;
|
||||
#endif // DECODE_METZ
|
||||
// Typically new protocols are added above this line.
|
||||
}
|
||||
#if DECODE_HASH
|
||||
|
||||
@@ -673,6 +673,11 @@ bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kZepealBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_ZEPEAL
|
||||
#if DECODE_METZ
|
||||
bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMetzBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_METZ
|
||||
};
|
||||
|
||||
#endif // IRRECV_H_
|
||||
|
||||
@@ -677,6 +677,13 @@
|
||||
#define SEND_VOLTAS _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_VOLTAS
|
||||
|
||||
#ifndef DECODE_METZ
|
||||
#define DECODE_METZ _IR_ENABLE_DEFAULT_
|
||||
#endif // DECODE_METZ
|
||||
#ifndef SEND_METZ
|
||||
#define SEND_METZ _IR_ENABLE_DEFAULT_
|
||||
#endif // SEND_METZ
|
||||
|
||||
#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 || \
|
||||
@@ -819,8 +826,9 @@ enum decode_type_t {
|
||||
ZEPEAL,
|
||||
SANYO_AC,
|
||||
VOLTAS, // 90
|
||||
METZ,
|
||||
// Add new entries before this one, and update it to point to the last entry.
|
||||
kLastDecodeType = VOLTAS,
|
||||
kLastDecodeType = METZ,
|
||||
};
|
||||
|
||||
// Message lengths & required repeat values
|
||||
@@ -936,6 +944,8 @@ const uint16_t kLg32Bits = 32;
|
||||
const uint16_t kLgDefaultRepeat = kNoRepeat;
|
||||
const uint16_t kLutronBits = 35;
|
||||
const uint16_t kMagiquestBits = 56;
|
||||
const uint16_t kMetzBits = 19;
|
||||
const uint16_t kMetzMinRepeat = kNoRepeat;
|
||||
const uint16_t kMideaBits = 48;
|
||||
const uint16_t kMideaMinRepeat = kNoRepeat;
|
||||
const uint16_t kMidea24Bits = 24;
|
||||
|
||||
@@ -617,6 +617,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
|
||||
case MITSUBISHI2:
|
||||
case ZEPEAL:
|
||||
return 16;
|
||||
case METZ:
|
||||
return 19;
|
||||
case RC6:
|
||||
case SONY:
|
||||
case SONY_38K:
|
||||
@@ -987,7 +989,12 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
|
||||
case ZEPEAL:
|
||||
sendZepeal(data, nbits, min_repeat);
|
||||
break;
|
||||
#endif
|
||||
#endif // SEND_ZEPEAL
|
||||
#if SEND_METZ
|
||||
case METZ:
|
||||
sendMetz(data, nbits, min_repeat);
|
||||
break;
|
||||
#endif // SEND_METZ
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -634,12 +634,19 @@ class IRsend {
|
||||
void sendZepeal(const uint64_t data,
|
||||
const uint16_t nbits = kZepealBits,
|
||||
const uint16_t repeat = kZepealMinRepeat);
|
||||
#endif
|
||||
#endif // SEND_ZEPEAL
|
||||
#if SEND_VOLTAS
|
||||
void sendVoltas(const unsigned char data[],
|
||||
const uint16_t nbytes = kVoltasStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_VOLTAS
|
||||
#if SEND_METZ
|
||||
void sendMetz(const uint64_t data,
|
||||
const uint16_t nbits = kMetzBits,
|
||||
const uint16_t repeat = kMetzMinRepeat);
|
||||
static uint32_t encodeMetz(const uint8_t address, const uint8_t command,
|
||||
const bool toggle = false);
|
||||
#endif // SEND_METZ
|
||||
|
||||
protected:
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
@@ -267,5 +267,6 @@ const PROGMEM char *kAllProtocolNamesStr =
|
||||
D_STR_ZEPEAL "\x0"
|
||||
D_STR_SANYO_AC "\x0"
|
||||
D_STR_VOLTAS "\x0"
|
||||
D_STR_METZ "\x0"
|
||||
///< New protocol strings should be added just above this line.
|
||||
"\x0"; ///< This string requires double null termination.
|
||||
|
||||
101
src/ir_Metz.cpp
Normal file
101
src/ir_Metz.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Metz protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1241
|
||||
|
||||
// Supports:
|
||||
// Brand: Metz, Model: RM16 remote
|
||||
// Brand: Metz, Model: RM17 remote
|
||||
// Brand: Metz, Model: RM19 remote
|
||||
// Brand: Metz, Model: CH610 TV
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants.
|
||||
const uint16_t kMetzHdrMark = 880; ///< uSeconds.
|
||||
const uint16_t kMetzHdrSpace = 2336; ///< uSeconds.
|
||||
const uint16_t kMetzBitMark = 473; ///< uSeconds.
|
||||
const uint16_t kMetzOneSpace = 1640; ///< uSeconds.
|
||||
const uint16_t kMetzZeroSpace = 940; ///< uSeconds.
|
||||
const uint16_t kMetzFreq = 38000; ///< Hz.
|
||||
const uint8_t kMetzAddressBits = 3;
|
||||
const uint8_t kMetzCommandBits = 6;
|
||||
|
||||
#if SEND_METZ
|
||||
/// Send a Metz formatted message.
|
||||
/// Status: Beta / Needs testing against a real device.
|
||||
/// @param[in] data containing the IR command.
|
||||
/// @param[in] nbits Nr. of bits to send. usually kMetzBits
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendMetz(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kMetzHdrMark, kMetzHdrSpace, // Header
|
||||
kMetzBitMark, kMetzOneSpace, // Data
|
||||
kMetzBitMark, kMetzZeroSpace,
|
||||
kMetzBitMark, kDefaultMessageGap, // Footer.
|
||||
data, nbits, // Payload
|
||||
kMetzFreq, true, repeat, kDutyDefault);
|
||||
}
|
||||
|
||||
/// Encode a Metz address, command, and toggle bits into a code suitable
|
||||
/// for use with sendMetz().
|
||||
/// @param[in] address A 3-bit address value.
|
||||
/// @param[in] command A 6-bit command value.
|
||||
/// @param[in] toggle Should the toggle bit be set in the result?
|
||||
/// @return A 19-bit value suitable for use with `sendMetz()`.
|
||||
uint32_t IRsend::encodeMetz(const uint8_t address, const uint8_t command,
|
||||
const bool toggle) {
|
||||
return toggle << (2 * (kMetzAddressBits + kMetzCommandBits)) |
|
||||
(address & 0x7) << (2 * kMetzCommandBits + kMetzAddressBits) |
|
||||
(~address & 0x7) << (2 * kMetzCommandBits) |
|
||||
(command & 0x3F) << kMetzCommandBits |
|
||||
(~command & 0x3F);
|
||||
}
|
||||
#endif // SEND_METZ
|
||||
|
||||
#if DECODE_METZ
|
||||
/// Decode the supplied Metz message.
|
||||
/// Status: BETA / Probably works.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// @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.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeMetz(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kMetzBits) return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kMetzHdrMark, kMetzHdrSpace, // Header
|
||||
kMetzBitMark, kMetzOneSpace, // Data
|
||||
kMetzBitMark, kMetzZeroSpace,
|
||||
kMetzBitMark, kDefaultMessageGap, // Footer
|
||||
true, _tolerance, 0, true)) return false;
|
||||
|
||||
uint16_t command = GETBITS64(data, kMetzCommandBits, kMetzCommandBits);
|
||||
uint16_t address = GETBITS64(data, 2 * kMetzCommandBits + kMetzAddressBits,
|
||||
kMetzAddressBits);
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (command != invertBits(GETBITS64(data, 0, kMetzCommandBits),
|
||||
kMetzCommandBits) ||
|
||||
address != invertBits(GETBITS64(data, 2 * kMetzCommandBits,
|
||||
kMetzAddressBits),
|
||||
kMetzAddressBits)) return false;
|
||||
}
|
||||
// Success
|
||||
results->decode_type = decode_type_t::METZ;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = address;
|
||||
results->command = command;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_METZ
|
||||
@@ -601,6 +601,9 @@
|
||||
#ifndef D_STR_MAGIQUEST
|
||||
#define D_STR_MAGIQUEST "MAGIQUEST"
|
||||
#endif // D_STR_MAGIQUEST
|
||||
#ifndef D_STR_METZ
|
||||
#define D_STR_METZ "METZ"
|
||||
#endif // D_STR_METZ
|
||||
#ifndef D_STR_MIDEA
|
||||
#define D_STR_MIDEA "MIDEA"
|
||||
#endif // D_STR_MIDEA
|
||||
|
||||
95
test/ir_Metz_test.cpp
Normal file
95
test/ir_Metz_test.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2020 crankyoldgit
|
||||
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRrecv_test.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRsend_test.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
// Tests for decodeMetz().
|
||||
|
||||
TEST(TestDecodeMetz, RealExample) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
uint16_t rawbuf[41] = {
|
||||
// Header 1 0 1
|
||||
880, 2336, 462, 1656, 460, 952, 486, 1630,
|
||||
// 1 1 0 0
|
||||
462, 1654, 460, 1660, 462, 948, 488, 902,
|
||||
// 1 0 1 0
|
||||
486, 1656, 486, 926, 486, 1630, 486, 924,
|
||||
// 1 0 0 1
|
||||
488, 1630, 460, 954, 460, 950, 512, 1606,
|
||||
// 0 1 0 1
|
||||
460, 978, 436, 1656, 486, 926, 486, 1630,
|
||||
458};
|
||||
irsend.begin();
|
||||
irsend.reset();
|
||||
irsend.sendRaw(rawbuf, 41, 38);
|
||||
irsend.makeDecodeResult();
|
||||
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
ASSERT_EQ(decode_type_t::METZ, irsend.capture.decode_type);
|
||||
ASSERT_EQ(kMetzBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x5CA95, irsend.capture.value);
|
||||
EXPECT_EQ(0x2A, irsend.capture.command);
|
||||
EXPECT_EQ(0x3, irsend.capture.address);
|
||||
}
|
||||
|
||||
TEST(TestDecodeMetz, SyntheticExample) {
|
||||
IRsendTest irsend(kGpioUnused);
|
||||
IRrecv irrecv(kGpioUnused);
|
||||
irsend.begin();
|
||||
irsend.reset();
|
||||
irsend.sendMetz(0x5CA95);
|
||||
irsend.makeDecodeResult();
|
||||
|
||||
ASSERT_TRUE(irrecv.decode(&irsend.capture));
|
||||
EXPECT_EQ(decode_type_t::METZ, irsend.capture.decode_type);
|
||||
EXPECT_EQ(kMetzBits, irsend.capture.bits);
|
||||
EXPECT_EQ(0x5CA95, irsend.capture.value);
|
||||
EXPECT_EQ(0x2A, irsend.capture.command);
|
||||
EXPECT_EQ(0x3, irsend.capture.address);
|
||||
|
||||
EXPECT_EQ(
|
||||
"f38000d50"
|
||||
// 880 2336 462 1656 460 952 486 1630 462 1654 460 1660 462 948 488 902
|
||||
"m880s2336m473s1640m473s940m473s1640m473s1640m473s1640m473s940m473s940"
|
||||
// 486 1656 486 926 486 1630 486 924 488 1630 460 954 460 950 512 1606
|
||||
"m473s1640m473s940m473s1640m473s940m473s1640m473s940m473s940m473s1640"
|
||||
// 460 978 436 1656 486 926 486 1630
|
||||
"m473s940m473s1640m473s940m473s1640"
|
||||
// 458
|
||||
"m473s100000",
|
||||
irsend.outputStr());
|
||||
}
|
||||
|
||||
TEST(TestEncodeMetz, BasicTests) {
|
||||
EXPECT_EQ(0x703F, IRsend::encodeMetz(0, 0));
|
||||
EXPECT_EQ(0x703F, IRsend::encodeMetz(0, 0, false));
|
||||
EXPECT_EQ(0x4703F, IRsend::encodeMetz(0, 0, true));
|
||||
|
||||
EXPECT_EQ(0xE0BD, IRsend::encodeMetz(1, 2, false));
|
||||
EXPECT_EQ(0x4E0BD, IRsend::encodeMetz(1, 2, true));
|
||||
|
||||
EXPECT_EQ(0x38FC0, IRsend::encodeMetz(0b111, 0b111111, false));
|
||||
EXPECT_EQ(0x78FC0, IRsend::encodeMetz(0b111, 0b111111, true));
|
||||
|
||||
// Out of range.
|
||||
EXPECT_EQ(0x38FC0, IRsend::encodeMetz(255, 255, false));
|
||||
EXPECT_EQ(0x78FC0, IRsend::encodeMetz(255, 255, true));
|
||||
}
|
||||
|
||||
TEST(TestEncodeMetz, RealExamples) {
|
||||
EXPECT_EQ(0x5CA95, IRsend::encodeMetz(0x3, 0x2A, true));
|
||||
}
|
||||
|
||||
TEST(TestUtils, Housekeeping) {
|
||||
ASSERT_EQ("METZ", typeToString(decode_type_t::METZ));
|
||||
ASSERT_EQ(decode_type_t::METZ, strToDecodeType("METZ"));
|
||||
ASSERT_FALSE(hasACState(decode_type_t::METZ));
|
||||
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::METZ));
|
||||
ASSERT_EQ(kMetzBits, IRsend::defaultBits(decode_type_t::METZ));
|
||||
ASSERT_EQ(kMetzMinRepeat, IRsend::minRepeats(decode_type_t::METZ));
|
||||
}
|
||||
Reference in New Issue
Block a user