Skip to content

Commit

Permalink
Experimental support for Teknopoint A/C protocol
Browse files Browse the repository at this point in the history
* Basic support added via `sendTeknopoint()` & `decodeTeknopoint()`
* Unit test coverage for the additions/changes.
* Minor code style cleanup.

Note: Bit ordering not yet determined. May change without notice.

For #1486
  • Loading branch information
crankyoldgit committed Jun 24, 2021
1 parent 609abce commit 91dc3ea
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting XMP decode");
if (decodeXmp(results, offset, kXmpBits)) return true;
#endif // DECODE_XMP
#if DECODE_TEKNOPOINT
DPRINTLN("Attempting Teknopoint decode");
if (decodeTeknopoint(results, offset)) return true;
#endif // DECODE_TEKNOPOINT
// Typically new protocols are added above this line.
}
#if DECODE_HASH
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,11 @@ class IRrecv {
bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kTrumaBits, const bool strict = true);
#endif // DECODE_TRUMA
#if DECODE_TEKNOPOINT
bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kTeknopointBits,
const bool strict = true);
#endif // DECODE_TEKNOPOINT
};

#endif // IRRECV_H_
14 changes: 12 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,13 @@
#define SEND_HAIER_AC176 _IR_ENABLE_DEFAULT_
#endif // SEND_HAIER_AC176

#ifndef DECODE_TEKNOPOINT
#define DECODE_TEKNOPOINT _IR_ENABLE_DEFAULT_
#endif // DECODE_TEKNOPOINT
#ifndef SEND_TEKNOPOINT
#define SEND_TEKNOPOINT _IR_ENABLE_DEFAULT_
#endif // SEND_TEKNOPOINT

