update irremote ESP8266 to 2.5.6

This commit is contained in:
Florian committed 2019-03-16 13:16:54 +01:00
1 parent 1abd939125
commit 7a39bba272
36 files changed
+3023 -530

No files matched your search

+13 -1
View File
@@ -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.
+15 -2
View File
@@ -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
};
+25 -5
View File
@@ -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)
+132 -74
View File
@@ -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;
}
+18 -2
View File
@@ -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
+10
View File
@@ -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;
+109 -52
View File
@@ -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: ";
+6 -2
View File
@@ -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_
+18 -12
View File
@@ -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;
}
}
+5 -3
View File
@@ -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;
+122 -1
View File
@@ -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.
//
+253
View File
@@ -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
+69
View File
@@ -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_
+243
View File
@@ -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
+135
View File
@@ -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_
+212 -153
View File
@@ -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;
+85 -79
View File
@@ -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;
};