Skip to content

Commit

Permalink
Experimental basic support for 112 bit TCL AC
Browse files Browse the repository at this point in the history
Add basic send/decode routines for TCL112AC protocol.
Update example code to reflect addition.
Add some basic unit tests.
Note: Still need to confirm bit-ordering. e.g. LSB or MSB.

Ref #619
  • Loading branch information
crankyoldgit committed Feb 27, 2019
1 parent 73a0b7e commit f864b49
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 6 deletions.
9 changes: 9 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,7 @@ void handleRoot() {
"<option value='20'>Mitsubishi</option>"
"<option value='52'>MWM</option>"
"<option value='46'>Samsung</option>"
"<option value='55'>TCL112</option>"
"<option value='32'>Toshiba</option>"
"<option value='28'>Trotec</option>"
"<option value='45'>Whirlpool</option>"
Expand Down Expand Up @@ -725,6 +726,9 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) {
// Cap the maximum size.
stateSize = std::min(stateSize, kStateSizeMax);
break;
case TCL112AC:
stateSize = kTcl112AcStateLength;
break;
default: // Not a protocol we expected. Abort.
debug("Unexpected AirCon protocol detected. Ignoring.");
return false;
Expand Down Expand Up @@ -854,6 +858,11 @@ bool parseStringAndSendAirCon(const uint16_t irType, const String str) {
case MWM:
irsend.sendMWM(reinterpret_cast<uint8_t *>(state), stateSize);
break;
#endif
#if SEND_TCL112AC
case TCL112AC:
irsend.sendTcl112Ac(reinterpret_cast<uint8_t *>(state));
break;
#endif
default:
debug("Unexpected AirCon type in send request. Not sent.");
Expand Down
1 change: 1 addition & 0 deletions examples/IRrecvDumpV2/IRrecvDumpV2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <ir_Mitsubishi.h>
#include <ir_Panasonic.h>
#include <ir_Samsung.h>
#include <ir_Tcl.h>
#include <ir_Teco.h>
#include <ir_Toshiba.h>
#include <ir_Vestel.h>
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
DPRINTLN("Attempting Vestel AC decode");
if (decodeVestelAC(results)) return true;
#endif
#if DECODE_TCL112AC
DPRINTLN("Attempting TCL112AC decode");
if (decodeTcl112Ac(results)) return true;
#endif
#if DECODE_TECO
DPRINTLN("Attempting Teco decode");
if (decodeTeco(results)) return true;
Expand Down
6 changes: 5 additions & 1 deletion src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,11 @@ class IRrecv {
#endif
#if DECODE_VESTEL_AC
bool decodeVestelAC(decode_results *results, uint16_t nbits = kVestelACBits,
bool strict = true);
bool strict = true);
#endif
#if DECODE_TCL112AC
bool decodeTcl112Ac(decode_results *results, uint16_t nbits = kTcl112AcBits,
bool strict = true);
#endif
#if DECODE_TECO
bool decodeTeco(decode_results *results, uint16_t nbits = kTecoBits,
Expand Down
12 changes: 10 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,16 @@
#define DECODE_TECO true
#define SEND_TECO true

#define DECODE_TCL112AC true
#define SEND_TCL112AC true

#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 || \
DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \
DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \
DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || DECODE_VESTEL_AC )
DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \
DECODE_VESTEL_AC || DECODE_TCL112AC)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
Expand Down Expand Up @@ -287,13 +291,14 @@ enum decode_type_t {
LUTRON,
ELECTRA_AC,
PANASONIC_AC,
PIONEER, // 50
PIONEER, // (50)
LG2,
MWM,
DAIKIN2,
VESTEL_AC,
TECO, // (55)
SAMSUNG36,
TCL112AC,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -403,6 +408,9 @@ const uint16_t kSony15Bits = 15;
const uint16_t kSony20Bits = 20;
const uint16_t kSonyMinBits = 12;
const uint16_t kSonyMinRepeat = 2;
const uint16_t kTcl112AcStateLength = 14;
const uint16_t kTcl112AcBits = kTcl112AcStateLength * 8;
const uint16_t kTcl112AcDefaultRepeat = kNoRepeat;
const uint16_t kTecoBits = 35;
const uint16_t kTecoDefaultRepeat = kNoRepeat;
const uint16_t kToshibaACStateLength = 9;
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,11 @@ class IRsend {
void sendVestelAC(const uint64_t data, const uint16_t nbits = kVestelACBits,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_TCL112AC
void sendTcl112Ac(const unsigned char data[],
const uint16_t nbytes = kTcl112AcStateLength,
const uint16_t repeat = kTcl112AcDefaultRepeat);
#endif
#if SEND_TECO
void sendTeco(uint64_t data, uint16_t nbits = kTecoBits,
uint16_t repeat = kNoRepeat);
Expand Down
4 changes: 4 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
case SONY:
result = "SONY";
break;
case TCL112AC:
result = "TCL112AC";
break;
case TECO:
result = "TECO";
break;
Expand Down Expand Up @@ -293,6 +296,7 @@ bool hasACState(const decode_type_t protocol) {
case MWM:
case PANASONIC_AC:
case SAMSUNG_AC:
case TCL112AC:
case TOSHIBA_AC:
case WHIRLPOOL_AC:
return true;
Expand Down
107 changes: 107 additions & 0 deletions src/ir_Tcl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2019 David Conran

#include "ir_Tcl.h"
#include "IRremoteESP8266.h"
#include "IRutils.h"

// Constants


#if SEND_TCL112AC
void IRsend::sendTcl112Ac(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
sendGeneric(kTcl112AcHdrMark, kTcl112AcHdrSpace,
kTcl112AcBitMark, kTcl112AcOneSpace,
kTcl112AcBitMark, kTcl112AcZeroSpace,
kTcl112AcBitMark, kTcl112AcGap,
data, nbytes, 38000, false, repeat, 50);
}
#endif // SEND_TCL112AC

IRTcl112Ac::IRTcl112Ac(uint16_t pin) : _irsend(pin) { stateReset(); }

void IRTcl112Ac::begin() { _irsend.begin(); }

#if SEND_TCL112AC
void IRTcl112Ac::send(const uint16_t repeat) {
checksum();
_irsend.sendTcl112Ac(remote_state, kTcl112AcStateLength, repeat);
}
#endif // SEND_TCL112AC

void IRTcl112Ac::checksum() {
}

void IRTcl112Ac::stateReset() {
}

uint8_t* IRTcl112Ac::getRaw() {
checksum();
return remote_state;
}

void IRTcl112Ac::setRaw(const uint8_t new_code[], const uint16_t length) {
for (uint8_t i = 0; i < length && i < kTcl112AcStateLength; i++) {
remote_state[i] = new_code[i];
}
}

#if DECODE_TCL112AC
// Decode the supplied TCL112AC message.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically kTcl112AcBits.
// 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 mostly work.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/619
bool IRrecv::decodeTcl112Ac(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Samsung A/C message.
if (strict && nbits != kTcl112AcBits) return false;

uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;

// Message Header
if (!matchMark(results->rawbuf[offset++], kTcl112AcHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kTcl112AcHdrSpace)) return false;

// Data
// Keep reading bytes until we either run out of section or state to fill.
for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8, kTcl112AcBitMark,
kTcl112AcOneSpace, kTcl112AcBitMark,
kTcl112AcZeroSpace, kTolerance, 0, false);
if (data_result.success == false) {
DPRINT("DEBUG: offset = ");
DPRINTLN(offset + data_result.used);
return false; // Fail
}
results->state[i] = data_result.data;
}

// Footer
if (!matchMark(results->rawbuf[offset++], kTcl112AcBitMark)) return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kTcl112AcGap)) return false;
// Compliance
// Re-check we got the correct size/length due to the way we read the data.
if (dataBitsSoFar != nbits) return false;
// Success
results->decode_type = TCL112AC;
results->bits = dataBitsSoFar;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif // DECODE_TCL112AC
36 changes: 36 additions & 0 deletions src/ir_Tcl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2019 David Conran

#ifndef IR_TCL_H_
#define IR_TCL_H_

#include "IRremoteESP8266.h"
#include "IRsend.h"

// Constants
const uint16_t kTcl112AcHdrMark = 3000;
const uint16_t kTcl112AcHdrSpace = 1650;
const uint16_t kTcl112AcBitMark = 500;
const uint16_t kTcl112AcOneSpace = 1050;
const uint16_t kTcl112AcZeroSpace = 325;
const uint32_t kTcl112AcGap = 100000; // Just a guess.

class IRTcl112Ac {
public:
explicit IRTcl112Ac(uint16_t pin);

#if SEND_TCL112AC
void send(const uint16_t repeat = kTcl112AcDefaultRepeat);
#endif // SEND_TCL
void begin(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kTcl112AcStateLength);

private:
uint8_t remote_state[kTcl112AcStateLength];
void stateReset();
void checksum();
IRsend _irsend;
};

#endif // IR_TCL_H_
14 changes: 12 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \
ir_Toshiba_test ir_Midea_test ir_Magiquest_test ir_Lasertag_test \
ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \
ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test \
ir_MWM_test ir_Vestel_test ir_Teco_test
ir_MWM_test ir_Vestel_test ir_Teco_test ir_Tcl_test

# All Google Test headers. Usually you shouldn't change this
# definition.
Expand Down Expand Up @@ -80,7 +80,7 @@ PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \
ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \
ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \
ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \
ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o
ir_Pioneer.o ir_MWM.o ir_Vestel.o ir_Teco.o ir_Tcl.o

# All the IR Protocol header files.
PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \
Expand All @@ -101,6 +101,7 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \
$(USER_DIR)/ir_Panasonic.h \
$(USER_DIR)/ir_Whirlpool.h \
$(USER_DIR)/ir_Vestel.h \
$(USER_DIR)/ir_Tcl.h \
$(USER_DIR)/ir_Teco.h
# Common object files
COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \
Expand Down Expand Up @@ -515,3 +516,12 @@ ir_Teco_test.o : ir_Teco_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)

ir_Teco_test : $(COMMON_OBJ) ir_Teco_test.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@

ir_Tcl.o : $(USER_DIR)/ir_Tcl.cpp $(COMMON_DEPS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Tcl.cpp

ir_Tcl_test.o : ir_Tcl_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Tcl_test.cpp

ir_Tcl_test : $(COMMON_OBJ) ir_Tcl_test.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
Loading

0 comments on commit f864b49

Please sign in to comment.