#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 || \
Expand All @@ -766,7 +773,7 @@
DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \
DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC || \
DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176)
DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || DECODE_TEKNOPOINT)
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
// you might also want to add the protocol to hasACState function
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
Expand Down Expand Up @@ -907,8 +914,9 @@ enum decode_type_t {
XMP,
TRUMA, // 100
HAIER_AC176,
TEKNOPOINT,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = HAIER_AC176,
kLastDecodeType = TEKNOPOINT,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -1118,6 +1126,8 @@ const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8;
const uint16_t kTcl112AcDefaultRepeat = kNoRepeat;
const uint16_t kTecoBits = 35;
const uint16_t kTecoDefaultRepeat = kNoRepeat;
const uint16_t kTeknopointStateLength = 14;
const uint16_t kTeknopointBits = kTeknopointStateLength * 8;
const uint16_t kToshibaACStateLength = 9;
const uint16_t kToshibaACBits = kToshibaACStateLength * 8;
const uint16_t kToshibaACMinRepeat = kSingleRepeat;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kSharpAcBits;
case TCL112AC:
return kTcl112AcBits;
case TEKNOPOINT:
return kTeknopointBits;
case TOSHIBA_AC:
return kToshibaACBits;
case TROTEC:
Expand Down Expand Up @@ -1248,6 +1250,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendTcl112Ac(state, nbytes);
break;
#endif // SEND_TCL112AC
#if SEND_TEKNOPOINT
case TEKNOPOINT:
sendTeknopoint(state, nbytes);
break;
#endif // SEND_TEKNOPOINT
#if SEND_TOSHIBA_AC
case TOSHIBA_AC:
sendToshibaAC(state, nbytes);
Expand Down
9 changes: 7 additions & 2 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,8 +674,8 @@ class IRsend {
#endif // SEND_ZEPEAL
#if SEND_VOLTAS
void sendVoltas(const unsigned char data[],
const uint16_t nbytes = kVoltasStateLength,
const uint16_t repeat = kNoRepeat);
const uint16_t nbytes = kVoltasStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_VOLTAS
#if SEND_METZ
void sendMetz(const uint64_t data,
Expand Down Expand Up @@ -712,6 +712,11 @@ class IRsend {
void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits,
const uint16_t repeat = kNoRepeat);
#endif // SEND_TRUMA
#if SEND_TEKNOPOINT
void sendTeknopoint(const unsigned char data[],
const uint16_t nbytes = kTeknopointStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_TEKNOPOINT

protected:
#ifdef UNIT_TEST
Expand Down
1 change: 1 addition & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,5 +286,6 @@ const PROGMEM char *kAllProtocolNamesStr =
D_STR_XMP "\x0"
D_STR_TRUMA "\x0"
D_STR_HAIER_AC176 "\x0"
D_STR_TEKNOPOINT "\x0"
///< New protocol strings should be added just above this line.
"\x0"; ///< This string requires double null termination.
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ bool hasACState(const decode_type_t protocol) {
case SANYO_AC:
case SHARP_AC:
case TCL112AC:
case TEKNOPOINT:
case TOSHIBA_AC:
case TROTEC:
case VOLTAS:
Expand Down
73 changes: 73 additions & 0 deletions src/ir_Teknopoint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2021 David Conran (crankyoldgit)
/// @file
/// @brief Support for the Teknopoint protocol
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1486

// Supports:
// Brand: Teknopoint, Model: Allegro SSA-09H A/C

#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"

// Protocol timings
const uint16_t kTeknopointHdrMark = 3614;
const uint16_t kTeknopointBitMark = 439;
const uint16_t kTeknopointHdrSpace = 1610;
const uint16_t kTeknopointOneSpace = 1238;
const uint16_t kTeknopointZeroSpace = 567;
const uint16_t kTeknopointFreq = 38000; // Hz. (Guess Only)
const uint16_t kTeknopointOverhead = 3;

#if SEND_TEKNOPOINT
/// Send a Teknopoint formatted message.
/// Status: BETA / Probably works however the bit order is not yet determined.
/// @param[in] data An array of bytes containing the IR command.
/// It is assumed to be in MSB order for this code.
/// e.g.
/// @code
/// uint8_t data[kTeknopointStateLength] = {
/// 0xC4, 0xD3, 0x64, 0x80, 0x00, 0x24, 0xC0,
/// 0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0xCA};
/// @endcode
/// @param[in] nbytes Nr. of bytes of data in the array.
/// @param[in] repeat Nr. of times the message is to be repeated.
void IRsend::sendTeknopoint(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
sendGeneric(kTeknopointHdrMark, kTeknopointHdrSpace,
kTeknopointBitMark, kTeknopointOneSpace,
kTeknopointBitMark, kTeknopointZeroSpace,
kTeknopointBitMark, kDefaultMessageGap,
data, nbytes, // Bytes
kTeknopointFreq, true, repeat, kDutyDefault);
}
#endif // SEND_TEKNOPOINT

#if DECODE_TEKNOPOINT
/// Decode the supplied Teknopoint message.
/// Status: Alpha / Probably works however the bit order is not yet determined.
/// @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::decodeTeknopoint(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 - offset)
return false; // Too short a message to match.
if (strict && nbits != kTeknopointBits)
return false;

if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kTeknopointHdrMark, kTeknopointHdrSpace,
kTeknopointBitMark, kTeknopointOneSpace,
kTeknopointBitMark, kTeknopointZeroSpace,
kTeknopointBitMark, kDefaultMessageGap, true)) return false;
// Success
results->decode_type = decode_type_t::TEKNOPOINT;
results->bits = nbits;
return true;
}
#endif // DECODE_TEKNOPOINT
3 changes: 3 additions & 0 deletions src/locale/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,9 @@
#ifndef D_STR_TECO
#define D_STR_TECO "TECO"
#endif // D_STR_TECO
#ifndef D_STR_TEKNOPOINT
#define D_STR_TEKNOPOINT "TEKNOPOINT"
#endif // D_STR_TEKNOPOINT
#ifndef D_STR_TOSHIBA_AC
#define D_STR_TOSHIBA_AC "TOSHIBA_AC"
#endif // D_STR_TOSHIBA_AC
Expand Down
101 changes: 101 additions & 0 deletions test/ir_Teknopoint_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright 2021 David Conran (crankyoldgit)

#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "gtest/gtest.h"

// Tests for decodeTeknopoint().

TEST(TestDecodeTeknopoint, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// "On"
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1486#issue-904276382
const uint16_t rawData[227] = {
3614, 1610, 474, 1210, 390, 1294, 454, 550, 394, 606, 474, 530, 474, 1186,
394, 634, 390, 638, 450, 1210, 366, 1318, 450, 554, 394, 1270, 470, 554,
450, 530, 390, 1318, 450, 1214, 470, 530, 474, 1210, 370, 1294, 474, 550,
450, 554, 470, 1214, 450, 550, 394, 614, 466, 1218, 450, 550, 370, 634,
474, 530, 474, 550, 450, 550, 450, 554, 366, 634, 502, 554, 446, 554, 450,
550, 450, 554, 390, 614, 474, 526, 478, 526, 474, 550, 450, 534, 406, 614,
390, 1294, 394, 610, 390, 614, 470, 1214, 366, 638, 470, 554, 450, 1214,
366, 1318, 450, 550, 454, 550, 474, 530, 470, 530, 474, 550, 454, 550,
450, 1238, 450, 1210, 470, 1214, 454, 1210, 474, 550, 450, 554, 394, 582,
394, 634, 474, 526, 478, 550, 450, 526, 474, 1214, 474, 550, 454, 550,
394, 606, 370, 634, 474, 526, 478, 550, 450, 550, 394, 610, 366, 638, 414,
586, 478, 546, 454, 550, 422, 606, 390, 610, 474, 554, 450, 550, 454, 550,
450, 530, 410, 614, 478, 526, 474, 550, 450, 550, 454, 550, 390, 614, 474,
526, 474, 550, 454, 550, 450, 554, 366, 634, 474, 526, 478, 550, 450, 550,
454, 550, 366, 638, 474, 526, 474, 550, 454, 1210, 414, 1270, 394, 610,
390, 614, 470, 1214, 366, 638, 470, 1190, 474, 554, 390};
const uint8_t expectedState[kTeknopointStateLength] = {
0xC4, 0xD3, 0x64, 0x80, 0x00, 0x24, 0xC0,
0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0xCA};
irsend.begin();
irsend.reset();
irsend.sendRaw(rawData, 227, 38);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::TEKNOPOINT, irsend.capture.decode_type);
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
IRAcUtils::resultAcToString(&irsend.capture));
}

