update irremote ESP8266 to 2.5.6
This commit is contained in:
1 parent
1abd939125
commit
7a39bba272
36 files changed
+3023
-530
No files matched your search
@@ -364,6 +364,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
|
||||
DPRINTLN("Attempting SAMSUNG decode");
|
||||
if (decodeSAMSUNG(results)) return true;
|
||||
#endif
|
||||
#if DECODE_SAMSUNG36
|
||||
DPRINTLN("Attempting Samsung36 decode");
|
||||
if (decodeSamsung36(results)) return true;
|
||||
#endif
|
||||
#if DECODE_WHYNTER
|
||||
DPRINTLN("Attempting Whynter decode");
|
||||
if (decodeWhynter(results)) return true;
|
||||
@@ -495,7 +499,15 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
|
||||
#endif
|
||||
#if DECODE_VESTEL_AC
|
||||
DPRINTLN("Attempting Vestel AC decode");
|
||||
if (decodeVestelAC(results)) return true;
|
||||
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;
|
||||
#endif
|
||||
#if DECODE_HASH
|
||||
// decodeHash returns a hash on any input.
|
||||
|
||||
@@ -216,6 +216,11 @@ class IRrecv {
|
||||
bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits,
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG
|
||||
bool decodeSamsung36(decode_results *results,
|
||||
const uint16_t nbits = kSamsung36Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG_AC
|
||||
bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits,
|
||||
bool strict = true);
|
||||
@@ -335,8 +340,16 @@ class IRrecv {
|
||||
bool strict = true);
|
||||
#endif
|
||||
#if DECODE_VESTEL_AC
|
||||
bool decodeVestelAC(decode_results *results, uint16_t nbits = kVestelACBits,
|
||||
bool strict = true);
|
||||
bool decodeVestelAc(decode_results *results, uint16_t nbits = kVestelAcBits,
|
||||
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,
|
||||
bool strict = false);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
* Trotec AC code by stufisher
|
||||
* Carrier & Haier AC code by crankyoldgit
|
||||
* Vestel AC code by Erdem U. Altınyurt
|
||||
* Teco AC code by Fabien Valthier (hcoohb)
|
||||
*
|
||||
* GPL license, all text above must be included in any redistribution
|
||||
****************************************************/
|
||||
@@ -49,7 +50,7 @@
|
||||
#endif
|
||||
|
||||
// Library Version
|
||||
#define _IRREMOTEESP8266_VERSION_ "2.5.5"
|
||||
#define _IRREMOTEESP8266_VERSION_ "2.5.6"
|
||||
// Supported IR protocols
|
||||
// Each protocol you include costs memory and, during decode, costs time
|
||||
// Disable (set to false) all the protocols you do not need/want!
|
||||
@@ -86,6 +87,9 @@
|
||||
#define DECODE_SAMSUNG true
|
||||
#define SEND_SAMSUNG true
|
||||
|
||||
#define DECODE_SAMSUNG36 true
|
||||
#define SEND_SAMSUNG36 true
|
||||
|
||||
#define DECODE_SAMSUNG_AC true
|
||||
#define SEND_SAMSUNG_AC true
|
||||
|
||||
@@ -206,12 +210,19 @@
|
||||
#define DECODE_VESTEL_AC true
|
||||
#define SEND_VESTEL_AC true
|
||||
|
||||
#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.
|
||||
@@ -280,11 +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
|
||||
@@ -296,7 +310,7 @@ const uint16_t kAiwaRcT501MinRepeats = kSingleRepeat;
|
||||
const uint16_t kArgoStateLength = 12;
|
||||
const uint16_t kArgoDefaultRepeat = kNoRepeat;
|
||||
const uint16_t kCoolixBits = 24;
|
||||
const uint16_t kCoolixDefaultRepeat = kNoRepeat;
|
||||
const uint16_t kCoolixDefaultRepeat = 1;
|
||||
const uint16_t kCarrierAcBits = 32;
|
||||
const uint16_t kCarrierAcMinRepeat = kNoRepeat;
|
||||
// Daikin has a lot of static stuff that is discarded
|
||||
@@ -373,6 +387,7 @@ const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit.
|
||||
const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit.
|
||||
const uint16_t kRCMMBits = 24;
|
||||
const uint16_t kSamsungBits = 32;
|
||||
const uint16_t kSamsung36Bits = 36;
|
||||
const uint16_t kSamsungAcStateLength = 14;
|
||||
const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8;
|
||||
const uint16_t kSamsungAcExtendedStateLength = 21;
|
||||
@@ -393,6 +408,11 @@ 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;
|
||||
const uint16_t kToshibaACBits = kToshibaACStateLength * 8;
|
||||
const uint16_t kToshibaACMinRepeat = kSingleRepeat;
|
||||
@@ -402,7 +422,7 @@ const uint16_t kWhirlpoolAcStateLength = 21;
|
||||
const uint16_t kWhirlpoolAcBits = kWhirlpoolAcStateLength * 8;
|
||||
const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat;
|
||||
const uint16_t kWhynterBits = 32;
|
||||
const uint8_t kVestelACBits = 56;
|
||||
const uint8_t kVestelAcBits = 56;
|
||||
|
||||
|
||||
// Legacy defines. (Deprecated)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2017 David Conran
|
||||
// Copyright 2017,2019 David Conran
|
||||
|
||||
#include "IRsend.h"
|
||||
#ifndef UNIT_TEST
|
||||
@@ -488,57 +488,24 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) {
|
||||
}
|
||||
#endif // SEND_RAW
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
|
||||
// Send a simple (up to 64 bits) IR message of a given type.
|
||||
// An unknown/unsupported type will do nothing.
|
||||
// Args:
|
||||
// type: Protocol number/type of the message you want to send.
|
||||
// data: The data you want to send (up to 64 bits).
|
||||
// nbits: How many bits long the message is to be.
|
||||
// Returns:
|
||||
// bool: True if it is a type we can attempt to send, false if not.
|
||||
bool IRsend::send(decode_type_t type, uint64_t data, uint16_t nbits) {
|
||||
switch (type) {
|
||||
#if SEND_NEC
|
||||
case NEC:
|
||||
sendNEC(data, nbits);
|
||||
#if SEND_AIWA_RC_T501
|
||||
case AIWA_RC_T501:
|
||||
sendAiwaRCT501(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SONY
|
||||
case SONY:
|
||||
sendSony(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_RC5
|
||||
case RC5:
|
||||
sendRC5(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_RC6
|
||||
case RC6:
|
||||
sendRC6(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_DISH
|
||||
case DISH:
|
||||
sendDISH(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_JVC
|
||||
case JVC:
|
||||
sendJVC(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SAMSUNG
|
||||
case SAMSUNG:
|
||||
sendSAMSUNG(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_LG
|
||||
case LG:
|
||||
sendLG(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_LG
|
||||
case LG2:
|
||||
sendLG2(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_WHYNTER
|
||||
case WHYNTER:
|
||||
sendWhynter(data, nbits);
|
||||
#if SEND_CARRIER_AC
|
||||
case CARRIER_AC:
|
||||
sendCarrierAC(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_COOLIX
|
||||
@@ -551,14 +518,52 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
|
||||
sendDenon(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SHERWOOD
|
||||
case SHERWOOD:
|
||||
sendSherwood(data, nbits);
|
||||
#if SEND_DISH
|
||||
case DISH:
|
||||
sendDISH(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_RCMM
|
||||
case RCMM:
|
||||
sendRCMM(data, nbits);
|
||||
#if SEND_GICABLE
|
||||
case GICABLE:
|
||||
sendGICable(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_GREE
|
||||
case GREE:
|
||||
sendGree(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_JVC
|
||||
case JVC:
|
||||
sendJVC(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_LASERTAG
|
||||
case LASERTAG:
|
||||
sendLasertag(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_LG
|
||||
case LG:
|
||||
sendLG(data, nbits);
|
||||
break;
|
||||
case LG2:
|
||||
sendLG2(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_LUTRON
|
||||
case LUTRON:
|
||||
sendLutron(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_MAGIQUEST
|
||||
case MAGIQUEST:
|
||||
sendMagiQuest(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_MIDEA
|
||||
case MIDEA:
|
||||
sendMidea(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_MITSUBISHI
|
||||
@@ -571,24 +576,20 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
|
||||
sendMitsubishi2(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SHARP
|
||||
case SHARP:
|
||||
sendSharpRaw(data, nbits);
|
||||
#if SEND_NIKAI
|
||||
case NIKAI:
|
||||
sendNikai(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_AIWA_RC_T501
|
||||
case AIWA_RC_T501:
|
||||
sendAiwaRCT501(data, nbits);
|
||||
#if SEND_NEC
|
||||
case NEC:
|
||||
case NEC_LIKE:
|
||||
sendNEC(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_MIDEA
|
||||
case MIDEA:
|
||||
sendMidea(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_GICABLE
|
||||
case GICABLE:
|
||||
sendGICable(data, nbits);
|
||||
#if SEND_PANASONIC
|
||||
case PANASONIC:
|
||||
sendPanasonic64(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_PIONEER
|
||||
@@ -596,11 +597,68 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
|
||||
sendPioneer(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_VESTEL_AC
|
||||
case VESTEL_AC:
|
||||
sendVestelAC(data, nbits);
|
||||
#if SEND_RC5
|
||||
case RC5:
|
||||
sendRC5(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#if SEND_RC6
|
||||
case RC6:
|
||||
sendRC6(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_RCMM
|
||||
case RCMM:
|
||||
sendRCMM(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SAMSUNG
|
||||
case SAMSUNG:
|
||||
sendSAMSUNG(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SAMSUNG36
|
||||
case SAMSUNG36:
|
||||
sendSamsung36(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SANYO
|
||||
case SANYO_LC7461:
|
||||
sendSanyoLC7461(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SHARP
|
||||
case SHARP:
|
||||
sendSharpRaw(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SHERWOOD
|
||||
case SHERWOOD:
|
||||
sendSherwood(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_SONY
|
||||
case SONY:
|
||||
sendSony(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_TECO
|
||||
case TECO:
|
||||
sendTeco(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_VESTEL_AC
|
||||
case VESTEL_AC:
|
||||
sendVestelAc(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
#if SEND_WHYNTER
|
||||
case WHYNTER:
|
||||
sendWhynter(data, nbits);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -28,6 +28,8 @@ const uint8_t kDutyMax = 100; // Percentage
|
||||
// delayMicroseconds() is only accurate to 16383us.
|
||||
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
|
||||
const uint16_t kMaxAccurateUsecDelay = 16383;
|
||||
// Usecs to wait between messages we don't know the proper gap time.
|
||||
const uint32_t kDefaultMessageGap = 1000000;
|
||||
|
||||
// Classes
|
||||
class IRsend {
|
||||
@@ -66,7 +68,7 @@ class IRsend {
|
||||
const uint8_t *dataptr, const uint16_t nbytes,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
void send(uint16_t type, uint64_t data, uint16_t nbits);
|
||||
bool send(decode_type_t type, uint64_t data, uint16_t nbits);
|
||||
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
|
||||
void sendNEC(uint64_t data, uint16_t nbits = kNECBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
@@ -92,6 +94,10 @@ class IRsend {
|
||||
uint16_t repeat = kNoRepeat);
|
||||
uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command);
|
||||
#endif
|
||||
#if SEND_SAMSUNG36
|
||||
void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_SAMSUNG_AC
|
||||
void sendSamsungAC(unsigned char data[],
|
||||
uint16_t nbytes = kSamsungAcStateLength,
|
||||
@@ -309,9 +315,19 @@ class IRsend {
|
||||
uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_VESTEL_AC
|
||||
void sendVestelAC(const uint64_t data, const uint16_t nbits = kVestelACBits,
|
||||
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);
|
||||
#endif
|
||||
|
||||
|
||||
protected:
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
@@ -231,6 +231,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
|
||||
case SAMSUNG:
|
||||
result = "SAMSUNG";
|
||||
break;
|
||||
case SAMSUNG36:
|
||||
result = "SAMSUNG36";
|
||||
break;
|
||||
case SAMSUNG_AC:
|
||||
result = "SAMSUNG_AC";
|
||||
break;
|
||||
@@ -249,6 +252,12 @@ 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;
|
||||
case TOSHIBA_AC:
|
||||
result = "TOSHIBA_AC";
|
||||
break;
|
||||
@@ -287,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;
|
||||
|
||||
@@ -93,7 +93,7 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/484
|
||||
IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); }
|
||||
|
||||
void IRCoolixAC::stateReset() { remote_state = kCoolixDefaultState; }
|
||||
void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); }
|
||||
|
||||
void IRCoolixAC::begin() { _irsend.begin(); }
|
||||
|
||||
@@ -105,15 +105,50 @@ void IRCoolixAC::send(const uint16_t repeat) {
|
||||
|
||||
uint32_t IRCoolixAC::getRaw() { return remote_state; }
|
||||
|
||||
void IRCoolixAC::setRaw(const uint32_t new_code) { remote_state = new_code; }
|
||||
void IRCoolixAC::setRaw(const uint32_t new_code) {
|
||||
remote_state = new_code;
|
||||
saved_state = new_code;
|
||||
}
|
||||
|
||||
// Return true if the current state is a special state.
|
||||
bool IRCoolixAC::isSpecialState(void) {
|
||||
switch (remote_state) {
|
||||
case kCoolixClean:
|
||||
case kCoolixLed:
|
||||
case kCoolixOff:
|
||||
case kCoolixSwing:
|
||||
case kCoolixSleep:
|
||||
case kCoolixTurbo:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void IRCoolixAC::updateSavedState(void) {
|
||||
if (!isSpecialState()) saved_state = remote_state;
|
||||
}
|
||||
|
||||
void IRCoolixAC::recoverSavedState(void) {
|
||||
// If the current state is a special one, last known normal one.
|
||||
if (isSpecialState()) remote_state = saved_state;
|
||||
// If the saved_state was also a special state, reset as we expect a normal
|
||||
// state out of all this.
|
||||
if (isSpecialState()) stateReset();
|
||||
}
|
||||
|
||||
uint32_t IRCoolixAC::getNormalState(void) {
|
||||
return isSpecialState() ? saved_state : remote_state;
|
||||
}
|
||||
|
||||
void IRCoolixAC::setTempRaw(const uint8_t code) {
|
||||
recoverSavedState();
|
||||
remote_state &= ~kCoolixTempMask; // Clear the old temp.
|
||||
remote_state |= (code << 4);
|
||||
}
|
||||
|
||||
uint8_t IRCoolixAC::getTempRaw() {
|
||||
return (remote_state & kCoolixTempMask) >> 4;
|
||||
return (getNormalState() & kCoolixTempMask) >> 4;
|
||||
}
|
||||
|
||||
void IRCoolixAC::setTemp(const uint8_t desired) {
|
||||
@@ -132,6 +167,7 @@ uint8_t IRCoolixAC::getTemp() {
|
||||
}
|
||||
|
||||
void IRCoolixAC::setSensorTempRaw(const uint8_t code) {
|
||||
recoverSavedState();
|
||||
remote_state &= ~kCoolixSensorTempMask; // Clear previous sensor temp.
|
||||
remote_state |= ((code & 0xF) << 8);
|
||||
}
|
||||
@@ -145,7 +181,8 @@ void IRCoolixAC::setSensorTemp(const uint8_t desired) {
|
||||
}
|
||||
|
||||
uint8_t IRCoolixAC::getSensorTemp() {
|
||||
return ((remote_state & kCoolixSensorTempMask) >> 8) + kCoolixSensorTempMin;
|
||||
return ((getNormalState() & kCoolixSensorTempMask) >> 8) +
|
||||
kCoolixSensorTempMin;
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getPower() {
|
||||
@@ -154,25 +191,35 @@ bool IRCoolixAC::getPower() {
|
||||
}
|
||||
|
||||
void IRCoolixAC::setPower(const bool power) {
|
||||
if (!power) remote_state = kCoolixOff;
|
||||
// There really is no distinct "on" setting, so do nothing.
|
||||
if (power) {
|
||||
// There really is no distinct "on" setting, just ensure it a normal state.
|
||||
recoverSavedState();
|
||||
} else {
|
||||
updateSavedState();
|
||||
remote_state = kCoolixOff;
|
||||
}
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; }
|
||||
|
||||
void IRCoolixAC::setSwing() {
|
||||
// Assumes that repeated sending "swing" toggles the action on the device.
|
||||
updateSavedState();
|
||||
remote_state = kCoolixSwing;
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; }
|
||||
|
||||
void IRCoolixAC::setSleep() { remote_state = kCoolixSleep; }
|
||||
void IRCoolixAC::setSleep() {
|
||||
updateSavedState();
|
||||
remote_state = kCoolixSleep;
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; }
|
||||
|
||||
void IRCoolixAC::setTurbo() {
|
||||
// Assumes that repeated sending "turbo" toggles the action on the device.
|
||||
updateSavedState();
|
||||
remote_state = kCoolixTurbo;
|
||||
}
|
||||
|
||||
@@ -180,19 +227,24 @@ bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; }
|
||||
|
||||
void IRCoolixAC::setLed() {
|
||||
// Assumes that repeated sending "Led" toggles the action on the device.
|
||||
updateSavedState();
|
||||
remote_state = kCoolixLed;
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; }
|
||||
|
||||
void IRCoolixAC::setClean() { remote_state = kCoolixClean; }
|
||||
void IRCoolixAC::setClean() {
|
||||
updateSavedState();
|
||||
remote_state = kCoolixClean;
|
||||
}
|
||||
|
||||
bool IRCoolixAC::getZoneFollow() {
|
||||
return remote_state & kCoolixZoneFollowMask;
|
||||
return getNormalState() & kCoolixZoneFollowMask;
|
||||
}
|
||||
|
||||
// Internal use only.
|
||||
void IRCoolixAC::setZoneFollow(bool state) {
|
||||
recoverSavedState();
|
||||
if (state) {
|
||||
remote_state |= kCoolixZoneFollowMask;
|
||||
} else {
|
||||
@@ -201,6 +253,7 @@ void IRCoolixAC::setZoneFollow(bool state) {
|
||||
}
|
||||
|
||||
void IRCoolixAC::clearSensorTemp() {
|
||||
recoverSavedState();
|
||||
setZoneFollow(false);
|
||||
setSensorTempRaw(kCoolixSensorTempIgnoreCode);
|
||||
}
|
||||
@@ -214,6 +267,7 @@ void IRCoolixAC::setMode(const uint8_t mode) {
|
||||
case kCoolixAuto:
|
||||
case kCoolixHeat:
|
||||
case kCoolixDry:
|
||||
recoverSavedState();
|
||||
remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2);
|
||||
// Force the temp into a known-good state.
|
||||
setTemp(getTemp());
|
||||
@@ -222,15 +276,18 @@ void IRCoolixAC::setMode(const uint8_t mode) {
|
||||
}
|
||||
|
||||
uint8_t IRCoolixAC::getMode() {
|
||||
uint8_t mode = (remote_state & kCoolixModeMask) >> 2;
|
||||
uint8_t mode = (getNormalState() & kCoolixModeMask) >> 2;
|
||||
if (mode == kCoolixDry)
|
||||
if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan;
|
||||
return mode;
|
||||
}
|
||||
|
||||
uint8_t IRCoolixAC::getFan() { return (remote_state & kCoolixFanMask) >> 13; }
|
||||
uint8_t IRCoolixAC::getFan() {
|
||||
return (getNormalState() & kCoolixFanMask) >> 13;
|
||||
}
|
||||
|
||||
void IRCoolixAC::setFan(const uint8_t speed) {
|
||||
recoverSavedState();
|
||||
uint8_t newspeed = speed;
|
||||
switch (speed) {
|
||||
case kCoolixFanMin:
|
||||
@@ -263,6 +320,47 @@ std::string IRCoolixAC::toString() {
|
||||
result += "Off";
|
||||
return result; // If it's off, there is no other info.
|
||||
}
|
||||
// Special modes.
|
||||
if (getSwing()) {
|
||||
result += ", Swing: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getSleep()) {
|
||||
result += ", Sleep: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getTurbo()) {
|
||||
result += ", Turbo: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getLed()) {
|
||||
result += ", Led: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getClean()) {
|
||||
result += ", Clean: Toggle";
|
||||
return result;
|
||||
}
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case kCoolixAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kCoolixCool:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case kCoolixHeat:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case kCoolixDry:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case kCoolixFan:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case kCoolixFanAuto:
|
||||
@@ -289,47 +387,6 @@ std::string IRCoolixAC::toString() {
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
// Special modes.
|
||||
if (getSwing()) {
|
||||
result += ", Swing: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getSleep()) {
|
||||
result += ", Sleep: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getTurbo()) {
|
||||
result += ", Turbo: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getLed()) {
|
||||
result += ", Led: Toggle";
|
||||
return result;
|
||||
}
|
||||
if (getClean()) {
|
||||
result += ", Mode: Self clean";
|
||||
return result;
|
||||
}
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case kCoolixAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kCoolixCool:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case kCoolixHeat:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case kCoolixDry:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case kCoolixFan:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature.
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Zone Follow: ";
|
||||
|
||||
@@ -128,13 +128,17 @@ class IRCoolixAC {
|
||||
#endif
|
||||
|
||||
private:
|
||||
// The state of the IR remote in IR code form.
|
||||
uint32_t remote_state;
|
||||
uint32_t remote_state; // The state of the IR remote in IR code form.
|
||||
uint32_t saved_state; // Copy of the state if we required a special mode.
|
||||
IRsend _irsend;
|
||||
void setTempRaw(const uint8_t code);
|
||||
uint8_t getTempRaw();
|
||||
void setSensorTempRaw(const uint8_t code);
|
||||
void setZoneFollow(const bool state);
|
||||
bool isSpecialState(void);
|
||||
void updateSavedState(void);
|
||||
void recoverSavedState(void);
|
||||
uint32_t getNormalState(void);
|
||||
};
|
||||
|
||||
#endif // IR_COOLIX_H_
|
||||
@@ -755,18 +755,18 @@ void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes,
|
||||
// Leader
|
||||
sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace,
|
||||
0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload.
|
||||
0, 38000, false, 0, 50);
|
||||
0, kDaikin2Freq, false, 0, 50);
|
||||
// Section #1
|
||||
sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark,
|
||||
kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace,
|
||||
kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length,
|
||||
38000, false, 0, 50);
|
||||
kDaikin2Freq, false, 0, 50);
|
||||
// Section #2
|
||||
sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark,
|
||||
kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace,
|
||||
kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length,
|
||||
nbytes - kDaikin2Section1Length,
|
||||
38000, false, 0, 50);
|
||||
kDaikin2Freq, false, 0, 50);
|
||||
}
|
||||
}
|
||||
#endif // SEND_DAIKIN2
|
||||
@@ -1354,9 +1354,10 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits,
|
||||
kDaikin2Section2Length};
|
||||
|
||||
// Leader
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace))
|
||||
return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark,
|
||||
kDaikin2Tolerance)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace,
|
||||
kDaikin2Tolerance)) return false;
|
||||
|
||||
// Sections
|
||||
// Keep reading bytes until we either run out of section or state to fill.
|
||||
@@ -1365,8 +1366,10 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits,
|
||||
pos += sectionSize[section];
|
||||
|
||||
// Section Header
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark,
|
||||
kDaikin2Tolerance)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace,
|
||||
kDaikin2Tolerance)) return false;
|
||||
|
||||
// Section Data
|
||||
for (; offset <= results->rawlen - 16 && i < pos;
|
||||
@@ -1375,18 +1378,21 @@ bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits,
|
||||
data_result =
|
||||
matchData(&(results->rawbuf[offset]), 8, kDaikin2BitMark,
|
||||
kDaikin2OneSpace, kDaikin2BitMark,
|
||||
kDaikin2ZeroSpace, kTolerance, kMarkExcess, false);
|
||||
kDaikin2ZeroSpace, kDaikin2Tolerance, kMarkExcess, false);
|
||||
if (data_result.success == false) break; // Fail
|
||||
results->state[i] = (uint8_t)data_result.data;
|
||||
}
|
||||
|
||||
// Section Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark,
|
||||
kDaikin2Tolerance)) return false;
|
||||
if (section < kDaikin2Sections - 1) { // Inter-section gaps.
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap,
|
||||
kDaikin2Tolerance)) return false;
|
||||
} else { // Last section / End of message gap.
|
||||
if (offset <= results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset++], kDaikin2Gap)) return false;
|
||||
!matchAtLeast(results->rawbuf[offset++], kDaikin2Gap,
|
||||
kDaikin2Tolerance)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,17 +128,19 @@ const uint64_t kDaikinFirstHeader64 =
|
||||
0b1101011100000000000000001100010100000000001001111101101000010001;
|
||||
|
||||
// Another variant of the protocol for the Daikin ARC477A1 remote.
|
||||
const uint16_t kDaikin2Freq = 36700; // Modulation Frequency in Hz.
|
||||
const uint16_t kDaikin2LeaderMark = 10024;
|
||||
const uint16_t kDaikin2LeaderSpace = 25180;
|
||||
const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace;
|
||||
const uint16_t kDaikin2HdrMark = 3500;
|
||||
const uint16_t kDaikin2HdrSpace = 1728;
|
||||
const uint16_t kDaikin2BitMark = 418;
|
||||
const uint16_t kDaikin2OneSpace = 1315;
|
||||
const uint16_t kDaikin2ZeroSpace = 451;
|
||||
const uint16_t kDaikin2BitMark = 460;
|
||||
const uint16_t kDaikin2OneSpace = 1270;
|
||||
const uint16_t kDaikin2ZeroSpace = 420;
|
||||
const uint16_t kDaikin2Sections = 2;
|
||||
const uint16_t kDaikin2Section1Length = 20;
|
||||
const uint16_t kDaikin2Section2Length = 19;
|
||||
const uint8_t kDaikin2Tolerance = kTolerance + 5;
|
||||
|
||||
const uint8_t kDaikin2BitSleepTimer = 0b00100000;
|
||||
const uint8_t kDaikin2BitPurify = 0b00010000;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017 David Conran
|
||||
// Copyright 2017, 2018, 2019 David Conran
|
||||
|
||||
#include "ir_Samsung.h"
|
||||
#include <algorithm>
|
||||
@@ -168,6 +168,127 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SEND_SAMSUNG36
|
||||
// Send a Samsung 36-bit formatted message.
|
||||
//
|
||||
// Args:
|
||||
// data: The message to be sent.
|
||||
// nbits: The bit size of the message being sent. typically kSamsung36Bits.
|
||||
// repeat: The number of times the message is to be repeated.
|
||||
//
|
||||
// Status: Alpha / Experimental.
|
||||
//
|
||||
// Note:
|
||||
// Protocol is used by Samsung Bluray Remote: ak59-00167a
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/621
|
||||
void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
if (nbits < 16) return; // To small to send.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Block #1 (16 bits)
|
||||
sendGeneric(kSamsungHdrMark, kSamsungHdrSpace,
|
||||
kSamsungBitMark, kSamsungOneSpace,
|
||||
kSamsungBitMark, kSamsungZeroSpace,
|
||||
kSamsungBitMark, kSamsungHdrSpace,
|
||||
data >> (nbits - 16), 16, 38, true, 0, kDutyDefault);
|
||||
// Block #2 (The rest, typically 20 bits)
|
||||
sendGeneric(0, 0, // No header
|
||||
kSamsungBitMark, kSamsungOneSpace,
|
||||
kSamsungBitMark, kSamsungZeroSpace,
|
||||
kSamsungBitMark, kSamsungMinGap, // Gap is just a guess.
|
||||
// Mask off the rest of the bits.
|
||||
data & ((1ULL << (nbits - 16)) - 1),
|
||||
nbits - 16, 38, true, 0, kDutyDefault);
|
||||
}
|
||||
}
|
||||
#endif // SEND_SAMSUNG36
|
||||
|
||||
#if DECODE_SAMSUNG36
|
||||
// Decode the supplied Samsung36 message.
|
||||
//
|
||||
// Args:
|
||||
// results: Ptr to the data to decode and where to store the decode result.
|
||||
// nbits: Nr. of bits to expect in the data portion.
|
||||
// Typically kSamsung36Bits.
|
||||
// strict: Flag to indicate if we strictly adhere to the specification.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: Alpha / Experimental
|
||||
//
|
||||
// Note:
|
||||
// Protocol is used by Samsung Bluray Remote: ak59-00167a
|
||||
//
|
||||
// Ref:
|
||||
// https://github.com/markszabo/IRremoteESP8266/issues/621
|
||||
bool IRrecv::decodeSamsung36(decode_results *results, const uint16_t nbits,
|
||||
const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter * 2 - 1)
|
||||
return false; // Can't possibly be a valid Samsung message.
|
||||
// We need to be looking for > 16 bits to make sense.
|
||||
if (nbits <= 16) return false;
|
||||
if (strict && nbits != kSamsung36Bits)
|
||||
return false; // We expect nbits to be 36 bits of message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = kStartOffset;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false;
|
||||
// Calculate how long the common tick time is based on the header mark.
|
||||
uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks;
|
||||
if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false;
|
||||
// Calculate how long the common tick time is based on the header space.
|
||||
uint32_t s_tick =
|
||||
results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks;
|
||||
// Data (Block #1)
|
||||
match_result_t data_result =
|
||||
matchData(&(results->rawbuf[offset]), 16,
|
||||
kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick,
|
||||
kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
uint16_t bitsSoFar = data_result.used / 2;
|
||||
// Footer (Block #1)
|
||||
if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kSamsungHdrSpaceTicks * s_tick))
|
||||
return false;
|
||||
// Data (Block #2)
|
||||
data_result = matchData(&(results->rawbuf[offset]),
|
||||
nbits - 16,
|
||||
kSamsungBitMarkTicks * m_tick,
|
||||
kSamsungOneSpaceTicks * s_tick,
|
||||
kSamsungBitMarkTicks * m_tick,
|
||||
kSamsungZeroSpaceTicks * s_tick);
|
||||
if (data_result.success == false) return false;
|
||||
data <<= (nbits - 16);
|
||||
data += data_result.data;
|
||||
offset += data_result.used;
|
||||
bitsSoFar += data_result.used / 2;
|
||||
// Footer (Block #2)
|
||||
if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick))
|
||||
return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (nbits != bitsSoFar) return false;
|
||||
|
||||
// Success
|
||||
results->bits = bitsSoFar;
|
||||
results->value = data;
|
||||
results->decode_type = SAMSUNG36;
|
||||
results->command = data & ((1ULL << (nbits - 16)) - 1);
|
||||
results->address = data >> (nbits - 16);
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_SAMSUNG36
|
||||
|
||||
#if SEND_SAMSUNG_AC
|
||||
// Send a Samsung A/C message.
|
||||
//
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#include "ir_Tcl.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#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() { this->_irsend.begin(); }
|
||||
|
||||
#if SEND_TCL112AC
|
||||
void IRTcl112Ac::send(const uint16_t repeat) {
|
||||
this->checksum();
|
||||
this->_irsend.sendTcl112Ac(remote_state, kTcl112AcStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_TCL112AC
|
||||
|
||||
// Calculate the checksum for a given array.
|
||||
// Args:
|
||||
// state: The array to calculate the checksum over.
|
||||
// length: The size of the array.
|
||||
// Returns:
|
||||
// The 8 bit checksum value.
|
||||
uint8_t IRTcl112Ac::calcChecksum(uint8_t state[],
|
||||
const uint16_t length) {
|
||||
if (length)
|
||||
return sumBytes(state, length - 1);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate & set the checksum for the current internal state of the remote.
|
||||
void IRTcl112Ac::checksum(const uint16_t length) {
|
||||
// Stored the checksum value in the last byte.
|
||||
if (length > 1)
|
||||
remote_state[length - 1] = calcChecksum(remote_state, length);
|
||||
}
|
||||
|
||||
// Verify the checksum is valid for a given state.
|
||||
// Args:
|
||||
// state: The array to verify the checksum of.
|
||||
// length: The size of the state.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRTcl112Ac::validChecksum(uint8_t state[], const uint16_t length) {
|
||||
return (length > 1 && state[length - 1] == calcChecksum(state, length));
|
||||
}
|
||||
|
||||
void IRTcl112Ac::stateReset() {
|
||||
for (uint8_t i = 0; i < kTcl112AcStateLength; i++)
|
||||
remote_state[i] = 0x0;
|
||||
// A known good state. (On, Cool, 24C)
|
||||
remote_state[0] = 0x23;
|
||||
remote_state[1] = 0xCB;
|
||||
remote_state[2] = 0x26;
|
||||
remote_state[3] = 0x01;
|
||||
remote_state[5] = 0x24;
|
||||
remote_state[6] = 0x03;
|
||||
remote_state[7] = 0x07;
|
||||
remote_state[8] = 0x40;
|
||||
remote_state[13] = 0x03;
|
||||
}
|
||||
|
||||
uint8_t* IRTcl112Ac::getRaw() {
|
||||
this->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];
|
||||
}
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C to on.
|
||||
void IRTcl112Ac::on(void) { this->setPower(true); }
|
||||
|
||||
// Set the requested power state of the A/C to off.
|
||||
void IRTcl112Ac::off(void) { this->setPower(false); }
|
||||
|
||||
// Set the requested power state of the A/C.
|
||||
void IRTcl112Ac::setPower(const bool on) {
|
||||
if (on)
|
||||
remote_state[5] |= kTcl112AcPowerMask;
|
||||
else
|
||||
remote_state[5] &= ~kTcl112AcPowerMask;
|
||||
}
|
||||
|
||||
// Return the requested power state of the A/C.
|
||||
bool IRTcl112Ac::getPower(void) {
|
||||
return remote_state[5] & kTcl112AcPowerMask;
|
||||
}
|
||||
|
||||
// Get the requested climate operation mode of the a/c unit.
|
||||
// Returns:
|
||||
// A uint8_t containing the A/C mode.
|
||||
uint8_t IRTcl112Ac::getMode() {
|
||||
return remote_state[6] & 0xF;
|
||||
}
|
||||
|
||||
// Set the requested climate operation mode of the a/c unit.
|
||||
void IRTcl112Ac::setMode(const uint8_t mode) {
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
switch (mode) {
|
||||
case kTcl112AcAuto:
|
||||
case kTcl112AcCool:
|
||||
case kTcl112AcHeat:
|
||||
case kTcl112AcDry:
|
||||
case kTcl112AcFan:
|
||||
remote_state[6] &= 0xF0;
|
||||
remote_state[6] |= mode;
|
||||
break;
|
||||
default:
|
||||
setMode(kTcl112AcAuto);
|
||||
}
|
||||
}
|
||||
|
||||
void IRTcl112Ac::setTemp(const float celsius) {
|
||||
// Make sure we have desired temp in the correct range.
|
||||
float safecelsius = std::max(celsius, kTcl112AcTempMin);
|
||||
safecelsius = std::min(safecelsius, kTcl112AcTempMax);
|
||||
// Convert to integer nr. of half degrees.
|
||||
uint8_t nrHalfDegrees = safecelsius * 2;
|
||||
if (nrHalfDegrees & 1) // Do we have a half degree celsius?
|
||||
remote_state[12] |= kTcl112AcHalfDegree; // Add 0.5 degrees
|
||||
else
|
||||
remote_state[12] &= ~kTcl112AcHalfDegree; // Clear the half degree.
|
||||
remote_state[7] &= 0xF0; // Clear temp bits.
|
||||
remote_state[7] |= ((uint8_t)kTcl112AcTempMax - nrHalfDegrees / 2);
|
||||
}
|
||||
|
||||
float IRTcl112Ac::getTemp() {
|
||||
float result = kTcl112AcTempMax - (remote_state[7] & 0xF);
|
||||
if (remote_state[12] & kTcl112AcHalfDegree) result += 0.5;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRTcl112Ac::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRTcl112Ac::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
result += (this->getPower() ? "On" : "Off");
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (this->getMode()) {
|
||||
case kTcl112AcAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kTcl112AcCool:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case kTcl112AcHeat:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case kTcl112AcDry:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case kTcl112AcFan:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
uint16_t nrHalfDegrees = this->getTemp() * 2;
|
||||
result += ", Temp: " + uint64ToString(nrHalfDegrees / 2);
|
||||
if (nrHalfDegrees & 1) result += ".5";
|
||||
result += "C";
|
||||
return result;
|
||||
}
|
||||
|
||||
#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;
|
||||
// Verify we got a valid checksum.
|
||||
if (strict && !IRTcl112Ac::validChecksum(results->state)) 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
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#ifndef IR_TCL_H_
|
||||
#define IR_TCL_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#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.
|
||||
|
||||
const uint8_t kTcl112AcHeat = 1;
|
||||
const uint8_t kTcl112AcDry = 2;
|
||||
const uint8_t kTcl112AcCool = 3;
|
||||
const uint8_t kTcl112AcFan = 7;
|
||||
const uint8_t kTcl112AcAuto = 8;
|
||||
|
||||
const uint8_t kTcl112AcHalfDegree = 0b00100000;
|
||||
const float kTcl112AcTempMax = 31.0;
|
||||
const float kTcl112AcTempMin = 16.0;
|
||||
const uint8_t kTcl112AcPowerMask = 0x04;
|
||||
|
||||
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);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void);
|
||||
void setTemp(const float celsius); // Celsius in 0.5 increments
|
||||
float getTemp(void);
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void);
|
||||
static uint8_t calcChecksum(uint8_t state[],
|
||||
const uint16_t length = kTcl112AcStateLength);
|
||||
static bool validChecksum(uint8_t state[],
|
||||
const uint16_t length = kTcl112AcStateLength);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
private:
|
||||
uint8_t remote_state[kTcl112AcStateLength];
|
||||
void stateReset();
|
||||
void checksum(const uint16_t length = kTcl112AcStateLength);
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_TCL_H_
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2019 Fabien Valthier
|
||||
/*
|
||||
Node MCU/ESP8266 Sketch to emulate Teco
|
||||
*/
|
||||
|
||||
#include "ir_Teco.h"
|
||||
#include <algorithm>
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRutils.h"
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
// using SPACE modulation.
|
||||
const uint16_t kTecoHdrMark = 9000;
|
||||
const uint16_t kTecoHdrSpace = 4440;
|
||||
const uint16_t kTecoBitMark = 620;
|
||||
const uint16_t kTecoOneSpace = 1650;
|
||||
const uint16_t kTecoZeroSpace = 580;
|
||||
const uint32_t kTecoGap = kDefaultMessageGap; // Made-up value. Just a guess.
|
||||
|
||||
#if SEND_TECO
|
||||
// Send a Teco A/C message.
|
||||
//
|
||||
// Args:
|
||||
// data: Contents of the message to be sent.
|
||||
// nbits: Nr. of bits of data to be sent. Typically kTecoBits.
|
||||
// repeat: Nr. of additional times the message is to be sent.
|
||||
void IRsend::sendTeco(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(kTecoHdrMark, kTecoHdrSpace, kTecoBitMark, kTecoOneSpace,
|
||||
kTecoBitMark, kTecoZeroSpace, kTecoBitMark, kTecoGap,
|
||||
data, nbits, 38000, false, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_TECO
|
||||
|
||||
// Class for decoding and constructing Teco AC messages.
|
||||
IRTecoAc::IRTecoAc(const uint16_t pin) : _irsend(pin) { stateReset(); }
|
||||
|
||||
void IRTecoAc::begin() { _irsend.begin(); }
|
||||
|
||||
#if SEND_TECO
|
||||
void IRTecoAc::send(const uint16_t repeat) {
|
||||
_irsend.sendTeco(remote_state, kTecoBits, repeat);
|
||||
}
|
||||
#endif // SEND_TECO
|
||||
|
||||
void IRTecoAc::stateReset(void) {
|
||||
// Mode:auto, Power:Off, fan:auto, temp:16, swing:off, sleep:off
|
||||
remote_state = kTecoReset;
|
||||
}
|
||||
|
||||
uint64_t IRTecoAc::getRaw(void) { return remote_state; }
|
||||
|
||||
void IRTecoAc::setRaw(const uint64_t new_code) { remote_state = new_code; }
|
||||
|
||||
void IRTecoAc::on(void) { remote_state |= kTecoPower; }
|
||||
|
||||
void IRTecoAc::off(void) { remote_state &= ~kTecoPower; }
|
||||
|
||||
void IRTecoAc::setPower(const bool on) {
|
||||
if (on)
|
||||
this->on();
|
||||
else
|
||||
this->off();
|
||||
}
|
||||
|
||||
bool IRTecoAc::getPower(void) {
|
||||
return (remote_state & kTecoPower) == kTecoPower; }
|
||||
|
||||
void IRTecoAc::setTemp(const uint8_t temp) {
|
||||
uint8_t newtemp = temp;
|
||||
newtemp = std::min(newtemp, kTecoMaxTemp);
|
||||
newtemp = std::max(newtemp, kTecoMinTemp);
|
||||
newtemp -= kTecoMinTemp; // 16=0b000
|
||||
|
||||
remote_state &= ~kTecoTempMask; // reinit temp
|
||||
remote_state |= (newtemp << 8);
|
||||
}
|
||||
|
||||
uint8_t IRTecoAc::getTemp(void) {
|
||||
return ((remote_state & kTecoTempMask) >> 8) + kTecoMinTemp;
|
||||
}
|
||||
|
||||
// Set the speed of the fan
|
||||
void IRTecoAc::setFan(const uint8_t speed) {
|
||||
uint8_t newspeed = speed;
|
||||
switch (speed) {
|
||||
case kTecoFanAuto:
|
||||
case kTecoFanHigh:
|
||||
case kTecoFanMed:
|
||||
case kTecoFanLow:
|
||||
break;
|
||||
default:
|
||||
newspeed = kTecoFanAuto;
|
||||
}
|
||||
remote_state &= ~kTecoFanMask; // reinit fan
|
||||
remote_state |= (newspeed << 4);
|
||||
}
|
||||
|
||||
uint8_t IRTecoAc::getFan(void) { return (remote_state & kTecoFanMask) >> 4; }
|
||||
|
||||
void IRTecoAc::setMode(const uint8_t mode) {
|
||||
uint8_t newmode = mode;
|
||||
switch (mode) {
|
||||
case kTecoAuto:
|
||||
case kTecoCool:
|
||||
case kTecoDry:
|
||||
case kTecoFan:
|
||||
case kTecoHeat:
|
||||
break;
|
||||
default:
|
||||
newmode = kTecoAuto;
|
||||
}
|
||||
remote_state &= ~kTecoModeMask; // reinit mode
|
||||
remote_state |= newmode;
|
||||
}
|
||||
|
||||
uint8_t IRTecoAc::getMode(void) { return remote_state & kTecoModeMask; }
|
||||
|
||||
void IRTecoAc::setSwing(const bool on) {
|
||||
if (on)
|
||||
remote_state |= kTecoSwing;
|
||||
else
|
||||
remote_state &= ~kTecoSwing;
|
||||
}
|
||||
|
||||
bool IRTecoAc::getSwing(void) { return remote_state & kTecoSwing; }
|
||||
|
||||
void IRTecoAc::setSleep(const bool on) {
|
||||
if (on)
|
||||
remote_state |= kTecoSleep;
|
||||
else
|
||||
remote_state &= ~kTecoSleep;
|
||||
}
|
||||
|
||||
bool IRTecoAc::getSleep(void) { return remote_state & kTecoSleep; }
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRTecoAc::toString(void) {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRTecoAc::toString(void) {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
result += "Power: ";
|
||||
result += (this->getPower() ? "On" : "Off");
|
||||
result += ", Mode: " + uint64ToString(this->getMode());
|
||||
switch (this->getMode()) {
|
||||
case kTecoAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kTecoCool:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case kTecoHeat:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case kTecoDry:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case kTecoFan:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Temp: " + uint64ToString(this->getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(this->getFan());
|
||||
switch (this->getFan()) {
|
||||
case kTecoFanAuto:
|
||||
result += " (Auto)";
|
||||
break;
|
||||
case kTecoFanHigh:
|
||||
result += " (High)";
|
||||
break;
|
||||
case kTecoFanLow:
|
||||
result += " (Low)";
|
||||
break;
|
||||
case kTecoFanMed:
|
||||
result += " (Med)";
|
||||
break;
|
||||
default:
|
||||
result += " (UNKNOWN)";
|
||||
}
|
||||
result += ", Sleep: ";
|
||||
result += (this->getSleep() ? "On" : "Off");
|
||||
result += ", Swing: ";
|
||||
result += (this->getSwing() ? "On" : "Off");
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_TECO
|
||||
// Decode the supplied Teco 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 kTecoBits.
|
||||
// strict: Flag indicating if we should perform strict matching.
|
||||
// Returns:
|
||||
// boolean: True if it can decode it, false if it can't.
|
||||
//
|
||||
// Status: STABLE / Tested.
|
||||
bool IRrecv::decodeTeco(decode_results* results, uint16_t nbits, bool strict) {
|
||||
// Check if can possibly be a valid Teco message.
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false;
|
||||
if (strict && nbits != kTecoBits) return false; // Not what is expected
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t offset = kStartOffset;
|
||||
match_result_t data_result;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], kTecoHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kTecoHdrSpace)) return false;
|
||||
// Data (35 bits)
|
||||
data_result =
|
||||
matchData(&(results->rawbuf[offset]), 35, kTecoBitMark, kTecoOneSpace,
|
||||
kTecoBitMark, kTecoZeroSpace, kTolerance, kMarkExcess, false);
|
||||
if (data_result.success == false) return false;
|
||||
data = data_result.data;
|
||||
offset += data_result.used;
|
||||
uint16_t actualBits = data_result.used / 2;
|
||||
|
||||
// Footer.
|
||||
if (!matchMark(results->rawbuf[offset++], kTecoBitMark)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kTecoGap)) return false;
|
||||
|
||||
// Compliance
|
||||
if (actualBits < nbits) return false;
|
||||
if (strict && actualBits != nbits) return false; // Not as we expected.
|
||||
|
||||
// Success
|
||||
results->decode_type = TECO;
|
||||
results->bits = actualBits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_TECO
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2019 Fabien Valthier
|
||||
|
||||
#ifndef IR_TECO_H_
|
||||
#define IR_TECO_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
// Constants. Using LSB to be able to send only 35bits.
|
||||
const uint8_t kTecoAuto = 0; // 0b000
|
||||
const uint8_t kTecoCool = 1; // 0b001
|
||||
const uint8_t kTecoDry = 2; // 0b010
|
||||
const uint8_t kTecoFan = 3; // 0b110
|
||||
const uint8_t kTecoHeat = 4; // 0b001
|
||||
const uint8_t kTecoFanAuto = 0; // 0b00
|
||||
const uint8_t kTecoFanHigh = 3; // 0b11
|
||||
const uint8_t kTecoFanMed = 2; // 0b10
|
||||
const uint8_t kTecoFanLow = 1; // 0b01
|
||||
const uint8_t kTecoMinTemp = 16; // 16C
|
||||
const uint8_t kTecoMaxTemp = 30; // 30C
|
||||
|
||||
const uint64_t kTecoModeMask = 0b00000000000000000000000000000000111;
|
||||
const uint64_t kTecoPower = 0b00000000000000000000000000000001000;
|
||||
const uint64_t kTecoFanMask = 0b00000000000000000000000000000110000;
|
||||
const uint64_t kTecoSwing = 0b00000000000000000000000000001000000;
|
||||
const uint64_t kTecoSleep = 0b00000000000000000000000000010000000;
|
||||
const uint64_t kTecoTempMask = 0b00000000000000000000000111100000000;
|
||||
const uint64_t kTecoTimerHalfH = 0b00000000000000000000001000000000000;
|
||||
const uint64_t kTecoTimerTenHr = 0b00000000000000000000110000000000000;
|
||||
const uint64_t kTecoTimerOn = 0b00000000000000000001000000000000000;
|
||||
const uint64_t kTecoTimerUniHr = 0b00000000000000011110000000000000000;
|
||||
const uint64_t kTecoReset = 0b01001010000000000000010000000000000;
|
||||
/*
|
||||
(header mark and space)
|
||||
Teco AC map read and to be sent in LSB with number of bits
|
||||
|
||||
byte 0 = Cst 0x02
|
||||
byte 1 = Cst 0x50
|
||||
byte 2:
|
||||
b0-3 = 0b0000
|
||||
b4-7 = Timer hours (unit, not thenth)
|
||||
hours:
|
||||
0000 (0) = +0 hour
|
||||
0001 (1) = +1 hour
|
||||
...
|
||||
1001 (9) = +9 hours
|
||||
byte 3: = timer and Temperature
|
||||
b0 = Timer (1 = On, 0 = Off)
|
||||
b1-2 = Timer - number of 10hours
|
||||
10Hours:
|
||||
00 = 0 * 10hours of timer
|
||||
01 = 1 * 10 hours of timer
|
||||
10 = 2 * 10hours of timer
|
||||
b3 = Timer - half hour (1=half hour on, 0 = round hour)
|
||||
b4-7: Degrees C.
|
||||
0000 (0) = 16C
|
||||
0001 (1) = 17C
|
||||
0010 (2) = 18C
|
||||
...
|
||||
1101 (13) = 29C
|
||||
1110 (14) = 30C
|
||||
byte 4: Basics
|
||||
b0 = Sleep Mode (1 = On, 0 = Off)
|
||||
b1 = Vent swing (1 = On, 0 = Off)
|
||||
b2-3 = Fan
|
||||
Fan:
|
||||
00 = Auto
|
||||
01 = Fan 1
|
||||
10 = Fan 2
|
||||
11 = Fan 3 or higher
|
||||
b4 = Power Status (1 = On, 0 = Off)
|
||||
b5-7 = Modes LSB first
|
||||
Modes:
|
||||
000 = Auto (temp = 25C)
|
||||
001 = Cool
|
||||
010 = Dry (temp = 25C, but not shown)
|
||||
011 = Fan
|
||||
100 = Heat
|
||||
*/
|
||||
|
||||
// Classes
|
||||
class IRTecoAc {
|
||||
public:
|
||||
explicit IRTecoAc(const uint16_t pin);
|
||||
|
||||
void stateReset(void);
|
||||
#if SEND_TECO
|
||||
void send(const uint16_t repeat = kTecoDefaultRepeat);
|
||||
#endif // SEND_TECO
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
void setPower(const bool on);
|
||||
bool getPower(void);
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void);
|
||||
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void);
|
||||
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void);
|
||||
|
||||
void setSwing(const bool state);
|
||||
bool getSwing(void);
|
||||
|
||||
void setSleep(const bool state);
|
||||
bool getSleep(void);
|
||||
|
||||
// void setTimer(uint8_t time); // To check unit
|
||||
// uint8_t getTimer(uint8_t);
|
||||
|
||||
uint64_t getRaw(void);
|
||||
void setRaw(const uint64_t new_code);
|
||||
|
||||
#ifdef ARDUINO
|
||||
String toString(void);
|
||||
#else
|
||||
std::string toString(void);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// The state of the IR remote in IR code form.
|
||||
uint64_t remote_state;
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
#endif // IR_TECO_H_
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2018 Erdem U. Altinyurt
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#include "ir_Vestel.h"
|
||||
#include <algorithm>
|
||||
@@ -37,14 +38,14 @@
|
||||
//
|
||||
// Status: STABLE / Working.
|
||||
//
|
||||
void IRsend::sendVestelAC(const uint64_t data, const uint16_t nbits,
|
||||
void IRsend::sendVestelAc(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.
|
||||
|
||||
sendGeneric(kVestelACHdrMark, kVestelACHdrSpace, // Header
|
||||
kVestelACBitMark, kVestelACOneSpace, // Data
|
||||
kVestelACBitMark, kVestelACZeroSpace, // Data
|
||||
kVestelACBitMark, 100000, // Footer + repeat gap
|
||||
sendGeneric(kVestelAcHdrMark, kVestelAcHdrSpace, // Header
|
||||
kVestelAcBitMark, kVestelAcOneSpace, // Data
|
||||
kVestelAcBitMark, kVestelAcZeroSpace, // Data
|
||||
kVestelAcBitMark, 100000, // Footer + repeat gap
|
||||
data, nbits, 38, false, repeat, 50);
|
||||
}
|
||||
#endif
|
||||
@@ -52,133 +53,146 @@ void IRsend::sendVestelAC(const uint64_t data, const uint16_t nbits,
|
||||
// Code to emulate Vestel A/C IR remote control unit.
|
||||
|
||||
// Initialise the object.
|
||||
IRVestelAC::IRVestelAC(uint16_t pin) : _irsend(pin) { stateReset(); }
|
||||
IRVestelAc::IRVestelAc(uint16_t pin) : _irsend(pin) { stateReset(); }
|
||||
|
||||
// Reset the state of the remote to a known good state/sequence.
|
||||
void IRVestelAC::stateReset() {
|
||||
void IRVestelAc::stateReset() {
|
||||
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
|
||||
remote_state.rawCode = 0x0F00D9001FEF201LL;
|
||||
remote_time_state.rawCode = 0x00;
|
||||
remote_state = 0x0F00D9001FEF201ULL;
|
||||
remote_time_state = 0x201ULL;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Configure the pin for output.
|
||||
void IRVestelAC::begin() {
|
||||
void IRVestelAc::begin() {
|
||||
_irsend.begin();
|
||||
stateReset();
|
||||
}
|
||||
|
||||
#if SEND_VESTEL_AC
|
||||
// Send the current desired state to the IR LED.
|
||||
void IRVestelAC::send() {
|
||||
void IRVestelAc::send() {
|
||||
checksum(); // Ensure correct checksum before sending.
|
||||
uint64_t code_to_send;
|
||||
if (use_time_state)
|
||||
code_to_send = remote_time_state.rawCode;
|
||||
code_to_send = remote_time_state;
|
||||
else
|
||||
code_to_send = remote_state.rawCode;
|
||||
_irsend.sendVestelAC(code_to_send);
|
||||
code_to_send = remote_state;
|
||||
_irsend.sendVestelAc(code_to_send);
|
||||
}
|
||||
#endif // SEND_VESTEL_AC
|
||||
|
||||
// Return the internal state date of the remote.
|
||||
uint64_t IRVestelAC::getRaw() {
|
||||
uint64_t IRVestelAc::getRaw() {
|
||||
checksum();
|
||||
if (use_time_state) return remote_time_state.rawCode;
|
||||
return remote_state.rawCode;
|
||||
if (use_time_state) return remote_time_state;
|
||||
return remote_state;
|
||||
}
|
||||
|
||||
// Override the internal state with the new state.
|
||||
void IRVestelAC::setRaw(uint8_t* newState) {
|
||||
void IRVestelAc::setRaw(uint8_t* newState) {
|
||||
uint64_t upState = 0;
|
||||
for (int i = 0; i < 7; i++)
|
||||
upState |= static_cast<uint64_t>(newState[i]) << (i * 8);
|
||||
remote_state.rawCode = upState;
|
||||
remote_time_state.rawCode = upState;
|
||||
remote_state = upState;
|
||||
remote_time_state = upState;
|
||||
}
|
||||
|
||||
void IRVestelAC::setRaw(const uint64_t newState) {
|
||||
remote_state.rawCode = newState;
|
||||
remote_time_state.rawCode = newState;
|
||||
void IRVestelAc::setRaw(const uint64_t newState) {
|
||||
remote_state = newState;
|
||||
remote_time_state = newState;
|
||||
}
|
||||
|
||||
// Set the requested power state of the A/C to on.
|
||||
void IRVestelAC::on() { setPower(true); }
|
||||
void IRVestelAc::on() { setPower(true); }
|
||||
|
||||
// Set the requested power state of the A/C to off.
|
||||
void IRVestelAC::off() { setPower(false); }
|
||||
void IRVestelAc::off() { setPower(false); }
|
||||
|
||||
// Set the requested power state of the A/C.
|
||||
void IRVestelAC::setPower(const bool state) {
|
||||
void IRVestelAc::setPower(const bool state) {
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcPowerOffset);
|
||||
if (state)
|
||||
remote_state.power = 0xF;
|
||||
remote_state |= ((uint64_t)0xF << kVestelAcPowerOffset);
|
||||
else
|
||||
remote_state.power = 0xC;
|
||||
remote_state |= ((uint64_t)0xC << kVestelAcPowerOffset);
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the requested power state of the A/C.
|
||||
bool IRVestelAC::getPower() { return (remote_state.power == 0xF); }
|
||||
bool IRVestelAc::getPower() {
|
||||
return (remote_state >> kVestelAcPowerOffset == 0xF);
|
||||
}
|
||||
|
||||
// Set the temperature in Celsius degrees.
|
||||
void IRVestelAC::setTemp(const uint8_t temp) {
|
||||
void IRVestelAc::setTemp(const uint8_t temp) {
|
||||
uint8_t new_temp = temp;
|
||||
new_temp = std::max(kVestelACMinTempC, new_temp);
|
||||
// new_temp = std::max(kVestelACMinTempH, new_temp); Check MODE
|
||||
new_temp = std::min(kVestelACMaxTemp, new_temp);
|
||||
remote_state.temp = new_temp - 16;
|
||||
new_temp = std::max(kVestelAcMinTempC, new_temp);
|
||||
// new_temp = std::max(kVestelAcMinTempH, new_temp); Check MODE
|
||||
new_temp = std::min(kVestelAcMaxTemp, new_temp);
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcTempOffset);
|
||||
remote_state |= (uint64_t)(new_temp - 16) << kVestelAcTempOffset;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the set temperature.
|
||||
uint8_t IRVestelAC::getTemp(void) { return remote_state.temp + 16; }
|
||||
uint8_t IRVestelAc::getTemp(void) {
|
||||
return ((remote_state >> kVestelAcTempOffset) & 0xF) + 16;
|
||||
}
|
||||
|
||||
// Set the speed of the fan,
|
||||
// 1-3 set the fan speed, 0 or anything else set it to auto.
|
||||
void IRVestelAC::setFan(const uint8_t fan) {
|
||||
void IRVestelAc::setFan(const uint8_t fan) {
|
||||
switch (fan) {
|
||||
case kVestelACFanLow:
|
||||
case kVestelACFanMed:
|
||||
case kVestelACFanHigh:
|
||||
case kVestelACFanAutoCool:
|
||||
case kVestelACFanAutoHot:
|
||||
remote_state.fan = fan;
|
||||
case kVestelAcFanLow:
|
||||
case kVestelAcFanMed:
|
||||
case kVestelAcFanHigh:
|
||||
case kVestelAcFanAutoCool:
|
||||
case kVestelAcFanAutoHot:
|
||||
case kVestelAcFanAuto:
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcFanOffset);
|
||||
remote_state |= (uint64_t)fan << kVestelAcFanOffset;
|
||||
break;
|
||||
default:
|
||||
remote_state.fan = kVestelACFanAuto;
|
||||
setFan(kVestelAcFanAuto);
|
||||
}
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the requested state of the unit's fan.
|
||||
uint8_t IRVestelAC::getFan() { return remote_state.fan; }
|
||||
uint8_t IRVestelAc::getFan() {
|
||||
return (remote_state >> kVestelAcFanOffset) & 0xF;
|
||||
}
|
||||
|
||||
// Get the requested climate operation mode of the a/c unit.
|
||||
// Returns:
|
||||
// A uint8_t containing the A/C mode.
|
||||
uint8_t IRVestelAC::getMode() { return remote_state.mode; }
|
||||
uint8_t IRVestelAc::getMode() {
|
||||
return (remote_state >> kVestelAcModeOffset) & 0xF;
|
||||
}
|
||||
|
||||
// Set the requested climate operation mode of the a/c unit.
|
||||
void IRVestelAC::setMode(const uint8_t mode) {
|
||||
void IRVestelAc::setMode(const uint8_t mode) {
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
switch (mode) {
|
||||
case kVestelACAuto:
|
||||
case kVestelACCool:
|
||||
case kVestelACHeat:
|
||||
case kVestelACDry:
|
||||
case kVestelACFan:
|
||||
remote_state.mode = mode;
|
||||
case kVestelAcAuto:
|
||||
case kVestelAcCool:
|
||||
case kVestelAcHeat:
|
||||
case kVestelAcDry:
|
||||
case kVestelAcFan:
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcModeOffset);
|
||||
remote_state |= (uint64_t)mode << kVestelAcModeOffset;
|
||||
break;
|
||||
default:
|
||||
remote_state.mode = kVestelACAuto;
|
||||
setMode(kVestelAcAuto);
|
||||
}
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Set Auto mode of AC.
|
||||
void IRVestelAC::setAuto(const int8_t autoLevel) {
|
||||
void IRVestelAc::setAuto(const int8_t autoLevel) {
|
||||
if (autoLevel < -2 || autoLevel > 2) return;
|
||||
setMode(kVestelACAuto);
|
||||
setFan((autoLevel < 0 ? kVestelACFanAutoCool : kVestelACFanAutoHot));
|
||||
setMode(kVestelAcAuto);
|
||||
setFan((autoLevel < 0 ? kVestelAcFanAutoCool : kVestelAcFanAutoHot));
|
||||
if (autoLevel == 2)
|
||||
setTemp(30);
|
||||
else if (autoLevel == 1)
|
||||
@@ -191,124 +205,167 @@ void IRVestelAC::setAuto(const int8_t autoLevel) {
|
||||
setTemp(17);
|
||||
}
|
||||
|
||||
void IRVestelAc::setTimerActive(const bool on) {
|
||||
if (on) // activation
|
||||
remote_time_state |= ((uint64_t)1 << kVestelAcTimerFlagOffset);
|
||||
else // deactivate
|
||||
remote_time_state &= ~((uint64_t)1 << kVestelAcTimerFlagOffset);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
bool IRVestelAc::isTimerActive(void) {
|
||||
return (remote_time_state >> kVestelAcTimerFlagOffset) & 1;
|
||||
}
|
||||
|
||||
// Set Timer option of AC.
|
||||
// Valid time arguments are 0, 0.5, 1, 2, 3 and 5 hours (in min). 0 disables the
|
||||
// timer.
|
||||
void IRVestelAC::setTimer(const uint16_t minutes) {
|
||||
remote_time_state.t_turnOnMinute = 0;
|
||||
remote_time_state.t_turnOnHour = 0;
|
||||
remote_time_state.t_turnOffHour = minutes / 60;
|
||||
remote_time_state.t_turnOffMinute = (minutes % 60) / 10;
|
||||
remote_time_state.t_off_active = 0;
|
||||
if (minutes) {
|
||||
remote_time_state.t_timer_mode = 1;
|
||||
// Yes. t_on_active instead of t_off_active.
|
||||
remote_time_state.t_on_active = 1;
|
||||
} else { // Turn off the timer.
|
||||
remote_time_state.t_timer_mode = 0;
|
||||
remote_time_state.t_on_active = 0;
|
||||
}
|
||||
void IRVestelAc::setTimer(const uint16_t minutes) {
|
||||
// Clear both On & Off timers.
|
||||
remote_time_state &= ~((uint64_t)0xFFFF << kVestelAcOffTimeOffset);
|
||||
// Set the "Off" time with the nr of minutes before we turn off.
|
||||
remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10)
|
||||
<< kVestelAcOffTimeOffset;
|
||||
setOffTimerActive(false);
|
||||
// Yes. On Timer instead of Off timer active.
|
||||
setOnTimerActive(minutes != 0);
|
||||
setTimerActive(minutes != 0);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
uint16_t IRVestelAC::getTimer(void) { return getOffTimer(); }
|
||||
uint16_t IRVestelAc::getTimer(void) { return getOffTimer(); }
|
||||
|
||||
// Set the AC's internal clock
|
||||
void IRVestelAC::setTime(const uint16_t minutes) {
|
||||
remote_time_state.t_hour = minutes / 60;
|
||||
remote_time_state.t_minute = minutes % 60;
|
||||
void IRVestelAc::setTime(const uint16_t minutes) {
|
||||
remote_time_state &= ~((uint64_t)0x1F << kVestelAcHourOffset);
|
||||
remote_time_state |= (uint64_t)((minutes / 60) & 0x1F)
|
||||
<< kVestelAcHourOffset;
|
||||
remote_time_state &= ~((uint64_t)0xFF << kVestelAcMinuteOffset);
|
||||
remote_time_state |= (uint64_t)((minutes % 60) & 0xFF)
|
||||
<< kVestelAcMinuteOffset;
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
uint16_t IRVestelAC::getTime(void) {
|
||||
return remote_time_state.t_hour * 60 + remote_time_state.t_minute;
|
||||
uint16_t IRVestelAc::getTime(void) {
|
||||
return ((remote_time_state >> kVestelAcHourOffset) & 0x1F) * 60 +
|
||||
((remote_time_state >> kVestelAcMinuteOffset) & 0xFF);
|
||||
}
|
||||
|
||||
void IRVestelAc::setOnTimerActive(const bool on) {
|
||||
if (on) // activation
|
||||
remote_time_state |= ((uint64_t)1 << kVestelAcOnTimerFlagOffset);
|
||||
else // deactivate
|
||||
remote_time_state &= ~((uint64_t)1 << kVestelAcOnTimerFlagOffset);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
bool IRVestelAc::isOnTimerActive(void) {
|
||||
return (remote_time_state >> kVestelAcOnTimerFlagOffset) & 1;
|
||||
}
|
||||
|
||||
// Set AC's wake up time. Takes time in minute.
|
||||
void IRVestelAC::setOnTimer(const uint16_t minutes) {
|
||||
remote_time_state.t_turnOnHour = minutes / 60;
|
||||
// Register is 3 bit and can only hold minute/10
|
||||
remote_time_state.t_turnOnMinute = (minutes % 60) / 10;
|
||||
if (minutes)
|
||||
remote_time_state.t_on_active = 1; // activation
|
||||
else
|
||||
remote_time_state.t_on_active = 0; // deactivate
|
||||
remote_time_state.t_timer_mode = 0;
|
||||
void IRVestelAc::setOnTimer(const uint16_t minutes) {
|
||||
remote_time_state &= ~((uint64_t)0xFF << kVestelAcOnTimeOffset);
|
||||
remote_time_state |= (uint64_t)(((minutes / 60) << 3) + (minutes % 60) / 10)
|
||||
<< kVestelAcOnTimeOffset;
|
||||
setOnTimerActive(minutes != 0);
|
||||
setTimerActive(false);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
uint16_t IRVestelAC::getOnTimer(void) {
|
||||
return remote_time_state.t_turnOnHour * 60 +
|
||||
remote_time_state.t_turnOnMinute * 10;
|
||||
uint16_t IRVestelAc::getOnTimer(void) {
|
||||
uint8_t ontime = (remote_time_state >> kVestelAcOnTimeOffset) & 0xFF;
|
||||
return (ontime >> 3) * 60 + (ontime & 0x7) * 10;
|
||||
}
|
||||
|
||||
void IRVestelAc::setOffTimerActive(const bool on) {
|
||||
if (on) // activation
|
||||
remote_time_state |= ((uint64_t)1 << kVestelAcOffTimerFlagOffset);
|
||||
else // deactivate
|
||||
remote_time_state &= ~((uint64_t)1 << kVestelAcOffTimerFlagOffset);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
bool IRVestelAc::isOffTimerActive(void) {
|
||||
return (remote_time_state >> kVestelAcOffTimerFlagOffset) & 1;
|
||||
}
|
||||
|
||||
// Set AC's turn off time. Takes time in minute.
|
||||
void IRVestelAC::setOffTimer(const uint16_t minutes) {
|
||||
remote_time_state.t_turnOffHour = minutes / 60;
|
||||
// Register is 3 bit and can only hold minute/10
|
||||
remote_time_state.t_turnOffMinute = (minutes % 60) / 10;
|
||||
if (minutes)
|
||||
remote_time_state.t_off_active = 1; // activation
|
||||
else
|
||||
remote_time_state.t_off_active = 0; // deactivate
|
||||
remote_time_state.t_timer_mode = 0;
|
||||
void IRVestelAc::setOffTimer(const uint16_t minutes) {
|
||||
remote_time_state &= ~((uint64_t)0xFF << kVestelAcOffTimeOffset);
|
||||
remote_time_state |=
|
||||
(uint64_t)((((minutes / 60) << 3) + (minutes % 60) / 10) & 0xFF)
|
||||
<< kVestelAcOffTimeOffset;
|
||||
setOffTimerActive(minutes != 0);
|
||||
setTimerActive(false);
|
||||
use_time_state = true;
|
||||
}
|
||||
|
||||
uint16_t IRVestelAC::getOffTimer(void) {
|
||||
return remote_time_state.t_turnOffHour * 60 +
|
||||
remote_time_state.t_turnOffMinute * 10;
|
||||
uint16_t IRVestelAc::getOffTimer(void) {
|
||||
uint8_t offtime = (remote_time_state >> kVestelAcOffTimeOffset) & 0xFF;
|
||||
return (offtime >> 3) * 60 + (offtime & 0x7) * 10;
|
||||
}
|
||||
|
||||
// Set the Sleep state of the A/C.
|
||||
void IRVestelAC::setSleep(const bool state) {
|
||||
remote_state.turbo_sleep_normal = state ? kVestelACSleep : kVestelACNormal;
|
||||
void IRVestelAc::setSleep(const bool state) {
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset);
|
||||
remote_state |= (uint64_t)(state ? kVestelAcSleep : kVestelAcNormal)
|
||||
<< kVestelAcTurboSleepOffset;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the Sleep state of the A/C.
|
||||
bool IRVestelAC::getSleep() {
|
||||
return remote_state.turbo_sleep_normal == kVestelACSleep;
|
||||
bool IRVestelAc::getSleep() {
|
||||
return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcSleep;
|
||||
}
|
||||
|
||||
// Set the Turbo state of the A/C.
|
||||
void IRVestelAC::setTurbo(const bool state) {
|
||||
remote_state.turbo_sleep_normal = state ? kVestelACTurbo : kVestelACNormal;
|
||||
void IRVestelAc::setTurbo(const bool state) {
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcTurboSleepOffset);
|
||||
remote_state |= (uint64_t)(state ? kVestelAcTurbo : kVestelAcNormal)
|
||||
<< kVestelAcTurboSleepOffset;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the Turbo state of the A/C.
|
||||
bool IRVestelAC::getTurbo() {
|
||||
return remote_state.turbo_sleep_normal == kVestelACTurbo;
|
||||
bool IRVestelAc::getTurbo() {
|
||||
return ((remote_state >> kVestelAcTurboSleepOffset) & 0xF) == kVestelAcTurbo;
|
||||
}
|
||||
|
||||
// Set the Ion state of the A/C.
|
||||
void IRVestelAC::setIon(const bool state) {
|
||||
remote_state.ion = state ? kVestelACIon : 0;
|
||||
void IRVestelAc::setIon(const bool state) {
|
||||
remote_state &= ~((uint64_t)0x1 << kVestelAcIonOffset);
|
||||
|
||||
remote_state |= (uint64_t)(state ? 1 : 0) << kVestelAcIonOffset;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the Ion state of the A/C.
|
||||
bool IRVestelAC::getIon() { return remote_state.ion == kVestelACIon; }
|
||||
bool IRVestelAc::getIon() { return (remote_state >> kVestelAcIonOffset) & 1; }
|
||||
|
||||
// Set the Swing Roaming state of the A/C.
|
||||
void IRVestelAC::setSwing(const bool state) {
|
||||
remote_state.swing = state ? kVestelACSwing : 0xF;
|
||||
void IRVestelAc::setSwing(const bool state) {
|
||||
remote_state &= ~((uint64_t)0xF << kVestelAcSwingOffset);
|
||||
|
||||
remote_state |= (uint64_t)(state ? kVestelAcSwing : 0xF)
|
||||
<< kVestelAcSwingOffset;
|
||||
use_time_state = false;
|
||||
}
|
||||
|
||||
// Return the Swing Roaming state of the A/C.
|
||||
bool IRVestelAC::getSwing() { return remote_state.swing == kVestelACSwing; }
|
||||
bool IRVestelAc::getSwing() {
|
||||
return ((remote_state >> kVestelAcSwingOffset) & 0xF) == kVestelAcSwing;
|
||||
}
|
||||
|
||||
// Calculate the checksum for a given array.
|
||||
// Args:
|
||||
// state: The state to calculate the checksum over.
|
||||
// Returns:
|
||||
// The 8 bit checksum value.
|
||||
uint8_t IRVestelAC::calcChecksum(const uint64_t state) {
|
||||
uint8_t IRVestelAc::calcChecksum(const uint64_t state) {
|
||||
// Just counts the set bits +1 on stream and take inverse after mask
|
||||
uint8_t sum = 0;
|
||||
uint64_t temp_state = state & kVestelACCRCMask;
|
||||
uint64_t temp_state = state & kVestelAcCRCMask;
|
||||
for (; temp_state; temp_state >>= 1)
|
||||
if (temp_state & 1) sum++;
|
||||
sum += 2;
|
||||
@@ -321,46 +378,48 @@ uint8_t IRVestelAC::calcChecksum(const uint64_t state) {
|
||||
// state: The state to verify the checksum of.
|
||||
// Returns:
|
||||
// A boolean.
|
||||
bool IRVestelAC::validChecksum(const uint64_t state) {
|
||||
return (((state >> 12) & 0xFF) == calcChecksum(state));
|
||||
bool IRVestelAc::validChecksum(const uint64_t state) {
|
||||
return (((state >> kVestelAcChecksumOffset) & 0xFF) == calcChecksum(state));
|
||||
}
|
||||
|
||||
// Calculate & set the checksum for the current internal state of the remote.
|
||||
void IRVestelAC::checksum() {
|
||||
void IRVestelAc::checksum() {
|
||||
// Stored the checksum value in the last byte.
|
||||
remote_state.CRC = calcChecksum(remote_state.rawCode);
|
||||
remote_time_state.CRC = calcChecksum(remote_time_state.rawCode);
|
||||
remote_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset);
|
||||
remote_state |= (uint64_t)calcChecksum(remote_state)
|
||||
<< kVestelAcChecksumOffset;
|
||||
|
||||
remote_time_state &= ~((uint64_t)0xFF << kVestelAcChecksumOffset);
|
||||
remote_time_state |= (uint64_t)calcChecksum(remote_time_state)
|
||||
<< kVestelAcChecksumOffset;
|
||||
}
|
||||
|
||||
bool IRVestelAC::isTimeCommand() {
|
||||
return (remote_state.power == 0x00 || use_time_state);
|
||||
bool IRVestelAc::isTimeCommand() {
|
||||
return (remote_state >> kVestelAcPowerOffset == 0x00 || use_time_state);
|
||||
}
|
||||
|
||||
// Convert the internal state into a human readable string.
|
||||
#ifdef ARDUINO
|
||||
String IRVestelAC::toString() {
|
||||
String IRVestelAc::toString() {
|
||||
String result = "";
|
||||
#else
|
||||
std::string IRVestelAC::toString() {
|
||||
std::string IRVestelAc::toString() {
|
||||
std::string result = "";
|
||||
#endif // ARDUINO
|
||||
if (isTimeCommand()) {
|
||||
result += "Time: " + IRHaierAC::timeToString(getTime());
|
||||
|
||||
result += ", Timer: ";
|
||||
result += remote_time_state.t_timer_mode
|
||||
? IRHaierAC::timeToString(getTimer())
|
||||
: "Off";
|
||||
result += isTimerActive() ? IRHaierAC::timeToString(getTimer()) : "Off";
|
||||
|
||||
result += ", On Timer: ";
|
||||
result += (remote_time_state.t_on_active && !remote_time_state.t_timer_mode)
|
||||
result += (isOnTimerActive() && !isTimerActive())
|
||||
? IRHaierAC::timeToString(getOnTimer())
|
||||
: "Off";
|
||||
|
||||
result += ", Off Timer: ";
|
||||
result += remote_time_state.t_off_active
|
||||
? IRHaierAC::timeToString(getOffTimer())
|
||||
: "Off";
|
||||
result +=
|
||||
isOffTimerActive() ? IRHaierAC::timeToString(getOffTimer()) : "Off";
|
||||
return result;
|
||||
}
|
||||
// Not a time command, it's a normal command.
|
||||
@@ -368,19 +427,19 @@ std::string IRVestelAC::toString() {
|
||||
result += (getPower() ? "On" : "Off");
|
||||
result += ", Mode: " + uint64ToString(getMode());
|
||||
switch (getMode()) {
|
||||
case kVestelACAuto:
|
||||
case kVestelAcAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kVestelACCool:
|
||||
case kVestelAcCool:
|
||||
result += " (COOL)";
|
||||
break;
|
||||
case kVestelACHeat:
|
||||
case kVestelAcHeat:
|
||||
result += " (HEAT)";
|
||||
break;
|
||||
case kVestelACDry:
|
||||
case kVestelAcDry:
|
||||
result += " (DRY)";
|
||||
break;
|
||||
case kVestelACFan:
|
||||
case kVestelAcFan:
|
||||
result += " (FAN)";
|
||||
break;
|
||||
default:
|
||||
@@ -389,22 +448,22 @@ std::string IRVestelAC::toString() {
|
||||
result += ", Temp: " + uint64ToString(getTemp()) + "C";
|
||||
result += ", Fan: " + uint64ToString(getFan());
|
||||
switch (getFan()) {
|
||||
case kVestelACFanAuto:
|
||||
case kVestelAcFanAuto:
|
||||
result += " (AUTO)";
|
||||
break;
|
||||
case kVestelACFanLow:
|
||||
case kVestelAcFanLow:
|
||||
result += " (LOW)";
|
||||
break;
|
||||
case kVestelACFanMed:
|
||||
case kVestelAcFanMed:
|
||||
result += " (MEDIUM)";
|
||||
break;
|
||||
case kVestelACFanHigh:
|
||||
case kVestelAcFanHigh:
|
||||
result += " (HIGH)";
|
||||
break;
|
||||
case kVestelACFanAutoCool:
|
||||
case kVestelAcFanAutoCool:
|
||||
result += " (AUTO COOL)";
|
||||
break;
|
||||
case kVestelACFanAutoHot:
|
||||
case kVestelAcFanAutoHot:
|
||||
result += " (AUTO HOT)";
|
||||
break;
|
||||
default:
|
||||
@@ -433,13 +492,13 @@ std::string IRVestelAC::toString() {
|
||||
//
|
||||
// Status: Alpha / Needs testing against a real device.
|
||||
//
|
||||
bool IRrecv::decodeVestelAC(decode_results* results, uint16_t nbits,
|
||||
bool IRrecv::decodeVestelAc(decode_results* results, uint16_t nbits,
|
||||
bool strict) {
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
|
||||
if (strict)
|
||||
if (nbits != kVestelACBits)
|
||||
if (nbits != kVestelAcBits)
|
||||
return false; // Not strictly a Vestel AC message.
|
||||
|
||||
uint64_t data = 0;
|
||||
@@ -449,25 +508,25 @@ bool IRrecv::decodeVestelAC(decode_results* results, uint16_t nbits,
|
||||
return false; // We can't possibly capture a Vestel packet that big.
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], kVestelACHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kVestelACHdrSpace)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kVestelAcHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kVestelAcHdrSpace)) return false;
|
||||
|
||||
// Data (Normal)
|
||||
match_result_t data_result =
|
||||
matchData(&(results->rawbuf[offset]), nbits, kVestelACBitMark,
|
||||
kVestelACOneSpace, kVestelACBitMark, kVestelACZeroSpace,
|
||||
kVestelACTolerance, kMarkExcess, false);
|
||||
matchData(&(results->rawbuf[offset]), nbits, kVestelAcBitMark,
|
||||
kVestelAcOneSpace, kVestelAcBitMark, kVestelAcZeroSpace,
|
||||
kVestelAcTolerance, kMarkExcess, false);
|
||||
|
||||
if (data_result.success == false) return false;
|
||||
offset += data_result.used;
|
||||
data = data_result.data;
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kVestelACBitMark)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kVestelAcBitMark)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict)
|
||||
if (!IRVestelAC::validChecksum(data_result.data)) return false;
|
||||
if (!IRVestelAc::validChecksum(data_result.data)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = VESTEL_AC;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Copyright 2018 Erdem U. Altinyurt
|
||||
// (Used Midea.h as template)
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#ifndef IR_VESTEL_H_
|
||||
#define IR_VESTEL_H_
|
||||
|
||||
@@ -21,87 +22,88 @@
|
||||
|
||||
// Vestel added by Erdem U. Altinyurt
|
||||
|
||||
// Structure of a Command message (56 bits)
|
||||
// Signature: 12 bits. e.g. 0x201
|
||||
// Checksum: 8 bits
|
||||
// Swing: 4 bits. (auto 0xA, stop 0xF)
|
||||
// turbo_sleep_normal: 4bits. (normal 0x1, sleep 0x3, turbo 0x7)
|
||||
// Unused: 8 bits. (0x00)
|
||||
// Temperature: 4 bits. (Celcius, but offset by -16 degrees. e.g. 0x0 = 16C)
|
||||
// Fan Speed: 4 bits (auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot,
|
||||
// 0xC auto cool)
|
||||
// Mode: 3 bits. (auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4)
|
||||
// unknown/unused: 6 bits.
|
||||
// Ion flag: 1 bit.
|
||||
// unknown/unused: 1 bit.
|
||||
// Power/message type: 4 bits. (on 0xF, off 0xC, 0x0 == Timer mesage)
|
||||
//
|
||||
// Structure of a Time(r) message (56 bits)
|
||||
// Signature: 12 bits. e.g. 0x201
|
||||
// Checksum: 8 bits
|
||||
// Off Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2)
|
||||
// Off Hours: 5 bits. (0x17 == 11PM / 23:00)
|
||||
// On Minutes: 3 bits. (Stored in 10 min increments. eg. xx:20 is 0x2)
|
||||
// On Hours: 5 bits. (0x9 == 9AM / 09:00)
|
||||
// Clock Hours: 5 bits.
|
||||
// On Timer flag: 1 bit.
|
||||
// Off Timer flag: 1 bit.
|
||||
// Timer mode flag: 1 bit. (Off after X many hours/mins, not at clock time.)
|
||||
// Clock Minutes: 8 bits. (0-59)
|
||||
// Power/message type: 4 bits. (0x0 == Timer mesage, else see Comman message)
|
||||
|
||||
// Constants
|
||||
const uint16_t kVestelACHdrMark = 3110;
|
||||
const uint16_t kVestelACHdrSpace = 9066;
|
||||
const uint16_t kVestelACBitMark = 520;
|
||||
const uint16_t kVestelACOneSpace = 1535;
|
||||
const uint16_t kVestelACZeroSpace = 480;
|
||||
const uint16_t kVestelACTolerance = 30;
|
||||
const uint16_t kVestelAcHdrMark = 3110;
|
||||
const uint16_t kVestelAcHdrSpace = 9066;
|
||||
const uint16_t kVestelAcBitMark = 520;
|
||||
const uint16_t kVestelAcOneSpace = 1535;
|
||||
const uint16_t kVestelAcZeroSpace = 480;
|
||||
const uint16_t kVestelAcTolerance = 30;
|
||||
|
||||
const uint8_t kVestelACMinTempH = 16;
|
||||
const uint8_t kVestelACMinTempC = 18;
|
||||
const uint8_t kVestelACMaxTemp = 30;
|
||||
const uint8_t kVestelAcMinTempH = 16;
|
||||
const uint8_t kVestelAcMinTempC = 18;
|
||||
const uint8_t kVestelAcMaxTemp = 30;
|
||||
|
||||
const uint64_t kVestelACCRCMask = 0xFFFFFFFFFFF00000;
|
||||
const uint64_t kVestelAcCRCMask = 0xFFFFFFFFFFF00000;
|
||||
|
||||
const uint8_t kVestelACAuto = 0;
|
||||
const uint8_t kVestelACCool = 1;
|
||||
const uint8_t kVestelACDry = 2;
|
||||
const uint8_t kVestelACFan = 3;
|
||||
const uint8_t kVestelACHeat = 4;
|
||||
const uint8_t kVestelAcAuto = 0;
|
||||
const uint8_t kVestelAcCool = 1;
|
||||
const uint8_t kVestelAcDry = 2;
|
||||
const uint8_t kVestelAcFan = 3;
|
||||
const uint8_t kVestelAcHeat = 4;
|
||||
|
||||
const uint8_t kVestelACFanAuto = 1;
|
||||
const uint8_t kVestelACFanLow = 5;
|
||||
const uint8_t kVestelACFanMed = 9;
|
||||
const uint8_t kVestelACFanHigh = 0xB;
|
||||
const uint8_t kVestelACFanAutoCool = 0xC;
|
||||
const uint8_t kVestelACFanAutoHot = 0xD;
|
||||
const uint8_t kVestelAcFanAuto = 1;
|
||||
const uint8_t kVestelAcFanLow = 5;
|
||||
const uint8_t kVestelAcFanMed = 9;
|
||||
const uint8_t kVestelAcFanHigh = 0xB;
|
||||
const uint8_t kVestelAcFanAutoCool = 0xC;
|
||||
const uint8_t kVestelAcFanAutoHot = 0xD;
|
||||
|
||||
const uint8_t kVestelACNormal = 1;
|
||||
const uint8_t kVestelACSleep = 3;
|
||||
const uint8_t kVestelACTurbo = 7;
|
||||
const uint8_t kVestelACIon = 4;
|
||||
const uint8_t kVestelACSwing = 0xA;
|
||||
const uint8_t kVestelAcNormal = 1;
|
||||
const uint8_t kVestelAcSleep = 3;
|
||||
const uint8_t kVestelAcTurbo = 7;
|
||||
const uint8_t kVestelAcIon = 4;
|
||||
const uint8_t kVestelAcSwing = 0xA;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpacked-bitfield-compat"
|
||||
union VestelACState {
|
||||
// Remotes Command Stack
|
||||
struct {
|
||||
uint16_t footer : 12; // 0x201 footer
|
||||
uint8_t CRC : 8;
|
||||
uint8_t swing : 4; // auto 0xA, stop 0xF
|
||||
uint8_t turbo_sleep_normal : 4; // normal 0x1, sleep 0x3, turbo 0x7
|
||||
uint8_t zero : 8; // 0x00
|
||||
uint8_t temp : 4; // temp-16 degree Celcius
|
||||
uint8_t fan : 4; // auto 0x1, low 0x5, mid 0x9, high 0xB, 0xD auto hot, 0xC
|
||||
// auto cool
|
||||
uint8_t mode : 4; // auto 0x0, cold 0x1, dry 0x2, fan 0x3, hot 0x4
|
||||
uint8_t ion : 4; // on 0x4, off 0x0
|
||||
uint8_t power : 4; // on 0xF, off 0xC
|
||||
uint8_t not_used : 8;
|
||||
} __attribute__((packed)); // avoids padding.
|
||||
const uint8_t kVestelAcChecksumOffset = 12;
|
||||
const uint8_t kVestelAcSwingOffset = 20;
|
||||
const uint8_t kVestelAcTurboSleepOffset = 24;
|
||||
const uint8_t kVestelAcTempOffset = 36;
|
||||
const uint8_t kVestelAcFanOffset = 40;
|
||||
const uint8_t kVestelAcModeOffset = 44;
|
||||
const uint8_t kVestelAcIonOffset = 50;
|
||||
const uint8_t kVestelAcPowerOffset = 52;
|
||||
const uint8_t kVestelAcOffTimeOffset = 20;
|
||||
const uint8_t kVestelAcOnTimeOffset = 28;
|
||||
const uint8_t kVestelAcHourOffset = 36; // 5 bits
|
||||
const uint8_t kVestelAcOnTimerFlagOffset = kVestelAcHourOffset + 5;
|
||||
const uint8_t kVestelAcOffTimerFlagOffset = kVestelAcHourOffset + 6;
|
||||
const uint8_t kVestelAcTimerFlagOffset = kVestelAcHourOffset + 7;
|
||||
const uint8_t kVestelAcMinuteOffset = 44;
|
||||
|
||||
uint64_t rawCode;
|
||||
|
||||
// Remotes Timer Stack for programming AC unit for turn of after some time or
|
||||
// self wakeup - turn off at defined time. There are no timer functions
|
||||
// implemented.
|
||||
// Here is the stack only for decoding and reverse enginering purposes.
|
||||
struct {
|
||||
uint16_t t_footer : 12; // 0x201 footer
|
||||
uint8_t t_CRC : 8;
|
||||
// timer regs has 0x18 hour mask & 0x3=Minute mask minutes are divided to 10
|
||||
// format. Need to multiply with 10 Examples: 0x01= xx:10 and 0x08 = 01:00,
|
||||
// 0x03= 00:30 0x08=01:00 0x10=02:00 0x44=08:40
|
||||
uint8_t t_turnOffMinute : 3; // off minute/10
|
||||
uint8_t t_turnOffHour : 5;
|
||||
uint8_t t_turnOnMinute : 3; // on minute/10
|
||||
uint8_t t_turnOnHour : 5;
|
||||
uint8_t t_hour : 5; // actual time hour
|
||||
uint8_t t_on_active : 1;
|
||||
uint8_t t_off_active : 1;
|
||||
uint8_t t_timer_mode : 1; // For timer operation, also need t_on_active bit
|
||||
uint8_t t_minute : 8; // actual time minute
|
||||
uint16_t t_not_used : 16; // 00
|
||||
} __attribute__((packed)); // avoids padding.
|
||||
};
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
class IRVestelAC {
|
||||
class IRVestelAc {
|
||||
public:
|
||||
explicit IRVestelAC(uint16_t pin);
|
||||
explicit IRVestelAc(uint16_t pin);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_VESTEL_AC
|
||||
@@ -140,20 +142,24 @@ class IRVestelAC {
|
||||
void setIon(const bool state);
|
||||
bool getIon(void);
|
||||
bool isTimeCommand(void);
|
||||
bool isOnTimerActive(void);
|
||||
void setOnTimerActive(const bool on);
|
||||
bool isOffTimerActive(void);
|
||||
void setOffTimerActive(const bool on);
|
||||
bool isTimerActive(void);
|
||||
void setTimerActive(const bool on);
|
||||
static uint8_t calcChecksum(const uint64_t state);
|
||||
#ifdef ARDUINO
|
||||
String toString();
|
||||
#else
|
||||
std::string toString();
|
||||
#endif
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
// private:
|
||||
#endif
|
||||
VestelACState remote_state;
|
||||
VestelACState remote_time_state;
|
||||
bool use_time_state = false;
|
||||
private:
|
||||
uint64_t remote_state;
|
||||
uint64_t remote_time_state;
|
||||
bool use_time_state;
|
||||
void checksum();
|
||||
static uint8_t calcChecksum(const uint64_t state);
|
||||
IRsend _irsend;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user