TEST(TestDecodeTeknopoint, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();

// "On"
const uint8_t expectedState[kTeknopointStateLength] = {
0xC4, 0xD3, 0x64, 0x80, 0x00, 0x24, 0xC0,
0xF0, 0x10, 0x00, 0x00, 0x00, 0x00, 0xCA};

irsend.sendTeknopoint(expectedState);
irsend.makeDecodeResult();

ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::TEKNOPOINT, irsend.capture.decode_type);
ASSERT_EQ(kTeknopointBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"",
IRAcUtils::resultAcToString(&irsend.capture));

EXPECT_EQ(
"f38000d50"
"m3614s1610"
"m439s1238m439s1238m439s567m439s567m439s567m439s1238m439s567m439s567"
"m439s1238m439s1238m439s567m439s1238m439s567m439s567m439s1238m439s1238"
"m439s567m439s1238m439s1238m439s567m439s567m439s1238m439s567m439s567"
"m439s1238m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s1238m439s567m439s567m439s1238m439s567m439s567"
"m439s1238m439s1238m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s1238m439s1238m439s1238m439s1238m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s1238m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s567m439s567m439s567m439s567m439s567m439s567m439s567m439s567"
"m439s1238m439s1238m439s567m439s567m439s1238m439s567m439s1238m439s567"
"m439s100000",
irsend.outputStr());
}

TEST(TestUtils, Housekeeping) {
ASSERT_EQ("TEKNOPOINT", typeToString(decode_type_t::TEKNOPOINT));
ASSERT_EQ(decode_type_t::TEKNOPOINT, strToDecodeType("TEKNOPOINT"));
ASSERT_TRUE(hasACState(decode_type_t::TEKNOPOINT));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kTeknopointBits, IRsend::defaultBits(decode_type_t::TEKNOPOINT));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::TEKNOPOINT));
}

0 comments on commit 91dc3ea

Please sign in to comment.