Update IRRemoteESP8266 version to 2.5.5

factorize IR send signal function between the 2 types of receiving
Add new IRRemoteESP8266 protocols
replace pins on mega according schema
remove unecessaries spaces
This commit is contained in:
Florian committed 2019-02-08 16:50:37 +01:00
1 parent 0180a0dbd5
commit f0305236db
149 files changed
+19751 -6644

No files matched your search

+406 -427
View File
@@ -248,16 +248,10 @@ void IRtoMQTT(){
trc(valueRPT); trc(valueRPT);
} }
#ifdef ESP8266 // send coolix not available for arduino IRRemote library if(topicOri && (strstr(topicOri, "IR_NEC") == NULL)){
#ifdef IR_COOLIX signalSent = sendIdentifiedProtocol(topicOri, data, (unsigned char *)datacallback, valueBITS, valueRPT);
if (strstr(topicOri, "IR_COOLIX") != NULL){ }else{
if (valueBITS == 0) valueBITS = COOLIX_BITS; trc(F("Using NEC protocol"));
irsend.sendCOOLIX(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#endif
if (strstr(topicOri, "IR_NEC") != NULL || strstr(topicOri, subjectMQTTtoIR) != NULL ){
if (valueBITS == 0) valueBITS = NEC_BITS; if (valueBITS == 0) valueBITS = NEC_BITS;
#ifdef ESP8266 #ifdef ESP8266
irsend.sendNEC(data, valueBITS, valueRPT); irsend.sendNEC(data, valueBITS, valueRPT);
@@ -266,211 +260,6 @@ void IRtoMQTT(){
#endif #endif
signalSent = true; signalSent = true;
} }
#ifdef IR_Whynter
if (strstr(topicOri, "IR_Whynter") != NULL){
if (valueBITS == 0) valueBITS = WHYNTER_BITS;
#ifdef ESP8266
irsend.sendWhynter(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_LG
if (strstr(topicOri, "IR_LG") != NULL){
if (valueBITS == 0) valueBITS = LG_BITS;
#ifdef ESP8266
irsend.sendLG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_Sony
if (strstr(topicOri, "IR_Sony") != NULL){
if (valueBITS == 0) valueBITS = SONY_12_BITS;
if (valueRPT == 0) valueRPT = repeatIRwNumber;
#ifdef ESP8266
irsend.sendSony(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_DISH
if (strstr(topicOri, "IR_DISH") != NULL){
if (valueBITS == 0) valueBITS = DISH_BITS;
#ifdef ESP8266
irsend.sendDISH(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_RC5
if (strstr(topicOri, "IR_RC5") != NULL){
if (valueBITS == 0) valueBITS = RC5_BITS;
#ifdef ESP8266
irsend.sendRC5(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_RC6
if (strstr(topicOri, "IR_RC6") != NULL){
if (valueBITS == 0) valueBITS = RC6_MODE0_BITS;
#ifdef ESP8266
irsend.sendRC6(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_Sharp
if (strstr(topicOri, "IR_Sharp") != NULL){
if (valueBITS == 0) valueBITS = SHARP_BITS;
#ifdef ESP8266
irsend.sendSharpRaw(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_SAMSUNG
if (strstr(topicOri, "IR_SAMSUNG") != NULL){
if (valueBITS == 0) valueBITS = SAMSUNG_BITS;
#ifdef ESP8266
irsend.sendSAMSUNG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_JVC
if (strstr(topicOri, "IR_JVC") != NULL){
if (valueBITS == 0) valueBITS = JVC_BITS;
if (valueRPT == 0) valueRPT = repeatIRwNumber;
#ifdef ESP8266
irsend.sendJVC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_PANASONIC
if (strstr(topicOri, "IR_PANASONIC") != NULL){
if (valueRPT == 0) valueRPT = repeatIRwNumber;
#ifdef ESP8266
if (valueBITS == 0) valueBITS = PANASONIC_BITS;
irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendPanasonic(PanasonicAddress, data);
#endif
signalSent = true;
}
#endif
#ifdef ESP8266 // sendings not available on arduino
#ifdef IR_RCMM
if (strstr(topicOri, "IR_RCMM") != NULL){
if (valueBITS == 0) valueBITS = RCMM_BITS;
irsend.sendRCMM(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_DENON
if (strstr(topicOri, "IR_DENON") != NULL){
if (valueBITS == 0) valueBITS = DENON_BITS;
irsend.sendDenon(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_GICABLE
if (strstr(topicOri, "IR_GICABLE") != NULL){
if (valueBITS == 0) valueBITS = GICABLE_BITS;
if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) GICABLE_MIN_REPEAT);
irsend.sendGICable(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_SHERWOOD
if (strstr(topicOri, "IR_SHERWOOD") != NULL){
if (valueBITS == 0) valueBITS = SHERWOOD_BITS;
if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) SHERWOOD_MIN_REPEAT);
irsend.sendSherwood(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MITSUBISHI
if (strstr(topicOri, "IR_MITSUBISHI") != NULL){
if (valueBITS == 0) valueBITS = MITSUBISHI_BITS;
if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT);
irsend.sendMitsubishi(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_NIKAI
if (strstr(topicOri, "IR_NIKAI") != NULL){
if (valueBITS == 0) valueBITS = NIKAI_BITS;
irsend.sendNikai(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MIDEA
if (strstr(topicOri, "IR_MIDEA") != NULL){
if (valueBITS == 0) valueBITS = MIDEA_BITS;
irsend.sendMidea(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MAGIQUEST
if (strstr(topicOri, "IR_MAGIQUEST") != NULL){
if (valueBITS == 0) valueBITS = MAGIQUEST_BITS;
irsend.sendMagiQuest(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_LASERTAG
if (strstr(topicOri, "IR_LASERTAG") != NULL){
if (valueBITS == 0) valueBITS = LASERTAG_BITS;
irsend.sendLasertag(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_CARRIER_AC
if (strstr(topicOri, "IR_CARRIER_AC") != NULL){
if (valueBITS == 0) valueBITS = CARRIER_AC_BITS;
irsend.sendCarrierAC(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MITSUBISHI2
if (strstr(topicOri, "IR_MITSUBISHI2") != NULL){
if (valueBITS == 0) valueBITS = MITSUBISHI_BITS;
if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT);
irsend.sendMitsubishi2(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_AIWA_RC_T501
if (strstr(topicOri, "IR_MITSUBISHI") != NULL){
if (valueBITS == 0) valueBITS = AIWA_RC_T501_BITS;
if (valueRPT == 0) valueRPT = std::max(valueRPT, (uint16_t) AIWA_RC_T501_MIN_REPEAT);
irsend.sendAiwaRCT501(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#endif
if (signalSent){ // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also if (signalSent){ // we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
pub(subjectGTWIRtoMQTT, datacallback); pub(subjectGTWIRtoMQTT, datacallback);
@@ -486,6 +275,7 @@ void IRtoMQTT(){
trc(F("MQTTtoIR json")); trc(F("MQTTtoIR json"));
unsigned long data = IRdata["value"]; unsigned long data = IRdata["value"];
const char * raw = IRdata["raw"]; const char * raw = IRdata["raw"];
const char * datastring = IRdata["datastring"];
if (data != 0||raw) { if (data != 0||raw) {
trc(F("MQTTtoIR data || raw ok")); trc(F("MQTTtoIR data || raw ok"));
boolean signalSent = false; boolean signalSent = false;
@@ -558,218 +348,7 @@ void IRtoMQTT(){
} }
#endif #endif
}else if(protocol_name && (strstr(protocol_name, "IR_NEC") == NULL)){ }else if(protocol_name && (strstr(protocol_name, "IR_NEC") == NULL)){
#ifdef ESP8266 // send coolix not available for arduino IRRemote library signalSent = sendIdentifiedProtocol(protocol_name, data, (unsigned char*)datastring, valueBITS, valueRPT);
#ifdef IR_COOLIX
if (strstr(protocol_name, "IR_COOLIX") != NULL){
if (valueBITS == 0) valueBITS = COOLIX_BITS;
irsend.sendCOOLIX(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#endif
#ifdef IR_Whynter
if (strstr(protocol_name, "IR_Whynter") != NULL){
if (valueBITS == 0) valueBITS = WHYNTER_BITS;
#ifdef ESP8266
irsend.sendWhynter(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_LG
if (strstr(protocol_name, "IR_LG") != NULL){
if (valueBITS == 0) valueBITS = LG_BITS;
#ifdef ESP8266
irsend.sendLG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_Sony
if (strstr(protocol_name, "IR_Sony") != NULL){
if (valueBITS == 0) valueBITS = SONY_12_BITS;
#ifdef ESP8266
irsend.sendSony(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_DISH
if (strstr(protocol_name, "IR_DISH") != NULL){
if (valueBITS == 0) valueBITS = DISH_BITS;
#ifdef ESP8266
irsend.sendDISH(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_RC5
if (strstr(protocol_name, "IR_RC5") != NULL){
if (valueBITS == 0) valueBITS = RC5_BITS;
#ifdef ESP8266
irsend.sendRC5(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_RC6
if (strstr(protocol_name, "IR_RC6") != NULL){
if (valueBITS == 0) valueBITS = RC6_MODE0_BITS;
#ifdef ESP8266
irsend.sendRC6(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_Sharp
if (strstr(protocol_name, "IR_Sharp") != NULL){
if (valueBITS == 0) valueBITS = SHARP_BITS;
#ifdef ESP8266
irsend.sendSharpRaw(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_SAMSUNG
if (strstr(protocol_name, "IR_SAMSUNG") != NULL){
if (valueBITS == 0) valueBITS = SAMSUNG_BITS;
#ifdef ESP8266
irsend.sendSAMSUNG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_JVC
if (strstr(protocol_name, "IR_JVC") != NULL){
if (valueBITS == 0) valueBITS = JVC_BITS;
#ifdef ESP8266
irsend.sendJVC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS);
#endif
signalSent = true;
}
#endif
#ifdef IR_PANASONIC
if (strstr(protocol_name, "IR_PANASONIC") != NULL){
#ifdef ESP8266
if (valueBITS == 0) valueBITS = PANASONIC_BITS;
irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendPanasonic(PanasonicAddress, data);
#endif
signalSent = true;
}
#endif
#ifdef ESP8266 // sendings not available on arduino
#ifdef IR_RCMM
if (strstr(protocol_name, "IR_RCMM") != NULL){
if (valueBITS == 0) valueBITS = RCMM_BITS;
irsend.sendRCMM(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_DENON
if (strstr(protocol_name, "IR_DENON") != NULL){
if (valueBITS == 0) valueBITS = DENON_BITS;
irsend.sendDenon(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_GICABLE
if (strstr(protocol_name, "IR_GICABLE") != NULL){
if (valueBITS == 0) valueBITS = GICABLE_BITS;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, (uint16_t) GICABLE_MIN_REPEAT);
irsend.sendGICable(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_SHERWOOD
if (strstr(protocol_name, "IR_SHERWOOD") != NULL){
if (valueBITS == 0) valueBITS = SHERWOOD_BITS;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, (uint16_t) SHERWOOD_MIN_REPEAT);
irsend.sendSherwood(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MITSUBISHI
if (strstr(protocol_name, "IR_MITSUBISHI") != NULL){
if (valueBITS == 0) valueBITS = MITSUBISHI_BITS;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT);
irsend.sendMitsubishi(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_NIKAI
if (strstr(protocol_name, "IR_NIKAI") != NULL){
if (valueBITS == 0) valueBITS = NIKAI_BITS;
irsend.sendNikai(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MIDEA
if (strstr(protocol_name, "IR_MIDEA") != NULL){
if (valueBITS == 0) valueBITS = MIDEA_BITS;
irsend.sendMidea(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MAGIQUEST
if (strstr(protocol_name, "IR_MAGIQUEST") != NULL){
if (valueBITS == 0) valueBITS = MAGIQUEST_BITS;
irsend.sendMagiQuest(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_LASERTAG
if (strstr(protocol_name, "IR_LASERTAG") != NULL){
if (valueBITS == 0) valueBITS = LASERTAG_BITS;
irsend.sendLasertag(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_CARRIER_AC
if (strstr(protocol_name, "IR_CARRIER_AC") != NULL){
if (valueBITS == 0) valueBITS = CARRIER_AC_BITS;
irsend.sendCarrierAC(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_MITSUBISHI2
if (strstr(protocol_name, "IR_MITSUBISHI2") != NULL){
if (valueBITS == 0) valueBITS = MITSUBISHI_BITS;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, (uint16_t) MITSUBISHI_MIN_REPEAT);
irsend.sendMitsubishi2(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#ifdef IR_AIWA_RC_T501
if (strstr(protocol_name, "IR_MITSUBISHI") != NULL){
if (valueBITS == 0) valueBITS = AIWA_RC_T501_BITS;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, (uint16_t) AIWA_RC_T501_MIN_REPEAT);
irsend.sendAiwaRCT501(data, valueBITS, valueRPT);
signalSent = true;
}
#endif
#endif
}else{ }else{
trc(F("Using NEC protocol")); trc(F("Using NEC protocol"));
if (valueBITS == 0) valueBITS = NEC_BITS; if (valueBITS == 0) valueBITS = NEC_BITS;
@@ -791,4 +370,404 @@ void IRtoMQTT(){
} }
} }
#endif #endif
boolean sendIdentifiedProtocol(const char * protocol_name, unsigned long data, unsigned char * datastring, unsigned int valueBITS, uint16_t valueRPT){
#ifdef IR_Whynter
if (strstr(protocol_name, "IR_Whynter") != NULL){
if (valueBITS == 0) valueBITS = WHYNTER_BITS;
#ifdef ESP8266
irsend.sendWhynter(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendWhynter(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_LG
if (strstr(protocol_name, "IR_LG") != NULL){
if (valueBITS == 0) valueBITS = LG_BITS;
#ifdef ESP8266
irsend.sendLG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendLG(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_Sony
if (strstr(protocol_name, "IR_Sony") != NULL){
if (valueBITS == 0) valueBITS = SONY_12_BITS;
#ifdef ESP8266
irsend.sendSony(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSony(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_DISH
if (strstr(protocol_name, "IR_DISH") != NULL){
if (valueBITS == 0) valueBITS = DISH_BITS;
#ifdef ESP8266
irsend.sendDISH(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendDISH(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_RC5
if (strstr(protocol_name, "IR_RC5") != NULL){
if (valueBITS == 0) valueBITS = RC5_BITS;
#ifdef ESP8266
irsend.sendRC5(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC5(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_RC6
if (strstr(protocol_name, "IR_RC6") != NULL){
if (valueBITS == 0) valueBITS = RC6_MODE0_BITS;
#ifdef ESP8266
irsend.sendRC6(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendRC6(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_Sharp
if (strstr(protocol_name, "IR_Sharp") != NULL){
if (valueBITS == 0) valueBITS = SHARP_BITS;
#ifdef ESP8266
irsend.sendSharpRaw(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSharpRaw(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_SAMSUNG
if (strstr(protocol_name, "IR_SAMSUNG") != NULL){
if (valueBITS == 0) valueBITS = SAMSUNG_BITS;
#ifdef ESP8266
irsend.sendSAMSUNG(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendSAMSUNG(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_JVC
if (strstr(protocol_name, "IR_JVC") != NULL){
if (valueBITS == 0) valueBITS = JVC_BITS;
#ifdef ESP8266
irsend.sendJVC(data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendJVC(data, valueBITS);
#endif
return true;
}
#endif
#ifdef IR_PANASONIC
if (strstr(protocol_name, "IR_PANASONIC") != NULL){
#ifdef ESP8266
if (valueBITS == 0) valueBITS = PANASONIC_BITS;
irsend.sendPanasonic(PanasonicAddress, data, valueBITS, valueRPT);
#else
for (int i=0; i <= valueRPT; i++) irsend.sendPanasonic(PanasonicAddress, data);
#endif
return true;
}
#endif
#ifdef ESP8266 // sendings not available on arduino
#ifdef IR_COOLIX
if (strstr(protocol_name, "IR_COOLIX") != NULL){
if (valueBITS == 0) valueBITS = kCoolixBits;
irsend.sendCOOLIX(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_RCMM
if (strstr(protocol_name, "IR_RCMM") != NULL){
if (valueBITS == 0) valueBITS = kRCMMBits;
irsend.sendRCMM(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_DENON
if (strstr(protocol_name, "IR_DENON") != NULL){
if (valueBITS == 0) valueBITS = DENON_BITS;
irsend.sendDenon(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_GICABLE
if (strstr(protocol_name, "IR_GICABLE") != NULL){
if (valueBITS == 0) valueBITS = kGicableBits;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, kGicableMinRepeat);
irsend.sendGICable(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_SHERWOOD
if (strstr(protocol_name, "IR_SHERWOOD") != NULL){
if (valueBITS == 0) valueBITS = kSherwoodBits;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, kSherwoodMinRepeat);
irsend.sendSherwood(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MITSUBISHI
if (strstr(protocol_name, "IR_MITSUBISHI") != NULL){
if (valueBITS == 0) valueBITS = kMitsubishiBits;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, kMitsubishiMinRepeat);
irsend.sendMitsubishi(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_NIKAI
if (strstr(protocol_name, "IR_NIKAI") != NULL){
if (valueBITS == 0) valueBITS = kNikaiBits;
irsend.sendNikai(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MIDEA
if (strstr(protocol_name, "IR_MIDEA") != NULL){
if (valueBITS == 0) valueBITS = kMideaBits;
irsend.sendMidea(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MAGIQUEST
if (strstr(protocol_name, "IR_MAGIQUEST") != NULL){
if (valueBITS == 0) valueBITS = kMagiquestBits;
irsend.sendMagiQuest(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_LASERTAG
if (strstr(protocol_name, "IR_LASERTAG") != NULL){
if (valueBITS == 0) valueBITS = kLasertagBits;
irsend.sendLasertag(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_CARRIER_AC
if (strstr(protocol_name, "IR_CARRIER_AC") != NULL){
if (valueBITS == 0) valueBITS = kCarrierAcBits;
irsend.sendCarrierAC(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MITSUBISHI2
if (strstr(protocol_name, "IR_MITSUBISHI2") != NULL){
if (valueBITS == 0) valueBITS = kMitsubishiBits;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, kMitsubishiMinRepeat);
irsend.sendMitsubishi2(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_AIWA_RC_T501
if (strstr(protocol_name, "IR_AIWA_RC_T501") != NULL){
if (valueBITS == 0) valueBITS = kAiwaRcT501Bits;
if (valueRPT == repeatIRwNumber) valueRPT = std::max(valueRPT, kAiwaRcT501MinRepeats);
irsend.sendAiwaRCT501(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_SANYO
if (strstr(protocol_name, "IR_SANYO") != NULL){
if (valueBITS == 0) valueBITS = kSanyoLC7461Bits;
irsend.sendSanyoLC7461(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_DAIKIN
if (strstr(protocol_name, "IR_DAIKIN") != NULL){
if (valueBITS == 0) valueBITS = kDaikinStateLength;
irsend.sendDaikin(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_KELVINATOR
if (strstr(protocol_name, "IR_KELVINATOR") != NULL){
if (valueBITS == 0) valueBITS = kKelvinatorStateLength;
irsend.sendKelvinator(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MITSUBISHI_AC
if (strstr(protocol_name, "IR_MITSUBISHI_AC") != NULL){
if (valueBITS == 0) valueBITS = kMitsubishiACStateLength;
irsend.sendMitsubishiAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_SANYO
if (strstr(protocol_name, "IR_SANYO") != NULL){
if (valueBITS == 0) valueBITS = kSanyoLC7461Bits;
irsend.sendSanyoLC7461(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_GREE
if (strstr(protocol_name, "IR_GREE") != NULL){
if (valueBITS == 0) valueBITS = kGreeStateLength;
irsend.sendGree(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_ARGO
if (strstr(protocol_name, "IR_ARGO") != NULL){
if (valueBITS == 0) valueBITS = kArgoStateLength;
irsend.sendArgo(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_TROTEC
if (strstr(protocol_name, "IR_TROTEC") != NULL){
if (valueBITS == 0) valueBITS = kTrotecStateLength;
irsend.sendTrotec(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_TOSHIBA_AC
if (strstr(protocol_name, "IR_TOSHIBA_AC") != NULL){
if (valueBITS == 0) valueBITS = kToshibaACBits;
irsend.sendToshibaAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_FUJITSU_AC
if (strstr(protocol_name, "IR_FUJITSU_AC") != NULL){
irsend.sendFujitsuAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MAGIQUEST
if (strstr(protocol_name, "IR_MAGIQUEST") != NULL){
if (valueBITS == 0) valueBITS = kMagiquestBits;
irsend.sendMagiQuest(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_HAIER_AC
if (strstr(protocol_name, "IR_HAIER_AC") != NULL){
if (valueBITS == 0) valueBITS = kHaierACStateLength;
irsend.sendHaierAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_HITACHI_AC
if (strstr(protocol_name, "IR_HITACHI_AC") != NULL){
if (valueBITS == 0) valueBITS = kHitachiAc2StateLength;
irsend.sendHitachiAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_HITACHI_AC1
if (strstr(protocol_name, "IR_HITACHI_AC1") != NULL){
if (valueBITS == 0) valueBITS = kHitachiAc2StateLength;
irsend.sendHitachiAC1(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_HITACHI_AC2
if (strstr(protocol_name, "IR_HITACHI_AC2") != NULL){
if (valueBITS == 0) valueBITS = kHitachiAc2StateLength;
irsend.sendHitachiAC2(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_GICABLE
if (strstr(protocol_name, "IR_GICABLE") != NULL){
if (valueBITS == 0) valueBITS = kGicableBits;
irsend.sendGICable(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_HAIER_AC_YRW02
if (strstr(protocol_name, "IR_HAIER_AC_YRW02") != NULL){
if (valueBITS == 0) valueBITS = kHaierACYRW02StateLength;
irsend.sendHaierACYRW02(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_WHIRLPOOL_AC
if (strstr(protocol_name, "IR_WHIRLPOOL_AC") != NULL){
if (valueBITS == 0) valueBITS = kWhirlpoolAcStateLength;
irsend.sendWhirlpoolAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_SAMSUNG_AC
if (strstr(protocol_name, "IR_SAMSUNG_AC") != NULL){
if (valueBITS == 0) valueBITS = kSamsungAcStateLength;
irsend.sendSamsungAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_LUTRON
if (strstr(protocol_name, "IR_LUTRON") != NULL){
if (valueBITS == 0) valueBITS = kLutronBits;
irsend.sendLutron(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_ELECTRA_AC
if (strstr(protocol_name, "IR_ELECTRA_AC") != NULL){
if (valueBITS == 0) valueBITS = kElectraAcStateLength;
irsend.sendElectraAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_PANASONIC_AC
if (strstr(protocol_name, "IR_PANASONIC_AC") != NULL){
if (valueBITS == 0) valueBITS = kPanasonicAcStateLength;
irsend.sendPanasonicAC(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_PIONEER
if (strstr(protocol_name, "IR_PIONEER") != NULL){
if (valueBITS == 0) valueBITS = kPioneerBits;
irsend.sendPioneer(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_LG2
if (strstr(protocol_name, "IR_LG2") != NULL){
if (valueBITS == 0) valueBITS = kLgBits;
irsend.sendLG2(data, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_MWM
if (strstr(protocol_name, "IR_MWM") != NULL){
irsend.sendMWM(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_DAIKIN2
if (strstr(protocol_name, "IR_DAIKIN2") != NULL){
if (valueBITS == 0) valueBITS = kDaikin2StateLength;
irsend.sendDaikin2(datastring, valueBITS, valueRPT);
return true;
}
#endif
#ifdef IR_VESTEL_AC
if (strstr(protocol_name, "IR_VESTEL_AC") != NULL){
if (valueBITS == 0) valueBITS = kVestelACBits;
irsend.sendVestelAC(data, valueBITS, valueRPT);
return true;
}
#endif
return false;
#endif
}
#endif #endif
+30 -3
View File
@@ -68,6 +68,33 @@
#define IR_DENON #define IR_DENON
#define IR_AIWA_RC_T501 #define IR_AIWA_RC_T501
#define IR_JVC #define IR_JVC
#define IR_SANYO
#define IR_DAIKIN
#define IR_KELVINATOR
#define IR_MITSUBISHI_AC
#define IR_SANYO_LC7461
#define IR_GREE
#define IR_ARGO
#define IR_TROTEC
#define IR_TOSHIBA_AC
#define IR_FUJITSU_AC
#define IR_MAGIQUEST
#define IR_HAIER_AC
#define IR_HITACHI_AC
#define IR_HITACHI_AC1
#define IR_HITACHI_AC2
#define IR_GICABLE
#define IR_HAIER_AC_YRW02
#define IR_WHIRLPOOL_AC
#define IR_SAMSUNG_AC
#define IR_LUTRON
#define IR_ELECTRA_AC
#define IR_PANASONIC_AC
#define IR_PIONEER
#define IR_LG2
#define IR_MWM
#define IR_DAIKIN2
#define IR_VESTEL_AC
#elif ESP32 #elif ESP32
#define IR_Raw #define IR_Raw
#define IR_COOLIX #define IR_COOLIX
@@ -105,14 +132,14 @@
/*-------------------PIN DEFINITIONS----------------------*/ /*-------------------PIN DEFINITIONS----------------------*/
#ifdef ESP8266 #ifdef ESP8266
#define IR_RECEIVER_PIN 2 #define IR_RECEIVER_PIN 2 //D4
#define IR_EMITTER_PIN 16 // replace by 0 (D3) if you use IR LOLIN controller shield #define IR_EMITTER_PIN 16 //D0/ replace by 0 (D3) if you use IR LOLIN controller shield
#elif ESP32 #elif ESP32
#define IR_RECEIVER_PIN 27 #define IR_RECEIVER_PIN 27
#define IR_EMITTER_PIN 14 #define IR_EMITTER_PIN 14
#elif __AVR_ATmega2560__ #elif __AVR_ATmega2560__
#define IR_RECEIVER_PIN 2 // 2 = D2 on mega #define IR_RECEIVER_PIN 2 // 2 = D2 on mega
#define IR_EMITTER_PIN 7 #define IR_EMITTER_PIN 9
#else #else
#define IR_RECEIVER_PIN 0 // 0 = D2 on arduino #define IR_RECEIVER_PIN 0 // 0 = D2 on arduino
#define IR_EMITTER_PIN 9 #define IR_EMITTER_PIN 9
+2
View File
@@ -10,6 +10,8 @@
- [Jonny Graham](https://github.com/jonnygraham/) - [Jonny Graham](https://github.com/jonnygraham/)
- [Stu Fisher](https://github.com/stufisher/) - [Stu Fisher](https://github.com/stufisher/)
- [Jorge Cisneros](https://github.com/jorgecis/) - [Jorge Cisneros](https://github.com/jorgecis/)
- [Denes Varga](https://github.com/denxhun/)
- [Brett T. Warden](https://github.com/bwarden/)
All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors). All contributors can be found on the [contributors site](https://github.com/markszabo/IRremoteESP8266/graphs/contributors).
+4
View File
@@ -35,8 +35,12 @@ test/*_test
tools/*.o tools/*.o
tools/*.a tools/*.a
tools/gc_decode tools/gc_decode
tools/mode2_decode
.pioenvs .pioenvs
.piolibdeps .piolibdeps
.clang_complete .clang_complete
.gcc-flags.json .gcc-flags.json
#Cygwin builds
*.exe
+1
View File
@@ -1,3 +1,4 @@
[submodule "lib/googletest"] [submodule "lib/googletest"]
path = lib/googletest path = lib/googletest
url = https://github.com/google/googletest.git url = https://github.com/google/googletest.git
branch = v1.8.x
+6 -5
View File
@@ -1,14 +1,14 @@
language: c language: c
env: env:
- BD=esp8266:esp8266:nodemcuv2:CpuFrequency=80,FlashSize=4M3M - BD=esp8266:esp8266:nodemcuv2:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled
- BD=esp8266:esp8266:d1_mini:CpuFrequency=80,FlashSize=4M3M - BD=esp8266:esp8266:d1_mini:xtal=80,eesz=4M3M,ip=lm2f,exception=disabled
before_install: before_install:
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16" - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16"
- sleep 3 - sleep 3
- export DISPLAY=:1.0 - export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-1.8.2-linux64.tar.xz - wget http://downloads.arduino.cc/arduino-1.8.8-linux64.tar.xz
- tar xf arduino-1.8.2-linux64.tar.xz - tar xf arduino-1.8.8-linux64.tar.xz
- sudo mv arduino-1.8.2 /usr/local/share/arduino - sudo mv arduino-1.8.8 /usr/local/share/arduino
- sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino - sudo ln -s /usr/local/share/arduino/arduino /usr/local/bin/arduino
- wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py - wget https://raw.githubusercontent.com/google/styleguide/gh-pages/cpplint/cpplint.py
install: install:
@@ -40,6 +40,7 @@ script:
- arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino - arduino --verify --board $BD $PWD/examples/TurnOnArgoAC/TurnOnArgoAC.ino
- arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino - arduino --verify --board $BD $PWD/examples/IRMQTTServer/IRMQTTServer.ino
- arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino - arduino --verify --board $BD $PWD/examples/TurnOnToshibaAC/TurnOnToshibaAC.ino
- arduino --verify --board $BD $PWD/examples/ControlSamsungAC/ControlSamsungAC.ino
# Also check the tools programs compile. # Also check the tools programs compile.
- (cd tools; make all) - (cd tools; make all)
# Check for lint issues. # Check for lint issues.
+19 -3
View File
@@ -1,17 +1,33 @@
# IRremote ESP8266 Library # IRremote ESP8266 Library
[![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266) [![Build Status](https://travis-ci.org/markszabo/IRremoteESP8266.svg?branch=master)](https://travis-ci.org/markszabo/IRremoteESP8266)
[![arduino-library-badge](https://www.ardu-badge.com/badge/IRremoteESP8266.svg?)](https://www.ardu-badge.com/IRremoteESP8266)
[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Average time to resolve an issue")
[![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open") [![Percentage of issues still open](http://isitmaintained.com/badge/open/markszabo/IRremoteESP8266.svg)](http://isitmaintained.com/project/markszabo/IRremoteESP8266 "Percentage of issues still open")
[![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266) [![GitLicense](https://gitlicense.com/badge/markszabo/IRremoteESP8266)](https://gitlicense.com/license/markszabo/IRremoteESP8266)
This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc. This library enables you to **send _and_ receive** infra-red signals on an [ESP8266 using the Arduino framework](https://github.com/esp8266/Arduino) using common 940nm IR LEDs and common IR receiver modules. e.g. TSOP{17,22,24,36,38,44,48}* etc.
## v2.4.2 Now Available ## v2.5.5 Now Available
Version 2.4.2 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes. Version 2.5.5 of the library is now [available](https://github.com/markszabo/IRremoteESP8266/releases/latest). You can view the [Release Notes](ReleaseNotes.md) for all the significant changes.
#### Upgrading from pre-v2.0 #### Upgrading from pre-v2.0
Usage of the library slight changed at v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page. Usage of the library has been slightly changed in v2.0. You will need to change your usage to work with v2.0 and beyond. You can read more about the changes required on our [Upgrade to v2.0](https://github.com/markszabo/IRremoteESP8266/wiki/Upgrading-to-v2.0) page.
#### Upgrading from pre-v2.5
The library has changed from using constants declared as `#define` to
[const](https://google.github.io/styleguide/cppguide.html#Constant_Names) with
the appropriate naming per the
[C++ style guide](https://google.github.io/styleguide/cppguide.html).
This may potentially cause old programs to not compile.
The most likely externally used `#define`s have been _aliased_ for limited
backward compatibility for projects using the old style. Going forward, only the
new `kConstantName` style will be supported for new protocol additions.
In the unlikely case it does break your code, then you may have been referencing
something you likely should not have. You should be able to quickly determine
the new name from the old. e.g. `CONSTANT_NAME` to `kConstantName`.
Use common sense or examining the library's code if this does affect code.
## Troubleshooting ## Troubleshooting
Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first. Before reporting an issue or asking for help, please try to follow our [Troubleshooting Guide](https://github.com/markszabo/IRremoteESP8266/wiki/Troubleshooting-Guide) first.
+120
View File
@@ -1,5 +1,125 @@
# Release Notes # Release Notes
## _v2.5.5 (20190207)_
**[Bug Fixes]**
- Fix decoding of Samsung A/C Extended messages. (#610)
- Fix IRMQTTServer example to work with GPIO0 as IR_RX (#608)
- Fix incorrect #define usage. (#597, #596)
**[Features]**
- Add deep decoding/construction of Daikin2 messages (#600)
- Added Old Vestel A/C support (56 Bits) with full functions. (#607)
**[Misc]**
- Add ControlSamsungAC example code. (#599)
- Add how to send a state/air-con to IRsendDemo.ino (#594)
## _v2.5.4 (20190102)_
**[Features]**
- Experimental basic support for 39 Byte Daikin A/C (#583)
- Handle send() repeats in A/C classes. Improve Coolix support. (#580)
- Add optional RX pin pullup and dump raw messages in IRMQTTServer.ino (#589)
**[Misc]**
- Make auto_analyse_raw_data.py work with Python3 (#581)
- Update CI/travis config due to esp8266 core 2.5.0 changes (#591)
## _v2.5.3 (20181123)_
**[Features]**
- Add deep support for the Hitachi 28-Byte A/C Protocol (#563)
- Deep decoding for Whirlpool A/C (#572)
- Improve security options for IRMQTTServer example. (#575)
- Require a changed firmware password before upload. (#576)
**[Misc]**
- Add missing '}' in output of Auto analyse. (#562)
- Make A/C example code a bit more simple. (#571)
## _v2.5.2 (20181021)_
**[Bug Fixes]**
- Add missing send() method to IRPanasonicAC class. (#545)
- Add missing sendWhirlpoolAC() to IRMQTTServer.ino (#558)
**[Features]**
- Add IR receiving support to IRMQTTServer. (#543)
- Pioneer support (#547)
- Add support for a second LG protocol variant. (#552)
- Support for short Panasonic A/C messages. (#553)
- Add support for Panasonic CKP series A/Cs. (#554)
- Experimental timer/clock support for Panasonic A/Cs. (#546)
- Add Made With Magic (MWM) support (#557)
**[Misc]**
- Grammar and typo fixes (#541, #549)
- Increase Panasonic A/C message tolerances. (#542)
- Added command mode2_decode in tools/ (#557)
- General code style cleanup (#560)
## _v2.5.1 (20181002)_
**[Bug Fixes]**
- Correct the byte used for Samsung AC Swing. (#529)
- Fix not sending Samsung A/C messages in IRMQTTServer. (#529)
**[Features]**
- Experimental support for Electra A/C messages. (#528)
- Experimental support for Panasonic A/C messages. (#535)
- Samsung A/C fixes & improvements (#529)
- IRMQTTServer v0.6.0 (#530)
**[Misc]**
- Change required WifiManager lib version to v0.14
- Add alias for RAWTICK to kRawTick. (#535)
- Update sendLutron() status. (#515)
- Remove leftover debug message in IRrecvDumpV2 (#526)
## _v2.5.0 (20180919)_
**[Bug Fixes]**
- Fix HTML menu error for GICABLE in IRMQTTServer. (#516)
- Fix Mitsubishi A/C mode setting. (#514)
- Add missing ',' in auto analyse tool generated code. (#513)
- Fix Fujitsu checksum validation. (#501)
- Remove errant Repeat debug statement in IRMQTTServer. (#507)
**[Features]**
- Mitsubishi A/C decode improvements. (#514)
- Basic support for Whirlpool A/C messages. (#511)
- Basic support for Samsung A/C messages. (#512)
- Experimental support for detailed Samsung A/C messages. (#521)
- Experimental support for detailed Coolix A/C messages. (#518)
- Experimental support for Lutron protocol. (#516)
- Calculate and use average values for timings in analysing tool. (#513)
**[Misc]**
- Style change from using #define's for constants to `const kConstantName`.
- Improve the JVC example code.
## _v2.4.3 (20180727)_
**[Bug Fixes]**
- Handle Space Gaps better in auto analyse tool. (#482)
- Correct min repeat for GICABLE in IRMQTTServer. (#494)
**[Features]**
- Add static IP config option to IRMQTTServer (#480)
- Full decoding/encoding support for the Haier YRW02 A/C. (#485 #486 #487)
**[Misc]**
- Update LG (28-bit) HDR mark and space timings. (#492)
- Spelling and grammar fixes (#491)
## _v2.4.2 (20180601)_ ## _v2.4.2 (20180601)_
**[Bug Fixes]** **[Bug Fixes]**
@@ -0,0 +1,99 @@
/* Copyright 2019 David Conran
*
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by kIrLed below.
*
* TL;DR: The IR LED needs to be driven by a transistor for a good result.
*
* Suggested circuit:
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
*
* Common mistakes & tips:
* * Don't just connect the IR LED directly to the pin, it won't
* have enough current to drive the IR LED effectively.
* * Make sure you have the IR LED polarity correct.
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
* * Typical digital camera/phones can be used to see if the IR LED is flashed.
* Replace the IR LED with a normal LED if you don't have a digital camera
* when debugging.
* * Avoid using the following pins unless you really know what you are doing:
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
* for your first time. e.g. ESP-12 etc.
*/
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Samsung.h>
const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRSamsungAc ac(kIrLed); // Set the GPIO used for sending messages.
void printState() {
// Display the settings.
Serial.println("Samsung A/C remote is in the following state:");
Serial.printf(" %s\n", ac.toString().c_str());
}
void setup() {
ac.begin();
Serial.begin(115200);
delay(200);
// Set up what we want to send. See ir_Samsung.cpp for all the options.
Serial.println("Default state of the remote.");
printState();
Serial.println("Setting initial state for A/C.");
ac.off();
ac.setFan(kSamsungAcFanLow);
ac.setMode(kSamsungAcCool);
ac.setTemp(25);
ac.setSwing(false);
printState();
}
void loop() {
// Turn the A/C unit on and set to cooling mode.
// Power changes require we send an extended message.
Serial.println("Sending an extended IR command to A/C ...");
ac.on();
ac.setMode(kSamsungAcCool);
ac.sendExtended();
printState();
delay(15000); // wait 15 seconds
// Increase the fan speed.
Serial.println("Sending a normal IR command to A/C ...");
ac.setFan(kSamsungAcFanHigh);
ac.send();
printState();
delay(15000);
// Change to swing the fan.
Serial.println("Sending a normal IR command to A/C ...");
ac.setSwing(true);
ac.send();
printState();
delay(15000);
// Change to Fan mode, lower the speed, and stop the swing.
Serial.println("Sending a normal IR command to A/C ...");
ac.setSwing(false);
ac.setMode(kSamsungAcFan);
ac.setFan(kSamsungAcFanLow);
ac.send();
printState();
delay(15000);
// Turn the A/C unit off.
// Power changes require we send an extended message.
Serial.println("Sending an extended IR command to A/C ...");
ac.off();
ac.sendExtended();
printState();
delay(15000); // wait 15 seconds
}
@@ -0,0 +1,17 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -24,12 +24,13 @@
* have enough current to drive the IR LED effectively. * have enough current to drive the IR LED effectively.
* * Make sure you have the IR LED polarity correct. * * Make sure you have the IR LED polarity correct.
* See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity * See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
* * Typical digital camera/phones can be used to see if the IR LED is flashed. * * Typical digital camera/phones can be used to see if the IR LED is
* Replace the IR LED with a normal LED if you don't have a digital camera * flashed. Replace the IR LED with a normal LED if you don't have a digital
* when debugging. * camera when debugging.
* * Avoid using the following pins unless you really know what you are doing: * * Avoid using the following pins unless you really know what you are doing:
* * Pin 0/D3: Can interfere with the boot/program mode & support circuits. * * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
* * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere. * * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will
* interfere.
* * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere. * * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
* * ESP-01 modules are tricky. We suggest you use a module with more GPIOs * * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
* for your first time. e.g. ESP-12 etc. * for your first time. e.g. ESP-12 etc.
@@ -45,11 +46,11 @@
// These codes can be found in GC's Control Tower database. // These codes can be found in GC's Control Tower database.
uint16_t Samsung_power_toggle[71] = { uint16_t Samsung_power_toggle[71] = {
38000, 1, 1, 170, 170, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20, 38000, 1, 1, 170, 170, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20,
20, 20, 20, 20, 63, 20, 63, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20, 63, 20, 63, 20, 20, 20,
20, 20, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20, 20, 20, 20,
20, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 1798}; 20, 20, 20, 20, 20, 20, 20, 20, 20, 63, 20, 20, 20, 63, 20,
63, 20, 63, 20, 63, 20, 63, 20, 63, 20, 1798};
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). #define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2).
@@ -9,7 +9,7 @@
* For more codes, visit: https://irdb.globalcache.com/ * For more codes, visit: https://irdb.globalcache.com/
* *
* How to use this program: * How to use this program:
* 1) Update "ssid" and "password" below for your WIFI network. * 1) Update "kSsid" and "kPassword" below for your WIFI network.
* 2) Compile and upload the sketch to your ESP8266 module. * 2) Compile and upload the sketch to your ESP8266 module.
* 3) (Optional) Use the serial connection to confirm it started and get the * 3) (Optional) Use the serial connection to confirm it started and get the
* IP address. * IP address.
@@ -46,8 +46,8 @@
#include <WiFiClient.h> #include <WiFiClient.h>
#include <WiFiServer.h> #include <WiFiServer.h>
const char* ssid = "..."; // Put your WIFI SSID here. const char* kSsid = "..."; // Put your WIFI SSID here.
const char* password = "..."; // Put your WIFI password here. const char* kPassword = "..."; // Put your WIFI Password here.
WiFiServer server(4998); // Uses port 4998. WiFiServer server(4998); // Uses port 4998.
WiFiClient client; WiFiClient client;
@@ -103,7 +103,7 @@ void setup() {
delay(100); delay(100);
Serial.println(" "); Serial.println(" ");
Serial.println("IR TCP Server"); Serial.println("IR TCP Server");
WiFi.begin(ssid, password); WiFi.begin(kSsid, kPassword);
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
delay(900); delay(900);
Serial.print("."); Serial.print(".");
File diff suppressed because it is too large. Load diff
@@ -7,7 +7,7 @@ build_flags = -DMQTT_MAX_PACKET_SIZE=512
lib_deps_builtin = lib_deps_builtin =
lib_deps_external = lib_deps_external =
PubSubClient PubSubClient
WifiManager@0.12 WifiManager@0.14
[env:nodemcuv2] [env:nodemcuv2]
platform = espressif8266 platform = espressif8266
@@ -4,7 +4,7 @@
* Copyright 2015 Mark Szabo * Copyright 2015 Mark Szabo
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -36,15 +36,15 @@
#include <IRsend.h> #include <IRsend.h>
#include <WiFiClient.h> #include <WiFiClient.h>
const char* ssid = "....."; const char* kSsid = ".....";
const char* password = "....."; const char* kPassword = ".....";
MDNSResponder mdns; MDNSResponder mdns;
ESP8266WebServer server(80); ESP8266WebServer server(80);
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
void handleRoot() { void handleRoot() {
server.send(200, "text/html", server.send(200, "text/html",
@@ -90,7 +90,7 @@ void setup(void) {
irsend.begin(); irsend.begin();
Serial.begin(115200); Serial.begin(115200);
WiFi.begin(ssid, password); WiFi.begin(kSsid, kPassword);
Serial.println(""); Serial.println("");
// Wait for connection // Wait for connection
@@ -100,7 +100,7 @@ void setup(void) {
} }
Serial.println(""); Serial.println("");
Serial.print("Connected to "); Serial.print("Connected to ");
Serial.println(ssid); Serial.println(kSsid);
Serial.print("IP address: "); Serial.print("IP address: ");
Serial.println(WiFi.localIP().toString()); Serial.println(WiFi.localIP().toString());
@@ -3,7 +3,7 @@
* This is very simple teaching code to show you how to use the library. * This is very simple teaching code to show you how to use the library.
* If you are trying to decode your Infra-Red remote(s) for later replay, * If you are trying to decode your Infra-Red remote(s) for later replay,
* use the IRrecvDumpV2.ino example code instead of this. * use the IRrecvDumpV2.ino example code instead of this.
* An IR detector/demodulator must be connected to the input RECV_PIN. * An IR detector/demodulator must be connected to the input kRecvPin.
* Copyright 2009 Ken Shirriff, http://arcfn.com * Copyright 2009 Ken Shirriff, http://arcfn.com
* Example circuit diagram: * Example circuit diagram:
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
@@ -25,9 +25,9 @@
// An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU // An IR detector/demodulator is connected to GPIO pin 14(D5 on a NodeMCU
// board). // board).
uint16_t RECV_PIN = 14; const uint16_t kRecvPin = 14;
IRrecv irrecv(RECV_PIN); IRrecv irrecv(kRecvPin);
decode_results results; decode_results results;
@@ -38,7 +38,7 @@ void setup() {
delay(50); delay(50);
Serial.println(); Serial.println();
Serial.print("IRrecvDemo is now running and waiting for IR message on Pin "); Serial.print("IRrecvDemo is now running and waiting for IR message on Pin ");
Serial.println(RECV_PIN); Serial.println(kRecvPin);
} }
void loop() { void loop() {
@@ -86,10 +86,10 @@ void dump(decode_results *results) {
if (i % 100 == 0) if (i % 100 == 0)
yield(); // Preemptive yield every 100th entry to feed the WDT. yield(); // Preemptive yield every 100th entry to feed the WDT.
if (i & 1) { if (i & 1) {
Serial.print(results->rawbuf[i] * RAWTICK, DEC); Serial.print(results->rawbuf[i] * kRawTick, DEC);
} else { } else {
Serial.print(", "); Serial.print(", ");
Serial.print((uint32_t) results->rawbuf[i] * RAWTICK, DEC); Serial.print((uint32_t) results->rawbuf[i] * kRawTick, DEC);
} }
} }
Serial.println("};"); Serial.println("};");
@@ -1,6 +1,6 @@
/* /*
* IRremoteESP8266: IRrecvDumpV2 - dump details of IR codes with IRrecv * IRremoteESP8266: IRrecvDumpV2 - dump details of IR codes with IRrecv
* An IR detector/demodulator must be connected to the input RECV_PIN. * An IR detector/demodulator must be connected to the input kRecvPin.
* *
* Copyright 2009 Ken Shirriff, http://arcfn.com * Copyright 2009 Ken Shirriff, http://arcfn.com
* Copyright 2017 David Conran * Copyright 2017 David Conran
@@ -9,6 +9,8 @@
* https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving * https://github.com/markszabo/IRremoteESP8266/wiki#ir-receiving
* *
* Changes: * Changes:
* Version 0.4 July, 2018
* - Minor improvements and more A/C unit support.
* Version 0.3 November, 2017 * Version 0.3 November, 2017
* - Support for A/C decoding for some protcols. * - Support for A/C decoding for some protcols.
* Version 0.2 April, 2017 * Version 0.2 April, 2017
@@ -20,36 +22,43 @@
#ifndef UNIT_TEST #ifndef UNIT_TEST
#include <Arduino.h> #include <Arduino.h>
#endif #endif
#include <IRremoteESP8266.h>
#include <IRrecv.h> #include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRutils.h> #include <IRutils.h>
#if DECODE_AC // The following are only needed for extended decoding of A/C Messages
#include <ir_Coolix.h>
#include <ir_Daikin.h> #include <ir_Daikin.h>
#include <ir_Fujitsu.h> #include <ir_Fujitsu.h>
#include <ir_Gree.h> #include <ir_Gree.h>
#include <ir_Haier.h> #include <ir_Haier.h>
#include <ir_Hitachi.h>
#include <ir_Kelvinator.h> #include <ir_Kelvinator.h>
#include <ir_Midea.h> #include <ir_Midea.h>
#include <ir_Mitsubishi.h>
#include <ir_Panasonic.h>
#include <ir_Samsung.h>
#include <ir_Toshiba.h> #include <ir_Toshiba.h>
#endif // DECODE_AC #include <ir_Vestel.h>
#include <ir_Whirlpool.h>
// ==================== start of TUNEABLE PARAMETERS ==================== // ==================== start of TUNEABLE PARAMETERS ====================
// An IR detector/demodulator is connected to GPIO pin 14 // An IR detector/demodulator is connected to GPIO pin 14
// e.g. D5 on a NodeMCU board. // e.g. D5 on a NodeMCU board.
#define RECV_PIN 14 const uint16_t kRecvPin = 14;
// The Serial connection baud rate. // The Serial connection baud rate.
// i.e. Status message will be sent to the PC at this baud rate. // i.e. Status message will be sent to the PC at this baud rate.
// Try to avoid slow speeds like 9600, as you will miss messages and // Try to avoid slow speeds like 9600, as you will miss messages and
// cause other problems. 115200 (or faster) is recommended. // cause other problems. 115200 (or faster) is recommended.
// NOTE: Make sure you set your Serial Monitor to the same speed. // NOTE: Make sure you set your Serial Monitor to the same speed.
#define BAUD_RATE 115200 const uint32_t kBaudRate = 115200;
// As this program is a special purpose capture/decoder, let us use a larger // As this program is a special purpose capture/decoder, let us use a larger
// than normal buffer so we can handle Air Conditioner remote codes. // than normal buffer so we can handle Air Conditioner remote codes.
#define CAPTURE_BUFFER_SIZE 1024 const uint16_t kCaptureBufferSize = 1024;
// TIMEOUT is the Nr. of milli-Seconds of no-more-data before we consider a // kTimeout is the Nr. of milli-Seconds of no-more-data before we consider a
// message ended. // message ended.
// This parameter is an interesting trade-off. The longer the timeout, the more // This parameter is an interesting trade-off. The longer the timeout, the more
// complex a message it can capture. e.g. Some device protocols will send // complex a message it can capture. e.g. Some device protocols will send
@@ -61,33 +70,34 @@
// them is often also around 20+ms. This can result in the raw data be 2-3+ // them is often also around 20+ms. This can result in the raw data be 2-3+
// times larger than needed as it has captured 2-3+ messages in a single // times larger than needed as it has captured 2-3+ messages in a single
// capture. Setting a low timeout value can resolve this. // capture. Setting a low timeout value can resolve this.
// So, choosing the best TIMEOUT value for your use particular case is // So, choosing the best kTimeout value for your use particular case is
// quite nuanced. Good luck and happy hunting. // quite nuanced. Good luck and happy hunting.
// NOTE: Don't exceed MAX_TIMEOUT_MS. Typically 130ms. // NOTE: Don't exceed kMaxTimeoutMs. Typically 130ms.
#if DECODE_AC #if DECODE_AC
#define TIMEOUT 50U // Some A/C units have gaps in their protocols of ~40ms. // Some A/C units have gaps in their protocols of ~40ms. e.g. Kelvinator
// e.g. Kelvinator
// A value this large may swallow repeats of some protocols // A value this large may swallow repeats of some protocols
const uint8_t kTimeout = 50;
#else // DECODE_AC #else // DECODE_AC
#define TIMEOUT 15U // Suits most messages, while not swallowing many repeats. // Suits most messages, while not swallowing many repeats.
const uint8_t kTimeout = 15;
#endif // DECODE_AC #endif // DECODE_AC
// Alternatives: // Alternatives:
// #define TIMEOUT 90U // Suits messages with big gaps like XMP-1 & some aircon // const uint8_t kTimeout = 90;
// units, but can accidentally swallow repeated messages // Suits messages with big gaps like XMP-1 & some aircon units, but can
// in the rawData[] output. // accidentally swallow repeated messages in the rawData[] output.
// #define TIMEOUT MAX_TIMEOUT_MS // This will set it to our currently allowed //
// maximum. Values this high are problematic // const uint8_t kTimeout = kMaxTimeoutMs;
// because it is roughly the typical boundary // This will set it to our currently allowed maximum.
// Values this high are problematic because it is roughly the typical boundary
// where most messages repeat. // where most messages repeat.
// e.g. It will stop decoding a message and // e.g. It will stop decoding a message and start sending it to serial at
// start sending it to serial at precisely // precisely the time when the next message is likely to be transmitted,
// the time when the next message is likely // and may miss it.
// to be transmitted, and may miss it.
// Set the smallest sized "UNKNOWN" message packets we actually care about. // Set the smallest sized "UNKNOWN" message packets we actually care about.
// This value helps reduce the false-positive detection rate of IR background // This value helps reduce the false-positive detection rate of IR background
// noise as real messages. The chances of background IR noise getting detected // noise as real messages. The chances of background IR noise getting detected
// as a message increases with the length of the TIMEOUT value. (See above) // as a message increases with the length of the kTimeout value. (See above)
// The downside of setting this message too large is you can miss some valid // The downside of setting this message too large is you can miss some valid
// short messages for protocols that this library doesn't yet decode. // short messages for protocols that this library doesn't yet decode.
// //
@@ -96,12 +106,11 @@
// Set lower if you are sure your setup is working, but it doesn't see messages // Set lower if you are sure your setup is working, but it doesn't see messages
// from your device. (e.g. Other IR remotes work.) // from your device. (e.g. Other IR remotes work.)
// NOTE: Set this value very high to effectively turn off UNKNOWN detection. // NOTE: Set this value very high to effectively turn off UNKNOWN detection.
#define MIN_UNKNOWN_SIZE 12 const uint16_t kMinUnknownSize = 12;
// ==================== end of TUNEABLE PARAMETERS ==================== // ==================== end of TUNEABLE PARAMETERS ====================
// Use turn on the save buffer feature for more complete capture coverage. // Use turn on the save buffer feature for more complete capture coverage.
IRrecv irrecv(RECV_PIN, CAPTURE_BUFFER_SIZE, TIMEOUT, true); IRrecv irrecv(kRecvPin, kCaptureBufferSize, kTimeout, true);
decode_results results; // Somewhere to store the results decode_results results; // Somewhere to store the results
@@ -115,6 +124,13 @@ void dumpACInfo(decode_results *results) {
description = ac.toString(); description = ac.toString();
} }
#endif // DECODE_DAIKIN #endif // DECODE_DAIKIN
#if DECODE_DAIKIN2
if (results->decode_type == DAIKIN2) {
IRDaikin2 ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_DAIKIN2
#if DECODE_FUJITSU_AC #if DECODE_FUJITSU_AC
if (results->decode_type == FUJITSU_AC) { if (results->decode_type == FUJITSU_AC) {
IRFujitsuAC ac(0); IRFujitsuAC ac(0);
@@ -129,6 +145,13 @@ void dumpACInfo(decode_results *results) {
description = ac.toString(); description = ac.toString();
} }
#endif // DECODE_KELVINATOR #endif // DECODE_KELVINATOR
#if DECODE_MITSUBISHI_AC
if (results->decode_type == MITSUBISHI_AC) {
IRMitsubishiAC ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_MITSUBISHI_AC
#if DECODE_TOSHIBA_AC #if DECODE_TOSHIBA_AC
if (results->decode_type == TOSHIBA_AC) { if (results->decode_type == TOSHIBA_AC) {
IRToshibaAC ac(0); IRToshibaAC ac(0);
@@ -157,22 +180,72 @@ void dumpACInfo(decode_results *results) {
description = ac.toString(); description = ac.toString();
} }
#endif // DECODE_HAIER_AC #endif // DECODE_HAIER_AC
#if DECODE_HAIER_AC_YRW02
if (results->decode_type == HAIER_AC_YRW02) {
IRHaierACYRW02 ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_HAIER_AC_YRW02
#if DECODE_SAMSUNG_AC
if (results->decode_type == SAMSUNG_AC) {
IRSamsungAc ac(0);
ac.setRaw(results->state, results->bits / 8);
description = ac.toString();
}
#endif // DECODE_SAMSUNG_AC
#if DECODE_COOLIX
if (results->decode_type == COOLIX) {
IRCoolixAC ac(0);
ac.setRaw(results->value); // Coolix uses value instead of state.
description = ac.toString();
}
#endif // DECODE_COOLIX
#if DECODE_PANASONIC_AC
if (results->decode_type == PANASONIC_AC &&
results->bits > kPanasonicAcShortBits) {
IRPanasonicAc ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_PANASONIC_AC
#if DECODE_HITACHI_AC
if (results->decode_type == HITACHI_AC) {
IRHitachiAc ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_HITACHI_AC
#if DECODE_WHIRLPOOL_AC
if (results->decode_type == WHIRLPOOL_AC) {
IRWhirlpoolAc ac(0);
ac.setRaw(results->state);
description = ac.toString();
}
#endif // DECODE_WHIRLPOOL_AC
#if DECODE_VESTEL_AC
if (results->decode_type == VESTEL_AC) {
IRVestelAC ac(0);
ac.setRaw(results->value); // Like Coolix, use value instead of state.
description = ac.toString();
}
#endif // DECODE_VESTEL_AC
// If we got a human-readable description of the message, display it. // If we got a human-readable description of the message, display it.
if (description != "") Serial.println("Mesg Desc.: " + description); if (description != "") Serial.println("Mesg Desc.: " + description);
} }
// The section of code run only once at start-up. // The section of code run only once at start-up.
void setup() { void setup() {
Serial.begin(BAUD_RATE, SERIAL_8N1, SERIAL_TX_ONLY); Serial.begin(kBaudRate, SERIAL_8N1, SERIAL_TX_ONLY);
while (!Serial) // Wait for the serial connection to be establised. while (!Serial) // Wait for the serial connection to be establised.
delay(50); delay(50);
Serial.println(); Serial.println();
Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin "); Serial.print("IRrecvDumpV2 is now running and waiting for IR input on Pin ");
Serial.println(RECV_PIN); Serial.println(kRecvPin);
#if DECODE_HASH #if DECODE_HASH
// Ignore messages with less than minimum on or off pulses. // Ignore messages with less than minimum on or off pulses.
irrecv.setUnknownThreshold(MIN_UNKNOWN_SIZE); irrecv.setUnknownThreshold(kMinUnknownSize);
#endif // DECODE_HASH #endif // DECODE_HASH
irrecv.enableIRIn(); // Start the receiver irrecv.enableIRIn(); // Start the receiver
} }
@@ -186,10 +259,11 @@ void loop() {
uint32_t now = millis(); uint32_t now = millis();
Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000); Serial.printf("Timestamp : %06u.%03u\n", now / 1000, now % 1000);
if (results.overflow) if (results.overflow)
Serial.printf("WARNING: IR code is too big for buffer (>= %d). " Serial.printf(
"WARNING: IR code is too big for buffer (>= %d). "
"This result shouldn't be trusted until this is resolved. " "This result shouldn't be trusted until this is resolved. "
"Edit & increase CAPTURE_BUFFER_SIZE.\n", "Edit & increase kCaptureBufferSize.\n",
CAPTURE_BUFFER_SIZE); kCaptureBufferSize);
// Display the basic output of what we found. // Display the basic output of what we found.
Serial.print(resultToHumanReadableBasic(&results)); Serial.print(resultToHumanReadableBasic(&results));
dumpACInfo(&results); // Display any extra A/C info if we have it. dumpACInfo(&results); // Display any extra A/C info if we have it.
@@ -1,11 +1,11 @@
/* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend. /* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend.
* *
* Version 1.0 April, 2017 * Version 1.1 January, 2019
* Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009, * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
* Copyright 2009 Ken Shirriff, http://arcfn.com * Copyright 2009 Ken Shirriff, http://arcfn.com
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -34,9 +34,9 @@
#include <IRremoteESP8266.h> #include <IRremoteESP8266.h>
#include <IRsend.h> #include <IRsend.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
// Example of data captured by IRrecvDumpV2.ino // Example of data captured by IRrecvDumpV2.ino
uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550, uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550,
@@ -46,6 +46,10 @@ uint16_t rawData[67] = {9000, 4500, 650, 550, 650, 1650, 600, 550, 650, 550,
650, 550, 650, 550, 600, 550, 650, 550, 650, 550, 650, 550, 650, 550, 600, 550, 650, 550, 650, 550,
650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650, 650, 1650, 600, 550, 650, 1650, 650, 1650, 650, 1650,
650, 1650, 650, 1650, 650, 1650, 600}; 650, 1650, 650, 1650, 650, 1650, 600};
// Example Samsung A/C state captured from IRrecvDumpV2.ino
uint8_t samsungState[kSamsungAcStateLength] = {
0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
0x01, 0xE2, 0xFE, 0x71, 0x40, 0x11, 0xF0};
void setup() { void setup() {
irsend.begin(); irsend.begin();
@@ -53,19 +57,16 @@ void setup() {
} }
void loop() { void loop() {
#if SEND_NEC
Serial.println("NEC"); Serial.println("NEC");
irsend.sendNEC(0x00FFE01FUL, 32); irsend.sendNEC(0x00FFE01FUL);
#endif // SEND_NEC
delay(2000); delay(2000);
#if SEND_SONY
Serial.println("Sony"); Serial.println("Sony");
irsend.sendSony(0xa90, 12, 2); irsend.sendSony(0xa90, 12, 2); // 12 bits & 2 repeats
#endif // SEND_SONY
delay(2000); delay(2000);
#if SEND_RAW
Serial.println("a rawData capture from IRrecvDumpV2"); Serial.println("a rawData capture from IRrecvDumpV2");
irsend.sendRaw(rawData, 67, 38); // Send a raw data capture at 38kHz. irsend.sendRaw(rawData, 67, 38); // Send a raw data capture at 38kHz.
#endif // SEND_RAW delay(2000);
Serial.println("a Samsung A/C state from IRrecvDumpV2");
irsend.sendSamsungAC(samsungState);
delay(2000); delay(2000);
} }
@@ -6,7 +6,7 @@
* Version 1.0 June, 2017 * Version 1.0 June, 2017
* *
* An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2), unless you * An IR LED circuit *MUST* be connected to ESP8266 pin 4 (D2), unless you
* change the IR_LED value below. * change the kIrLed value below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -35,9 +35,9 @@
#include <IRremoteESP8266.h> #include <IRremoteESP8266.h>
#include <IRsend.h> #include <IRsend.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message. IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
// Panasonic Plasma TV Descrete code (Power On). // Panasonic Plasma TV Descrete code (Power On).
// Acquired from: // Acquired from:
@@ -5,7 +5,7 @@
* JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post)
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -34,14 +34,13 @@
#include <IRremoteESP8266.h> #include <IRremoteESP8266.h>
#include <IRsend.h> #include <IRsend.h>
#define PanasonicAddress 0x4004 // Panasonic address (Pre data) const uint16_t kPanasonicAddress = 0x4004; // Panasonic address (Pre data)
#define PanasonicPower 0x100BCBD // Panasonic Power button const uint32_t kPanasonicPower = 0x100BCBD; // Panasonic Power button
const uint16_t kJVCPower = 0xC5E8;
#define JVCPower 0xC5E8 const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
IRsend irsend(IR_LED); // Set the GPIO to be used to sending the message.
void setup() { void setup() {
irsend.begin(); irsend.begin();
@@ -50,18 +49,15 @@ void setup() {
void loop() { void loop() {
// This should turn your TV on and off // This should turn your TV on and off
#if SEND_PANASONIC #if SEND_PANASONIC
irsend.sendPanasonic(PanasonicAddress, PanasonicPower); irsend.sendPanasonic(kPanasonicAddress, kPanasonicPower);
#else // SEND_PANASONIC #else // SEND_PANASONIC
Serial.println("Can't send because SEND_PANASONIC has been disabled."); Serial.println("Can't send because SEND_PANASONIC has been disabled.");
#endif // SEND_PANASONIC #endif // SEND_PANASONIC
#if SEND_JVC #if SEND_JVC
irsend.sendJVC(JVCPower, 16, 0); // hex value, 16 bits, no repeat irsend.sendJVC(kJVCPower, 16, 1); // hex value, 16 bits, single repeat
// see http://www.sbprojects.com/knowledge/ir/jvc.php for information
delayMicroseconds(50);
irsend.sendJVC(JVCPower, 16, 1); // hex value, 16 bits, repeat
delayMicroseconds(50);
#else // SEND_JVC #else // SEND_JVC
Serial.println("Can't send because SEND_JVC has been disabled."); Serial.println("Can't send because SEND_JVC has been disabled.");
#endif // SEND_JVC #endif // SEND_JVC
delay(10000); // Wait 10 seconds before we repeat everything.
} }
@@ -64,9 +64,9 @@ void Ac_Activate(unsigned int temperature, unsigned int air_flow,
else else
ac_msbits4 = 0; // cooling ac_msbits4 = 0; // cooling
unsigned int ac_msbits5 = (temperature < 15) ? 0 : temperature - 15; unsigned int ac_msbits5 = (temperature < 15) ? 0 : temperature - 15;
unsigned int ac_msbits6; unsigned int ac_msbits6 = 0;
if (0 <= air_flow && air_flow <= 2) { if (air_flow <= 2) {
if (kAc_Type == 0) if (kAc_Type == 0)
ac_msbits6 = kAc_Flow_Tower[air_flow]; ac_msbits6 = kAc_Flow_Tower[air_flow];
else else
@@ -132,7 +132,7 @@ void setup() {
} }
void loop() { void loop() {
char b; char b = ' ';
Serial.println("# a : mode or temp b : air_flow, temp, swing, clean," Serial.println("# a : mode or temp b : air_flow, temp, swing, clean,"
" cooling/heating"); " cooling/heating");
Serial.println("# 0 : off 0"); Serial.println("# 0 : off 0");
@@ -162,7 +162,7 @@ void loop() {
default: default:
Serial.println("b="); // Prompt User for input Serial.println("b="); // Prompt User for input
while (Serial.available() == 0) {} while (Serial.available() == 0) {}
char b = Serial.read(); b = Serial.read();
} }
/* /*
@@ -198,7 +198,7 @@ void loop() {
Ac_Change_Air_Swing(1); Ac_Change_Air_Swing(1);
break; break;
case '3': // 1 : clean on, power on case '3': // 1 : clean on, power on
if (b == '0' | b == '1') if (b == '0' || b == '1')
Ac_Air_Clean(b); Ac_Air_Clean(b);
break; break;
case '4': case '4':
@@ -0,0 +1,17 @@
[platformio]
lib_extra_dirs = ../../
src_dir=.
[common]
build_flags =
lib_deps_builtin =
lib_deps_external =
[env:nodemcuv2]
platform = espressif8266
framework = arduino
board = nodemcuv2
build_flags = ${common.build_flags}
lib_deps =
${common.lib_deps_builtin}
${common.lib_deps_external}
@@ -1,6 +1,6 @@
/* Copyright 2017 crankyoldgit /* Copyright 2017, 2018 crankyoldgit
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -30,11 +30,11 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Argo.h> #include <ir_Argo.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRArgoAC argoir(IR_LED); // Set the GPIO to be used to sending the message. IRArgoAC ac(kIrLed); // Set the GPIO to be used to sending the message.
void setup() { void setup() {
argoir.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
} }
@@ -42,14 +42,14 @@ void loop() {
Serial.println("Sending..."); Serial.println("Sending...");
// Set up what we want to send. See ir_Argo.cpp for all the options. // Set up what we want to send. See ir_Argo.cpp for all the options.
argoir.setPower(true); ac.setPower(true);
argoir.setFan(ARGO_FAN_1); ac.setFan(kArgoFan1);
argoir.setCoolMode(ARGO_COOL_AUTO); ac.setCoolMode(kArgoCoolAuto);
argoir.setTemp(25); ac.setTemp(25);
#if SEND_ARGO #if SEND_ARGO
// Now send the IR signal. // Now send the IR signal.
argoir.send(); ac.send();
#else // SEND_ARGO #else // SEND_ARGO
Serial.println("Can't send because SEND_ARGO has been disabled."); Serial.println("Can't send because SEND_ARGO has been disabled.");
#endif // SEND_ARGO #endif // SEND_ARGO
@@ -1,7 +1,7 @@
/* Copyright 2017 sillyfrog /* Copyright 2017 sillyfrog
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -31,11 +31,11 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Daikin.h> #include <ir_Daikin.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRDaikinESP daikinir(IR_LED); // Set the GPIO to be used to sending the message IRDaikinESP ac(kIrLed); // Set the GPIO to be used to sending the message
void setup() { void setup() {
daikinir.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
} }
@@ -44,25 +44,25 @@ void loop() {
Serial.println("Sending..."); Serial.println("Sending...");
// Set up what we want to send. See ir_Daikin.cpp for all the options. // Set up what we want to send. See ir_Daikin.cpp for all the options.
daikinir.on(); ac.on();
daikinir.setFan(1); ac.setFan(1);
daikinir.setMode(DAIKIN_COOL); ac.setMode(kDaikinCool);
daikinir.setTemp(25); ac.setTemp(25);
daikinir.setSwingVertical(false); ac.setSwingVertical(false);
daikinir.setSwingHorizontal(false); ac.setSwingHorizontal(false);
// Set the current time to 1:33PM (13:33) // Set the current time to 1:33PM (13:33)
// Time works in minutes past midnight // Time works in minutes past midnight
daikinir.setCurrentTime((13*60) + 33); ac.setCurrentTime(13 * 60 + 33);
// Turn off about 1 hour later at 2:30PM (15:30) // Turn off about 1 hour later at 2:30PM (14:30)
daikinir.enableOffTimer((14*60) + 30); ac.enableOffTimer(14 * 60 + 30);
// Display what we are going to send. // Display what we are going to send.
Serial.println(daikinir.toString()); Serial.println(ac.toString());
// Now send the IR signal. // Now send the IR signal.
#if SEND_DAIKIN #if SEND_DAIKIN
daikinir.send(); ac.send();
#endif // SEND_DAIKIN #endif // SEND_DAIKIN
delay(15000); delay(15000);
@@ -1,26 +1,28 @@
// Copyright 2017 Jonny Graham // Copyright 2017 Jonny Graham, 2018 David Conran
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h> #include <IRsend.h>
#include <ir_Fujitsu.h> #include <ir_Fujitsu.h>
IRFujitsuAC fujitsu(5); // IR led controlled by Pin D1. const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRFujitsuAC ac(kIrLed);
void printState() { void printState() {
// Display the settings. // Display the settings.
Serial.println("Fujitsu A/C remote is in the following state:"); Serial.println("Fujitsu A/C remote is in the following state:");
Serial.printf(" Command:%d, Mode: %d, Temp: %dC, Fan Speed: %d," \ Serial.printf(" %s\n", ac.toString().c_str());
" Swing Mode: %d\n",
fujitsu.getCmd(), fujitsu.getMode(), fujitsu.getTemp(),
fujitsu.getFanSpeed(), fujitsu.getSwing());
// Display the encoded IR sequence. // Display the encoded IR sequence.
unsigned char* ir_code = fujitsu.getRaw(); unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x"); Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < fujitsu.getStateLength(); i++) for (uint8_t i = 0; i < ac.getStateLength(); i++)
Serial.printf("%02X", ir_code[i]); Serial.printf("%02X", ir_code[i]);
Serial.println(); Serial.println();
} }
void setup() { void setup() {
fujitsu.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
delay(200); delay(200);
@@ -28,18 +30,18 @@ void setup() {
Serial.println("Default state of the remote."); Serial.println("Default state of the remote.");
printState(); printState();
Serial.println("Setting desired state for A/C."); Serial.println("Setting desired state for A/C.");
fujitsu.setCmd(FUJITSU_AC_CMD_TURN_ON); ac.setCmd(kFujitsuAcCmdTurnOn);
fujitsu.setSwing(FUJITSU_AC_SWING_BOTH); ac.setSwing(kFujitsuAcSwingBoth);
fujitsu.setMode(FUJITSU_AC_MODE_COOL); ac.setMode(kFujitsuAcModeCool);
fujitsu.setFanSpeed(FUJITSU_AC_FAN_HIGH); ac.setFanSpeed(kFujitsuAcFanHigh);
fujitsu.setTemp(24); ac.setTemp(24); // 24C
} }
void loop() { void loop() {
// Now send the IR signal. // Now send the IR signal.
Serial.println("Sending IR command to A/C ..."); Serial.println("Sending IR command to A/C ...");
#if SEND_FUJITSU_AC #if SEND_FUJITSU_AC
fujitsu.send(); ac.send();
#else // SEND_FUJITSU_AC #else // SEND_FUJITSU_AC
Serial.println("Can't send because SEND_FUJITSU_AC has been disabled."); Serial.println("Can't send because SEND_FUJITSU_AC has been disabled.");
#endif // SEND_FUJITSU_AC #endif // SEND_FUJITSU_AC
@@ -1,7 +1,7 @@
/* Copyright 2016 David Conran /* Copyright 2016, 2018 David Conran
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -30,30 +30,23 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Kelvinator.h> #include <ir_Kelvinator.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRKelvinatorAC kelvir(IR_LED); // Set the GPIO to be used for sending messages. IRKelvinatorAC ac(kIrLed); // Set the GPIO to be used for sending messages.
void printState() { void printState() {
// Display the settings. // Display the settings.
Serial.println("Kelvinator A/C remote is in the following state:"); Serial.println("Kelvinator A/C remote is in the following state:");
Serial.printf(" Basic\n Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d\n", Serial.printf(" %s\n", ac.toString().c_str());
kelvir.getPower(), kelvir.getMode(), kelvir.getTemp(),
kelvir.getFan());
Serial.printf(" Options\n X-Fan: %d, Light: %d, Ion Filter: %d\n",
kelvir.getXFan(), kelvir.getLight(), kelvir.getIonFilter());
Serial.printf(" Swing (V): %d, Swing (H): %d, Turbo: %d, Quiet: %d\n",
kelvir.getSwingVertical(), kelvir.getSwingHorizontal(),
kelvir.getTurbo(), kelvir.getQuiet());
// Display the encoded IR sequence. // Display the encoded IR sequence.
unsigned char* ir_code = kelvir.getRaw(); unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x"); Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++) for (uint8_t i = 0; i < kKelvinatorStateLength; i++)
Serial.printf("%02X", ir_code[i]); Serial.printf("%02X", ir_code[i]);
Serial.println(); Serial.println();
} }
void setup() { void setup() {
kelvir.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
delay(200); delay(200);
@@ -62,22 +55,22 @@ void setup() {
Serial.println("Default state of the remote."); Serial.println("Default state of the remote.");
printState(); printState();
Serial.println("Setting desired state for A/C."); Serial.println("Setting desired state for A/C.");
kelvir.on(); ac.on();
kelvir.setFan(1); ac.setFan(1);
kelvir.setMode(KELVINATOR_COOL); ac.setMode(kKelvinatorCool);
kelvir.setTemp(26); ac.setTemp(26);
kelvir.setSwingVertical(false); ac.setSwingVertical(false);
kelvir.setSwingHorizontal(true); ac.setSwingHorizontal(true);
kelvir.setXFan(true); ac.setXFan(true);
kelvir.setIonFilter(false); ac.setIonFilter(false);
kelvir.setLight(true); ac.setLight(true);
} }
void loop() { void loop() {
// Now send the IR signal. // Now send the IR signal.
#if SEND_KELVINATOR #if SEND_KELVINATOR
Serial.println("Sending IR command to A/C ..."); Serial.println("Sending IR command to A/C ...");
kelvir.send(); ac.send();
#endif // SEND_KELVINATOR #endif // SEND_KELVINATOR
printState(); printState();
delay(5000); delay(5000);
@@ -1,7 +1,7 @@
/* Copyright 2017 David Conran /* Copyright 2017, 2018 David Conran
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -30,26 +30,23 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Mitsubishi.h> #include <ir_Mitsubishi.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRMitsubishiAC mitsubir(IR_LED); // Set the GPIO used for sending messages. IRMitsubishiAC ac(kIrLed); // Set the GPIO used for sending messages.
void printState() { void printState() {
// Display the settings. // Display the settings.
Serial.println("Mitsubishi A/C remote is in the following state:"); Serial.println("Mitsubishi A/C remote is in the following state:");
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d," \ Serial.printf(" %s\n", ac.toString().c_str());
" Vane Mode: %d\n",
mitsubir.getPower(), mitsubir.getMode(), mitsubir.getTemp(),
mitsubir.getFan(), mitsubir.getVane());
// Display the encoded IR sequence. // Display the encoded IR sequence.
unsigned char* ir_code = mitsubir.getRaw(); unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x"); Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++) for (uint8_t i = 0; i < kMitsubishiACStateLength; i++)
Serial.printf("%02X", ir_code[i]); Serial.printf("%02X", ir_code[i]);
Serial.println(); Serial.println();
} }
void setup() { void setup() {
mitsubir.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
delay(200); delay(200);
@@ -57,18 +54,18 @@ void setup() {
Serial.println("Default state of the remote."); Serial.println("Default state of the remote.");
printState(); printState();
Serial.println("Setting desired state for A/C."); Serial.println("Setting desired state for A/C.");
mitsubir.on(); ac.on();
mitsubir.setFan(1); ac.setFan(1);
mitsubir.setMode(MITSUBISHI_AC_COOL); ac.setMode(kMitsubishiAcCool);
mitsubir.setTemp(26); ac.setTemp(26);
mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO); ac.setVane(kMitsubishiAcVaneAuto);
} }
void loop() { void loop() {
// Now send the IR signal. // Now send the IR signal.
#if SEND_MITSUBISHI_AC #if SEND_MITSUBISHI_AC
Serial.println("Sending IR command to A/C ..."); Serial.println("Sending IR command to A/C ...");
mitsubir.send(); ac.send();
#endif // SEND_MITSUBISHI_AC #endif // SEND_MITSUBISHI_AC
printState(); printState();
delay(5000); delay(5000);
@@ -1,7 +1,7 @@
/* Copyright 2017 David Conran /* Copyright 2017, 2018 David Conran
* *
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -30,25 +30,23 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Toshiba.h> #include <ir_Toshiba.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRToshibaAC toshibair(IR_LED); // Set the GPIO to be used for sending messages. IRToshibaAC ac(kIrLed); // Set the GPIO to be used for sending messages.
void printState() { void printState() {
// Display the settings. // Display the settings.
Serial.println("Toshiba A/C remote is in the following state:"); Serial.println("Toshiba A/C remote is in the following state:");
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d\n", Serial.printf(" %s\n", ac.toString().c_str());
toshibair.getPower(), toshibair.getMode(), toshibair.getTemp(),
toshibair.getFan());
// Display the encoded IR sequence. // Display the encoded IR sequence.
unsigned char* ir_code = toshibair.getRaw(); unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x"); Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) for (uint8_t i = 0; i < kToshibaACStateLength; i++)
Serial.printf("%02X", ir_code[i]); Serial.printf("%02X", ir_code[i]);
Serial.println(); Serial.println();
} }
void setup() { void setup() {
toshibair.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
delay(200); delay(200);
@@ -56,17 +54,17 @@ void setup() {
Serial.println("Default state of the remote."); Serial.println("Default state of the remote.");
printState(); printState();
Serial.println("Setting desired state for A/C."); Serial.println("Setting desired state for A/C.");
toshibair.on(); ac.on();
toshibair.setFan(1); ac.setFan(1);
toshibair.setMode(TOSHIBA_AC_COOL); ac.setMode(kToshibaAcCool);
toshibair.setTemp(26); ac.setTemp(26);
} }
void loop() { void loop() {
// Now send the IR signal. // Now send the IR signal.
#if SEND_TOSHIBA_AC #if SEND_TOSHIBA_AC
Serial.println("Sending IR command to A/C ..."); Serial.println("Sending IR command to A/C ...");
toshibair.send(); ac.send();
#endif // SEND_TOSHIBA_AC #endif // SEND_TOSHIBA_AC
printState(); printState();
delay(5000); delay(5000);
@@ -1,6 +1,6 @@
/* Copyright 2017 stufisher /* Copyright 2017 stufisher
* An IR LED circuit *MUST* be connected to the ESP8266 on a pin * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
* as specified by IR_LED below. * as specified by kIrLed below.
* *
* TL;DR: The IR LED needs to be driven by a transistor for a good result. * TL;DR: The IR LED needs to be driven by a transistor for a good result.
* *
@@ -30,11 +30,11 @@
#include <IRsend.h> #include <IRsend.h>
#include <ir_Trotec.h> #include <ir_Trotec.h>
#define IR_LED 4 // ESP8266 GPIO pin to use. Recommended: 4 (D2). const uint16_t kIrLed = 4; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRTrotecESP trotecir(IR_LED); // Set the GPIO to be used for sending messages. IRTrotecESP ac(kIrLed); // Set the GPIO to be used for sending messages.
void setup() { void setup() {
trotecir.begin(); ac.begin();
Serial.begin(115200); Serial.begin(115200);
} }
@@ -42,14 +42,14 @@ void loop() {
Serial.println("Sending..."); Serial.println("Sending...");
// Set up what we want to send. See ir_Trotec.cpp for all the options. // Set up what we want to send. See ir_Trotec.cpp for all the options.
trotecir.setPower(true); ac.setPower(true);
trotecir.setSpeed(TROTEC_FAN_LOW); ac.setSpeed(kTrotecFanLow);
trotecir.setMode(TROTEC_COOL); ac.setMode(kTrotecCool);
trotecir.setTemp(25); ac.setTemp(25);
// Now send the IR signal. // Now send the IR signal.
#if SEND_TROTEC #if SEND_TROTEC
trotecir.send(); ac.send();
#else // SEND_TROTEC #else // SEND_TROTEC
Serial.println("Can't send because SEND_TROTEC has been disabled."); Serial.println("Can't send because SEND_TROTEC has been disabled.");
#endif // SEND_TROTEC #endif // SEND_TROTEC
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "IRremoteESP8266", "name": "IRremoteESP8266",
"version": "2.4.2", "version": "2.5.5",
"keywords": "infrared, ir, remote, esp8266", "keywords": "infrared, ir, remote, esp8266",
"description": "Send and receive infrared signals with multiple protocols (ESP8266)", "description": "Send and receive infrared signals with multiple protocols (ESP8266)",
"repository": "repository":
+1 -1
View File
@@ -1,5 +1,5 @@
name=IRremoteESP8266 name=IRremoteESP8266
version=2.4.2 version=2.5.5
author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran author=Sebastien Warin, Mark Szabo, Ken Shirriff, David Conran
maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto maintainer=Mark Szabo, David Conran, Sebastien Warin, Roi Dayan, Massimiliano Pinto
sentence=Send and receive infrared signals with multiple protocols (ESP8266) sentence=Send and receive infrared signals with multiple protocols (ESP8266)
+145 -130
View File
@@ -14,6 +14,7 @@ extern "C" {
#endif #endif
#include <algorithm> #include <algorithm>
#include "IRremoteESP8266.h" #include "IRremoteESP8266.h"
#include "IRutils.h"
#ifdef UNIT_TEST #ifdef UNIT_TEST
#undef ICACHE_RAM_ATTR #undef ICACHE_RAM_ATTR
@@ -34,8 +35,7 @@ irparams_t *irparams_save; // A copy of the interrupt state while decoding.
#ifndef UNIT_TEST #ifndef UNIT_TEST
static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) { static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) {
os_intr_lock(); os_intr_lock();
if (irparams.rawlen) if (irparams.rawlen) irparams.rcvstate = kStopState;
irparams.rcvstate = STATE_STOP;
os_intr_unlock(); os_intr_unlock();
} }
@@ -57,20 +57,19 @@ static void ICACHE_RAM_ATTR gpio_intr() {
if (rawlen >= irparams.bufsize) { if (rawlen >= irparams.bufsize) {
irparams.overflow = true; irparams.overflow = true;
irparams.rcvstate = STATE_STOP; irparams.rcvstate = kStopState;
} }
if (irparams.rcvstate == STATE_STOP) if (irparams.rcvstate == kStopState) return;
return;
if (irparams.rcvstate == STATE_IDLE) { if (irparams.rcvstate == kIdleState) {
irparams.rcvstate = STATE_MARK; irparams.rcvstate = kMarkState;
irparams.rawbuf[rawlen] = 1; irparams.rawbuf[rawlen] = 1;
} else { } else {
if (now < start) if (now < start)
irparams.rawbuf[rawlen] = (UINT32_MAX - start + now) / RAWTICK; irparams.rawbuf[rawlen] = (UINT32_MAX - start + now) / kRawTick;
else else
irparams.rawbuf[rawlen] = (now - start) / RAWTICK; irparams.rawbuf[rawlen] = (now - start) / kRawTick;
} }
irparams.rawlen++; irparams.rawlen++;
@@ -85,9 +84,9 @@ static void ICACHE_RAM_ATTR gpio_intr() {
// Class constructor // Class constructor
// Args: // Args:
// recvpin: GPIO pin the IR receiver module's data pin is connected to. // recvpin: GPIO pin the IR receiver module's data pin is connected to.
// bufsize: Nr. of entries to have in the capture buffer. (Default: RAWBUF) // bufsize: Nr. of entries to have in the capture buffer. (Default: kRawBuf)
// timeout: Nr. of milli-Seconds of no signal before we stop capturing data. // timeout: Nr. of milli-Seconds of no signal before we stop capturing data.
// (Default: TIMEOUT_MS) // (Default: kTimeoutMs)
// save_buffer: Use a second (save) buffer to decode from. (Def: false) // save_buffer: Use a second (save) buffer to decode from. (Def: false)
// Returns: // Returns:
// An IRrecv class object. // An IRrecv class object.
@@ -97,10 +96,11 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
irparams.bufsize = bufsize; irparams.bufsize = bufsize;
// Ensure we are going to be able to store all possible values in the // Ensure we are going to be able to store all possible values in the
// capture buffer. // capture buffer.
irparams.timeout = std::min(timeout, (uint8_t) MAX_TIMEOUT_MS); irparams.timeout = std::min(timeout, (uint8_t)kMaxTimeoutMs);
irparams.rawbuf = new uint16_t[bufsize]; irparams.rawbuf = new uint16_t[bufsize];
if (irparams.rawbuf == NULL) { if (irparams.rawbuf == NULL) {
DPRINTLN("Could not allocate memory for the primary IR buffer.\n" DPRINTLN(
"Could not allocate memory for the primary IR buffer.\n"
"Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!");
#ifndef UNIT_TEST #ifndef UNIT_TEST
ESP.restart(); // Mem alloc failure. Reboot. ESP.restart(); // Mem alloc failure. Reboot.
@@ -112,7 +112,8 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
irparams_save->rawbuf = new uint16_t[bufsize]; irparams_save->rawbuf = new uint16_t[bufsize];
// Check we allocated the memory successfully. // Check we allocated the memory successfully.
if (irparams_save->rawbuf == NULL) { if (irparams_save->rawbuf == NULL) {
DPRINTLN("Could not allocate memory for the second IR buffer.\n" DPRINTLN(
"Could not allocate memory for the second IR buffer.\n"
"Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!"); "Try a smaller size for CAPTURE_BUFFER_SIZE.\nRebooting!");
#ifndef UNIT_TEST #ifndef UNIT_TEST
ESP.restart(); // Mem alloc failure. Reboot. ESP.restart(); // Mem alloc failure. Reboot.
@@ -122,7 +123,7 @@ IRrecv::IRrecv(uint16_t recvpin, uint16_t bufsize, uint8_t timeout,
irparams_save = NULL; irparams_save = NULL;
} }
#if DECODE_HASH #if DECODE_HASH
unknown_threshold = UNKNOWN_THRESHOLD; unknown_threshold = kUnknownThreshold;
#endif // DECODE_HASH #endif // DECODE_HASH
} }
@@ -159,7 +160,7 @@ void IRrecv::disableIRIn() {
} }
void IRrecv::resume() { void IRrecv::resume() {
irparams.rcvstate = STATE_IDLE; irparams.rcvstate = kIdleState;
irparams.rawlen = 0; irparams.rawlen = 0;
irparams.overflow = false; irparams.overflow = false;
} }
@@ -167,7 +168,7 @@ void IRrecv::resume() {
// Make a copy of the interrupt state & buffer data. // Make a copy of the interrupt state & buffer data.
// Needed because irparams is marked as volatile, thus memcpy() isn't allowed. // Needed because irparams is marked as volatile, thus memcpy() isn't allowed.
// Only call this when you know the interrupt handlers won't modify anything. // Only call this when you know the interrupt handlers won't modify anything.
// i.e. In STATE_STOP. // i.e. In kStopState.
// //
// Args: // Args:
// src: Pointer to an irparams_t structure to copy from. // src: Pointer to an irparams_t structure to copy from.
@@ -184,22 +185,18 @@ void IRrecv::copyIrParams(volatile irparams_t *src, irparams_t *dst) {
dst_rawbuf_ptr = dst->rawbuf; dst_rawbuf_ptr = dst->rawbuf;
// Copy contents of src[] to dst[] // Copy contents of src[] to dst[]
for (uint16_t i = 0; i < sizeof(irparams_t); i++) for (uint16_t i = 0; i < sizeof(irparams_t); i++) cdst[i] = csrc[i];
cdst[i] = csrc[i];
// Restore the buffer pointer // Restore the buffer pointer
dst->rawbuf = dst_rawbuf_ptr; dst->rawbuf = dst_rawbuf_ptr;
// Copy the rawbuf // Copy the rawbuf
for (uint16_t i = 0; i < dst->bufsize; i++) for (uint16_t i = 0; i < dst->bufsize; i++) dst->rawbuf[i] = src->rawbuf[i];
dst->rawbuf[i] = src->rawbuf[i];
} }
// Obtain the maximum number of entries possible in the capture buffer. // Obtain the maximum number of entries possible in the capture buffer.
// i.e. It's size. // i.e. It's size.
uint16_t IRrecv::getBufSize() { uint16_t IRrecv::getBufSize() { return irparams.bufsize; }
return irparams.bufsize;
}
#if DECODE_HASH #if DECODE_HASH
// Set the minimum length we will consider for reporting UNKNOWN message types. // Set the minimum length we will consider for reporting UNKNOWN message types.
@@ -223,8 +220,7 @@ void IRrecv::setUnknownThreshold(uint16_t length) {
bool IRrecv::decode(decode_results *results, irparams_t *save) { bool IRrecv::decode(decode_results *results, irparams_t *save) {
// Proceed only if an IR message been received. // Proceed only if an IR message been received.
#ifndef UNIT_TEST #ifndef UNIT_TEST
if (irparams.rcvstate != STATE_STOP) if (irparams.rcvstate != kStopState) return false;
return false;
#endif #endif
// Clear the entry we are currently pointing to when we got the timeout. // Clear the entry we are currently pointing to when we got the timeout.
@@ -240,8 +236,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
bool resumed = false; // Flag indicating if we have resumed. bool resumed = false; // Flag indicating if we have resumed.
// If we were requested to use a save buffer previously, do so. // If we were requested to use a save buffer previously, do so.
if (save == NULL) if (save == NULL) save = irparams_save;
save = irparams_save;
if (save == NULL) { if (save == NULL) {
// We haven't been asked to copy it so use the existing memory. // We haven't been asked to copy it so use the existing memory.
@@ -273,8 +268,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
// Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC() // Try decodeAiwaRCT501() before decodeSanyoLC7461() & decodeNEC()
// because the protocols are similar. This protocol is more specific than // because the protocols are similar. This protocol is more specific than
// those ones, so should got before them. // those ones, so should got before them.
if (decodeAiwaRCT501(results)) if (decodeAiwaRCT501(results)) return true;
return true;
#endif #endif
#if DECODE_SANYO #if DECODE_SANYO
DPRINTLN("Attempting Sanyo LC7461 decode"); DPRINTLN("Attempting Sanyo LC7461 decode");
@@ -282,8 +276,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
// similar in timings & structure, but the Sanyo one is much longer than the // similar in timings & structure, but the Sanyo one is much longer than the
// NEC protocol (42 vs 32 bits) so this one should be tried first to try to // NEC protocol (42 vs 32 bits) so this one should be tried first to try to
// reduce false detection as a NEC packet. // reduce false detection as a NEC packet.
if (decodeSanyoLC7461(results)) if (decodeSanyoLC7461(results)) return true;
return true;
#endif #endif
#if DECODE_CARRIER_AC #if DECODE_CARRIER_AC
DPRINTLN("Attempting Carrier AC decode"); DPRINTLN("Attempting Carrier AC decode");
@@ -291,141 +284,131 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
// similar in timings & structure, but the Carrier one is much longer than the // similar in timings & structure, but the Carrier one is much longer than the
// NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried first to // NEC protocol (3x32 bits vs 1x32 bits) so this one should be tried first to
// try to reduce false detection as a NEC packet. // try to reduce false detection as a NEC packet.
if (decodeCarrierAC(results)) if (decodeCarrierAC(results)) return true;
return true; #endif
#if DECODE_PIONEER
DPRINTLN("Attempting Pioneer decode");
// Try decodePioneer() before decodeNEC() because the protocols are
// similar in timings & structure, but the Pioneer one is much longer than the
// NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried first to
// try to reduce false detection as a NEC packet.
if (decodePioneer(results)) return true;
#endif #endif
#if DECODE_NEC #if DECODE_NEC
DPRINTLN("Attempting NEC decode"); DPRINTLN("Attempting NEC decode");
if (decodeNEC(results)) if (decodeNEC(results)) return true;
return true;
#endif #endif
#if DECODE_SONY #if DECODE_SONY
DPRINTLN("Attempting Sony decode"); DPRINTLN("Attempting Sony decode");
if (decodeSony(results)) if (decodeSony(results)) return true;
return true;
#endif #endif
#if DECODE_MITSUBISHI #if DECODE_MITSUBISHI
DPRINTLN("Attempting Mitsubishi decode"); DPRINTLN("Attempting Mitsubishi decode");
if (decodeMitsubishi(results)) if (decodeMitsubishi(results)) return true;
return true; #endif
#if DECODE_MITSUBISHI_AC
DPRINTLN("Attempting Mitsubishi AC decode");
if (decodeMitsubishiAC(results)) return true;
#endif #endif
#if DECODE_MITSUBISHI2 #if DECODE_MITSUBISHI2
DPRINTLN("Attempting Mitsubishi2 decode"); DPRINTLN("Attempting Mitsubishi2 decode");
if (decodeMitsubishi2(results)) if (decodeMitsubishi2(results)) return true;
return true;
#endif #endif
#if DECODE_RC5 #if DECODE_RC5
DPRINTLN("Attempting RC5 decode"); DPRINTLN("Attempting RC5 decode");
if (decodeRC5(results)) if (decodeRC5(results)) return true;
return true;
#endif #endif
#if DECODE_RC6 #if DECODE_RC6
DPRINTLN("Attempting RC6 decode"); DPRINTLN("Attempting RC6 decode");
if (decodeRC6(results)) if (decodeRC6(results)) return true;
return true;
#endif #endif
#if DECODE_RCMM #if DECODE_RCMM
DPRINTLN("Attempting RC-MM decode"); DPRINTLN("Attempting RC-MM decode");
if (decodeRCMM(results)) if (decodeRCMM(results)) return true;
return true;
#endif #endif
#if DECODE_FUJITSU_AC #if DECODE_FUJITSU_AC
// Fujitsu A/C needs to precede Panasonic and Denon as it has a short // Fujitsu A/C needs to precede Panasonic and Denon as it has a short
// message which looks exactly the same as a Panasonic/Denon message. // message which looks exactly the same as a Panasonic/Denon message.
DPRINTLN("Attempting Fujitsu A/C decode"); DPRINTLN("Attempting Fujitsu A/C decode");
if (decodeFujitsuAC(results)) if (decodeFujitsuAC(results)) return true;
return true;
#endif #endif
#if DECODE_DENON #if DECODE_DENON
// Denon needs to precede Panasonic as it is a special case of Panasonic. // Denon needs to precede Panasonic as it is a special case of Panasonic.
DPRINTLN("Attempting Denon decode"); DPRINTLN("Attempting Denon decode");
if (decodeDenon(results, DENON_48_BITS) || if (decodeDenon(results, DENON_48_BITS) || decodeDenon(results, DENON_BITS) ||
decodeDenon(results, DENON_BITS) || decodeDenon(results, kDenonLegacyBits))
decodeDenon(results, DENON_LEGACY_BITS))
return true; return true;
#endif #endif
#if DECODE_PANASONIC #if DECODE_PANASONIC
DPRINTLN("Attempting Panasonic decode"); DPRINTLN("Attempting Panasonic decode");
if (decodePanasonic(results)) if (decodePanasonic(results)) return true;
return true;
#endif #endif
#if DECODE_LG #if DECODE_LG
DPRINTLN("Attempting LG (28-bit) decode"); DPRINTLN("Attempting LG (28-bit) decode");
if (decodeLG(results, LG_BITS, true)) if (decodeLG(results, kLgBits, true)) return true;
return true;
DPRINTLN("Attempting LG (32-bit) decode"); DPRINTLN("Attempting LG (32-bit) decode");
// LG32 should be tried before Samsung // LG32 should be tried before Samsung
if (decodeLG(results, LG32_BITS, true)) if (decodeLG(results, kLg32Bits, true)) return true;
return true;
#endif #endif
#if DECODE_GICABLE #if DECODE_GICABLE
// Note: Needs to happen before JVC decode, because it looks similar except // Note: Needs to happen before JVC decode, because it looks similar except
// with a required NEC-like repeat code. // with a required NEC-like repeat code.
DPRINTLN("Attempting GICable decode"); DPRINTLN("Attempting GICable decode");
if (decodeGICable(results)) if (decodeGICable(results)) return true;
return true;
#endif #endif
#if DECODE_JVC #if DECODE_JVC
DPRINTLN("Attempting JVC decode"); DPRINTLN("Attempting JVC decode");
if (decodeJVC(results)) if (decodeJVC(results)) return true;
return true;
#endif #endif
#if DECODE_SAMSUNG #if DECODE_SAMSUNG
DPRINTLN("Attempting SAMSUNG decode"); DPRINTLN("Attempting SAMSUNG decode");
if (decodeSAMSUNG(results)) if (decodeSAMSUNG(results)) return true;
return true;
#endif #endif
#if DECODE_WHYNTER #if DECODE_WHYNTER
DPRINTLN("Attempting Whynter decode"); DPRINTLN("Attempting Whynter decode");
if (decodeWhynter(results)) if (decodeWhynter(results)) return true;
return true;
#endif #endif
#if DECODE_DISH #if DECODE_DISH
DPRINTLN("Attempting DISH decode"); DPRINTLN("Attempting DISH decode");
if (decodeDISH(results)) if (decodeDISH(results)) return true;
return true;
#endif #endif
#if DECODE_SHARP #if DECODE_SHARP
DPRINTLN("Attempting Sharp decode"); DPRINTLN("Attempting Sharp decode");
if (decodeSharp(results)) if (decodeSharp(results)) return true;
return true;
#endif #endif
#if DECODE_COOLIX #if DECODE_COOLIX
DPRINTLN("Attempting Coolix decode"); DPRINTLN("Attempting Coolix decode");
if (decodeCOOLIX(results)) if (decodeCOOLIX(results)) return true;
return true;
#endif #endif
#if DECODE_NIKAI #if DECODE_NIKAI
DPRINTLN("Attempting Nikai decode"); DPRINTLN("Attempting Nikai decode");
if (decodeNikai(results)) if (decodeNikai(results)) return true;
return true;
#endif #endif
#if DECODE_KELVINATOR #if DECODE_KELVINATOR
// Kelvinator based-devices use a similar code to Gree ones, to avoid false // Kelvinator based-devices use a similar code to Gree ones, to avoid false
// matches this needs to happen before decodeGree(). // matches this needs to happen before decodeGree().
DPRINTLN("Attempting Kelvinator decode"); DPRINTLN("Attempting Kelvinator decode");
if (decodeKelvinator(results)) if (decodeKelvinator(results)) return true;
return true;
#endif #endif
#if DECODE_DAIKIN #if DECODE_DAIKIN
DPRINTLN("Attempting Daikin decode"); DPRINTLN("Attempting Daikin decode");
if (decodeDaikin(results)) if (decodeDaikin(results)) return true;
return true; #endif
#if DECODE_DAIKIN2
DPRINTLN("Attempting Daikin2 decode");
if (decodeDaikin2(results)) return true;
#endif #endif
#if DECODE_TOSHIBA_AC #if DECODE_TOSHIBA_AC
DPRINTLN("Attempting Toshiba AC decode"); DPRINTLN("Attempting Toshiba AC decode");
if (decodeToshibaAC(results)) if (decodeToshibaAC(results)) return true;
return true;
#endif #endif
#if DECODE_MIDEA #if DECODE_MIDEA
DPRINTLN("Attempting Midea decode"); DPRINTLN("Attempting Midea decode");
if (decodeMidea(results)) if (decodeMidea(results)) return true;
return true;
#endif #endif
#if DECODE_MAGIQUEST #if DECODE_MAGIQUEST
DPRINTLN("Attempting Magiquest decode"); DPRINTLN("Attempting Magiquest decode");
if (decodeMagiQuest(results)) if (decodeMagiQuest(results)) return true;
return true;
#endif #endif
/* NOTE: Disabled due to poor quality. /* NOTE: Disabled due to poor quality.
#if DECODE_SANYO #if DECODE_SANYO
@@ -444,43 +427,75 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
// other protocols that are NEC-like as well, as turning off strict may // other protocols that are NEC-like as well, as turning off strict may
// cause this to match other valid protocols. // cause this to match other valid protocols.
DPRINTLN("Attempting NEC (non-strict) decode"); DPRINTLN("Attempting NEC (non-strict) decode");
if (decodeNEC(results, NEC_BITS, false)) { if (decodeNEC(results, kNECBits, false)) {
results->decode_type = NEC_LIKE; results->decode_type = NEC_LIKE;
return true; return true;
} }
#endif #endif
#if DECODE_LASERTAG #if DECODE_LASERTAG
DPRINTLN("Attempting Lasertag decode"); DPRINTLN("Attempting Lasertag decode");
if (decodeLasertag(results)) if (decodeLasertag(results)) return true;
return true;
#endif #endif
#if DECODE_GREE #if DECODE_GREE
// Gree based-devices use a similar code to Kelvinator ones, to avoid false // Gree based-devices use a similar code to Kelvinator ones, to avoid false
// matches this needs to happen after decodeKelvinator(). // matches this needs to happen after decodeKelvinator().
DPRINTLN("Attempting Gree decode"); DPRINTLN("Attempting Gree decode");
if (decodeGree(results)) if (decodeGree(results)) return true;
return true;
#endif #endif
#if DECODE_HAIER_AC #if DECODE_HAIER_AC
DPRINTLN("Attempting Haier AC decode"); DPRINTLN("Attempting Haier AC decode");
if (decodeHaierAC(results)) if (decodeHaierAC(results)) return true;
return true; #endif
#if DECODE_HAIER_AC_YRW02
DPRINTLN("Attempting Haier AC YR-W02 decode");
if (decodeHaierACYRW02(results)) return true;
#endif #endif
#if DECODE_HITACHI_AC2 #if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC // HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode"); DPRINTLN("Attempting Hitachi AC2 decode");
if (decodeHitachiAC(results, HITACHI_AC2_BITS)) if (decodeHitachiAC(results, kHitachiAc2Bits)) return true;
return true;
#endif #endif
#if DECODE_HITACHI_AC #if DECODE_HITACHI_AC
DPRINTLN("Attempting Hitachi AC decode"); DPRINTLN("Attempting Hitachi AC decode");
if (decodeHitachiAC(results, HITACHI_AC_BITS)) if (decodeHitachiAC(results, kHitachiAcBits)) return true;
return true;
#endif #endif
#if DECODE_HITACHI_AC1 #if DECODE_HITACHI_AC1
DPRINTLN("Attempting Hitachi AC1 decode"); DPRINTLN("Attempting Hitachi AC1 decode");
if (decodeHitachiAC(results, HITACHI_AC1_BITS)) if (decodeHitachiAC(results, kHitachiAc1Bits)) return true;
return true; #endif
#if DECODE_WHIRLPOOL_AC
DPRINTLN("Attempting Whirlpool AC decode");
if (decodeWhirlpoolAC(results)) return true;
#endif
#if DECODE_SAMSUNG_AC
DPRINTLN("Attempting Samsung AC (extended) decode");
// Check the extended size first, as it should fail fast due to longer length.
if (decodeSamsungAC(results, kSamsungAcExtendedBits, false)) return true;
// Now check for the more common length.
DPRINTLN("Attempting Samsung AC decode");
if (decodeSamsungAC(results, kSamsungAcBits)) return true;
#endif
#if DECODE_ELECTRA_AC
DPRINTLN("Attempting Electra AC decode");
if (decodeElectraAC(results)) return true;
#endif
#if DECODE_PANASONIC_AC
DPRINTLN("Attempting Panasonic AC decode");
if (decodePanasonicAC(results)) return true;
DPRINTLN("Attempting Panasonic AC short decode");
if (decodePanasonicAC(results, kPanasonicAcShortBits)) return true;
#endif
#if DECODE_LUTRON
DPRINTLN("Attempting Lutron decode");
if (decodeLutron(results)) return true;
#endif
#if DECODE_MWM
DPRINTLN("Attempting MWM decode");
if (decodeMWM(results)) return true;
#endif
#if DECODE_VESTEL_AC
DPRINTLN("Attempting Vestel AC decode");
if (decodeVestelAC(results)) return true;
#endif #endif
#if DECODE_HASH #if DECODE_HASH
// decodeHash returns a hash on any input. // decodeHash returns a hash on any input.
@@ -533,9 +548,9 @@ uint32_t IRrecv::ticksHigh(uint32_t usecs, uint8_t tolerance, uint16_t delta) {
// //
// Returns: // Returns:
// Boolean: true if it matches, false if it doesn't. // Boolean: true if it matches, false if it doesn't.
bool IRrecv::match(uint32_t measured, uint32_t desired, bool IRrecv::match(uint32_t measured, uint32_t desired, uint8_t tolerance,
uint8_t tolerance, uint16_t delta) { uint16_t delta) {
measured *= RAWTICK; // Convert to uSecs. measured *= kRawTick; // Convert to uSecs.
DPRINT("Matching: "); DPRINT("Matching: ");
DPRINT(ticksLow(desired, tolerance, delta)); DPRINT(ticksLow(desired, tolerance, delta));
DPRINT(" <= "); DPRINT(" <= ");
@@ -546,7 +561,6 @@ bool IRrecv::match(uint32_t measured, uint32_t desired,
measured <= ticksHigh(desired, tolerance, delta)); measured <= ticksHigh(desired, tolerance, delta));
} }
// Check if we match a pulse(measured) of at least desired within // Check if we match a pulse(measured) of at least desired within
// tolerance percent and/or a fixed delta margin. // tolerance percent and/or a fixed delta margin.
// //
@@ -561,7 +575,7 @@ bool IRrecv::match(uint32_t measured, uint32_t desired,
// Boolean: true if it matches, false if it doesn't. // Boolean: true if it matches, false if it doesn't.
bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired, bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired,
uint8_t tolerance, uint16_t delta) { uint8_t tolerance, uint16_t delta) {
measured *= RAWTICK; // Convert to uSecs. measured *= kRawTick; // Convert to uSecs.
DPRINT("Matching ATLEAST "); DPRINT("Matching ATLEAST ");
DPRINT(measured); DPRINT(measured);
DPRINT(" vs "); DPRINT(" vs ");
@@ -594,10 +608,10 @@ bool IRrecv::matchAtLeast(uint32_t measured, uint32_t desired,
// //
// Returns: // Returns:
// Boolean: true if it matches, false if it doesn't. // Boolean: true if it matches, false if it doesn't.
bool IRrecv::matchMark(uint32_t measured, uint32_t desired, bool IRrecv::matchMark(uint32_t measured, uint32_t desired, uint8_t tolerance,
uint8_t tolerance, int16_t excess) { int16_t excess) {
DPRINT("Matching MARK "); DPRINT("Matching MARK ");
DPRINT(measured * RAWTICK); DPRINT(measured * kRawTick);
DPRINT(" vs "); DPRINT(" vs ");
DPRINT(desired); DPRINT(desired);
DPRINT(" + "); DPRINT(" + ");
@@ -617,10 +631,10 @@ bool IRrecv::matchMark(uint32_t measured, uint32_t desired,
// //
// Returns: // Returns:
// Boolean: true if it matches, false if it doesn't. // Boolean: true if it matches, false if it doesn't.
bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, bool IRrecv::matchSpace(uint32_t measured, uint32_t desired, uint8_t tolerance,
uint8_t tolerance, int16_t excess) { int16_t excess) {
DPRINT("Matching SPACE "); DPRINT("Matching SPACE ");
DPRINT(measured * RAWTICK); DPRINT(measured * kRawTick);
DPRINT(" vs "); DPRINT(" vs ");
DPRINT(desired); DPRINT(desired);
DPRINT(" - "); DPRINT(" - ");
@@ -662,9 +676,8 @@ int16_t IRrecv::compare(uint16_t oldval, uint16_t newval) {
*/ */
bool IRrecv::decodeHash(decode_results *results) { bool IRrecv::decodeHash(decode_results *results) {
// Require at least some samples to prevent triggering on noise // Require at least some samples to prevent triggering on noise
if (results->rawlen < unknown_threshold) if (results->rawlen < unknown_threshold) return false;
return false; int32_t hash = kFnvBasis32;
int32_t hash = FNV_BASIS_32;
// 'rawlen - 2' to avoid the look ahead from going out of bounds. // 'rawlen - 2' to avoid the look ahead from going out of bounds.
// Should probably be -3 to avoid comparing the trailing space entry, // Should probably be -3 to avoid comparing the trailing space entry,
// however it is left this way for compatibility with previously captured // however it is left this way for compatibility with previously captured
@@ -672,7 +685,7 @@ bool IRrecv::decodeHash(decode_results *results) {
for (uint16_t i = 1; i < results->rawlen - 2; i++) { for (uint16_t i = 1; i < results->rawlen - 2; i++) {
int16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]); int16_t value = compare(results->rawbuf[i], results->rawbuf[i + 2]);
// Add value into the hash // Add value into the hash
hash = (hash * FNV_PRIME_32) ^ value; hash = (hash * kFnvPrime32) ^ value;
} }
results->value = hash & 0xFFFFFFFF; results->value = hash & 0xFFFFFFFF;
results->bits = results->rawlen / 2; results->bits = results->rawlen / 2;
@@ -684,7 +697,8 @@ bool IRrecv::decodeHash(decode_results *results) {
#endif // DECODE_HASH #endif // DECODE_HASH
// Match & decode the typical data section of an IR message. // Match & decode the typical data section of an IR message.
// The data value constructed as the Most Significant Bit first. // The data value is stored in the least significant bits reguardless of the
// bit ordering requested.
// //
// Args: // Args:
// data_ptr: A pointer to where we are at in the capture buffer. // data_ptr: A pointer to where we are at in the capture buffer.
@@ -693,34 +707,35 @@ bool IRrecv::decodeHash(decode_results *results) {
// onespace: Nr. of uSeconds in an expected space signal for a '1' bit. // onespace: Nr. of uSeconds in an expected space signal for a '1' bit.
// zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit. // zeromark: Nr. of uSeconds in an expected mark signal for a '0' bit.
// zerospace: Nr. of uSeconds in an expected space signal for a '0' bit. // zerospace: Nr. of uSeconds in an expected space signal for a '0' bit.
// tolerance: Percentage error margin to allow. // tolerance: Percentage error margin to allow. (Def: kTolerance)
// excess: Nr. of useconds. (Def: kMarkExcess)
// MSBfirst: Bit order to save the data in. (Def: true)
// Returns: // Returns:
// A match_result_t structure containing the success (or not), the data value, // A match_result_t structure containing the success (or not), the data value,
// and how many buffer entries were used. // and how many buffer entries were used.
match_result_t IRrecv::matchData(volatile uint16_t *data_ptr, match_result_t IRrecv::matchData(
const uint16_t nbits, const uint16_t onemark, volatile uint16_t *data_ptr, const uint16_t nbits, const uint16_t onemark,
const uint32_t onespace, const uint32_t onespace, const uint16_t zeromark, const uint32_t zerospace,
const uint16_t zeromark, const uint8_t tolerance, const int16_t excess, const bool MSBfirst) {
const uint32_t zerospace,
const uint8_t tolerance) {
match_result_t result; match_result_t result;
result.success = false; // Fail by default. result.success = false; // Fail by default.
result.data = 0; result.data = 0;
for (result.used = 0; for (result.used = 0; result.used < nbits * 2;
result.used < nbits * 2;
result.used += 2, data_ptr += 2) { result.used += 2, data_ptr += 2) {
// Is the bit a '1'? // Is the bit a '1'?
if (matchMark(*data_ptr, onemark, tolerance) && if (matchMark(*data_ptr, onemark, tolerance, excess) &&
matchSpace(*(data_ptr + 1), onespace, tolerance)) matchSpace(*(data_ptr + 1), onespace, tolerance, excess)) {
result.data = (result.data << 1) | 1; result.data = (result.data << 1) | 1;
// or is the bit a '0'? } else if (matchMark(*data_ptr, zeromark, tolerance, excess) &&
else if (matchMark(*data_ptr, zeromark, tolerance) && matchSpace(*(data_ptr + 1), zerospace, tolerance, excess)) {
matchSpace(*(data_ptr + 1), zerospace, tolerance)) result.data <<= 1; // The bit is a '0'.
result.data <<= 1; } else {
else if (!MSBfirst) result.data = reverseBits(result.data, result.used / 2);
return result; // It's neither, so fail. return result; // It's neither, so fail.
} }
}
result.success = true; result.success = true;
if (!MSBfirst) result.data = reverseBits(result.data, nbits);
return result; return result;
} }
+131 -81
View File
@@ -15,40 +15,49 @@
#include "IRremoteESP8266.h" #include "IRremoteESP8266.h"
// Constants // Constants
#define HEADER 2U // Usual nr. of header entries. const uint16_t kHeader = 2; // Usual nr. of header entries.
#define FOOTER 2U // Usual nr. of footer (stop bits) entries. const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries.
#define OFFSET_START 1U // Usual rawbuf entry to start processing from. const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from.
#define MS_TO_USEC(x) (x * 1000U) // Convert milli-Seconds to micro-Seconds. #define MS_TO_USEC(x) (x * 1000U) // Convert milli-Seconds to micro-Seconds.
// Marks tend to be 100us too long, and spaces 100us too short // Marks tend to be 100us too long, and spaces 100us too short
// when received due to sensor lag. // when received due to sensor lag.
#define MARK_EXCESS 50U const uint16_t kMarkExcess = 50;
#define RAWBUF 100U // Default length of raw capture buffer const uint16_t kRawBuf = 100; // Default length of raw capture buffer
#define REPEAT UINT64_MAX const uint64_t kRepeat = UINT64_MAX;
#define UNKNOWN_THRESHOLD 6U // Default min size of reported UNKNOWN messages. // Default min size of reported UNKNOWN messages.
const uint16_t kUnknownThreshold = 6;
// receiver states // receiver states
#define STATE_IDLE 2U const uint8_t kIdleState = 2;
#define STATE_MARK 3U const uint8_t kMarkState = 3;
#define STATE_SPACE 4U const uint8_t kSpaceState = 4;
#define STATE_STOP 5U const uint8_t kStopState = 5;
#define TOLERANCE 25U // default percent tolerance in measurements const uint8_t kTolerance = 25; // default percent tolerance in measurements.
#define RAWTICK 2U // Capture tick to uSec factor. const uint16_t kRawTick = 2; // Capture tick to uSec factor.
#define RAWTICK kRawTick // Deprecated. For legacy user code support only.
// How long (ms) before we give up wait for more data? // How long (ms) before we give up wait for more data?
// Don't exceed MAX_TIMEOUT_MS without a good reason. // Don't exceed kMaxTimeoutMs without a good reason.
// That is the capture buffers maximum value size. (UINT16_MAX / RAWTICK) // That is the capture buffers maximum value size. (UINT16_MAX / kRawTick)
// Typically messages/protocols tend to repeat around the 100ms timeframe, // Typically messages/protocols tend to repeat around the 100ms timeframe,
// thus we should timeout before that to give us some time to try to decode // thus we should timeout before that to give us some time to try to decode
// before we need to start capturing a possible new message. // before we need to start capturing a possible new message.
// Typically 15ms suits most applications. However, some protocols demand a // Typically 15ms suits most applications. However, some protocols demand a
// higher value. e.g. 90ms for XMP-1 and some aircon units. // higher value. e.g. 90ms for XMP-1 and some aircon units.
#define TIMEOUT_MS 15U // In MilliSeconds. const uint8_t kTimeoutMs = 15; // In MilliSeconds.
#define MAX_TIMEOUT_MS (RAWTICK * UINT16_MAX / MS_TO_USEC(1)) #define TIMEOUT_MS kTimeoutMs // For legacy documentation.
const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1));
// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param // Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
#define FNV_PRIME_32 16777619UL const uint32_t kFnvPrime32 = 16777619UL;
#define FNV_BASIS_32 2166136261UL const uint32_t kFnvBasis32 = 2166136261UL;
#if DECODE_AC
// Hitachi AC is the current largest state size. // Hitachi AC is the current largest state size.
#define STATE_SIZE_MAX HITACHI_AC2_STATE_LENGTH const uint16_t kStateSizeMax = kHitachiAc2StateLength;
#else
// Just define something
const uint16_t kStateSizeMax = 0;
#endif
// Types // Types
// information for the interrupt handler // information for the interrupt handler
@@ -87,9 +96,7 @@ class decode_results {
uint32_t address; // Decoded device address. uint32_t address; // Decoded device address.
uint32_t command; // Decoded command. uint32_t command; // Decoded command.
}; };
#if DECODE_AC // Only include state if we must. It's big. uint8_t state[kStateSizeMax]; // Multi-byte results.
uint8_t state[STATE_SIZE_MAX]; // Complex multi-byte A/C result.
#endif
}; };
uint16_t bits; // Number of bits in decoded value uint16_t bits; // Number of bits in decoded value
volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks
@@ -101,8 +108,8 @@ class decode_results {
// main class for receiving IR // main class for receiving IR
class IRrecv { class IRrecv {
public: public:
explicit IRrecv(uint16_t recvpin, uint16_t bufsize = RAWBUF, explicit IRrecv(uint16_t recvpin, uint16_t bufsize = kRawBuf,
uint8_t timeout = TIMEOUT_MS, uint8_t timeout = kTimeoutMs,
bool save_buffer = false); // Constructor bool save_buffer = false); // Constructor
~IRrecv(); // Destructor ~IRrecv(); // Destructor
bool decode(decode_results *results, irparams_t *save = NULL); bool decode(decode_results *results, irparams_t *save = NULL);
@@ -114,13 +121,13 @@ class IRrecv {
void setUnknownThreshold(uint16_t length); void setUnknownThreshold(uint16_t length);
#endif #endif
static bool match(uint32_t measured, uint32_t desired, static bool match(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, uint16_t delta = 0); uint8_t tolerance = kTolerance, uint16_t delta = 0);
static bool matchMark(uint32_t measured, uint32_t desired, static bool matchMark(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, uint8_t tolerance = kTolerance,
int16_t excess = MARK_EXCESS); int16_t excess = kMarkExcess);
static bool matchSpace(uint32_t measured, uint32_t desired, static bool matchSpace(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, uint8_t tolerance = kTolerance,
int16_t excess = MARK_EXCESS); int16_t excess = kMarkExcess);
#ifndef UNIT_TEST #ifndef UNIT_TEST
private: private:
@@ -132,84 +139,93 @@ class IRrecv {
// These are called by decode // These are called by decode
void copyIrParams(volatile irparams_t *src, irparams_t *dst); void copyIrParams(volatile irparams_t *src, irparams_t *dst);
int16_t compare(uint16_t oldval, uint16_t newval); int16_t compare(uint16_t oldval, uint16_t newval);
static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = TOLERANCE, static uint32_t ticksLow(uint32_t usecs, uint8_t tolerance = kTolerance,
uint16_t delta = 0); uint16_t delta = 0);
static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = TOLERANCE, static uint32_t ticksHigh(uint32_t usecs, uint8_t tolerance = kTolerance,
uint16_t delta = 0); uint16_t delta = 0);
bool matchAtLeast(uint32_t measured, uint32_t desired, bool matchAtLeast(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, uint16_t delta = 0); uint8_t tolerance = kTolerance, uint16_t delta = 0);
match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits, match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits,
const uint16_t onemark, const uint32_t onespace, const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace, const uint16_t zeromark, const uint32_t zerospace,
const uint8_t tolerance = TOLERANCE); const uint8_t tolerance = kTolerance,
const int16_t excess = kMarkExcess,
const bool MSBfirst = true);
bool decodeHash(decode_results *results); bool decodeHash(decode_results *results);
#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO) #if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || SEND_SANYO)
bool decodeNEC(decode_results *results, uint16_t nbits = NEC_BITS, bool decodeNEC(decode_results *results, uint16_t nbits = kNECBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_SONY #if DECODE_SONY
bool decodeSony(decode_results *results, uint16_t nbits = SONY_MIN_BITS, bool decodeSony(decode_results *results, uint16_t nbits = kSonyMinBits,
bool strict = false); bool strict = false);
#endif #endif
#if DECODE_SANYO #if DECODE_SANYO
// DISABLED due to poor quality. // DISABLED due to poor quality.
// bool decodeSanyo(decode_results *results, // bool decodeSanyo(decode_results *results,
// uint16_t nbits = SANYO_SA8650B_BITS, // uint16_t nbits = kSanyoSA8650BBits,
// bool strict = false); // bool strict = false);
bool decodeSanyoLC7461(decode_results *results, bool decodeSanyoLC7461(decode_results *results,
uint16_t nbits = SANYO_LC7461_BITS, uint16_t nbits = kSanyoLC7461Bits, bool strict = true);
bool strict = true);
#endif #endif
#if DECODE_MITSUBISHI #if DECODE_MITSUBISHI
bool decodeMitsubishi(decode_results *results, bool decodeMitsubishi(decode_results *results,
uint16_t nbits = MITSUBISHI_BITS, uint16_t nbits = kMitsubishiBits, bool strict = true);
bool strict = true);
#endif #endif
#if DECODE_MITSUBISHI2 #if DECODE_MITSUBISHI2
bool decodeMitsubishi2(decode_results *results, bool decodeMitsubishi2(decode_results *results,
uint16_t nbits = MITSUBISHI_BITS, uint16_t nbits = kMitsubishiBits, bool strict = true);
bool strict = true);
#endif #endif
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG) #if DECODE_MITSUBISHI_AC
bool decodeMitsubishiAC(decode_results *results,
uint16_t nbits = kMitsubishiACBits,
bool strict = false);
#endif
#if (DECODE_RC5 || DECODE_R6 || DECODE_LASERTAG || DECODE_MWM)
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used, int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
uint16_t bitTime, uint8_t tolerance = TOLERANCE, uint16_t bitTime, uint8_t tolerance = kTolerance,
int16_t excess = MARK_EXCESS, uint16_t delta = 0); int16_t excess = kMarkExcess, uint16_t delta = 0,
uint8_t maxwidth = 3);
#endif #endif
#if DECODE_RC5 #if DECODE_RC5
bool decodeRC5(decode_results *results, uint16_t nbits = RC5X_BITS, bool decodeRC5(decode_results *results, uint16_t nbits = kRC5XBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_RC6 #if DECODE_RC6
bool decodeRC6(decode_results *results, uint16_t nbits = RC6_MODE0_BITS, bool decodeRC6(decode_results *results, uint16_t nbits = kRC6Mode0Bits,
bool strict = false); bool strict = false);
#endif #endif
#if DECODE_RCMM #if DECODE_RCMM
bool decodeRCMM(decode_results *results, uint16_t nbits = RCMM_BITS, bool decodeRCMM(decode_results *results, uint16_t nbits = kRCMMBits,
bool strict = false); bool strict = false);
#endif #endif
#if (DECODE_PANASONIC || DECODE_DENON) #if (DECODE_PANASONIC || DECODE_DENON)
bool decodePanasonic(decode_results *results, uint16_t nbits = PANASONIC_BITS, bool decodePanasonic(decode_results *results, uint16_t nbits = kPanasonicBits,
bool strict = false, bool strict = false,
uint32_t manufacturer = PANASONIC_MANUFACTURER); uint32_t manufacturer = kPanasonicManufacturer);
#endif #endif
#if DECODE_LG #if DECODE_LG
bool decodeLG(decode_results *results, uint16_t nbits = LG_BITS, bool decodeLG(decode_results *results, uint16_t nbits = kLgBits,
bool strict = false); bool strict = false);
#endif #endif
#if DECODE_JVC #if DECODE_JVC
bool decodeJVC(decode_results *results, uint16_t nbits = JVC_BITS, bool decodeJVC(decode_results *results, uint16_t nbits = kJvcBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_SAMSUNG #if DECODE_SAMSUNG
bool decodeSAMSUNG(decode_results *results, uint16_t nbits = SAMSUNG_BITS, bool decodeSAMSUNG(decode_results *results, uint16_t nbits = kSamsungBits,
bool strict = true);
#endif
#if DECODE_SAMSUNG_AC
bool decodeSamsungAC(decode_results *results, uint16_t nbits = kSamsungAcBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_WHYNTER #if DECODE_WHYNTER
bool decodeWhynter(decode_results *results, uint16_t nbits = WHYNTER_BITS, bool decodeWhynter(decode_results *results, uint16_t nbits = kWhynterBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_COOLIX #if DECODE_COOLIX
bool decodeCOOLIX(decode_results *results, uint16_t nbits = COOLIX_BITS, bool decodeCOOLIX(decode_results *results, uint16_t nbits = kCoolixBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_DENON #if DECODE_DENON
@@ -217,75 +233,109 @@ class IRrecv {
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_DISH #if DECODE_DISH
bool decodeDISH(decode_results *results, uint16_t nbits = DISH_BITS, bool decodeDISH(decode_results *results, uint16_t nbits = kDishBits,
bool strict = true); bool strict = true);
#endif #endif
#if (DECODE_SHARP || DECODE_DENON) #if (DECODE_SHARP || DECODE_DENON)
bool decodeSharp(decode_results *results, uint16_t nbits = SHARP_BITS, bool decodeSharp(decode_results *results, uint16_t nbits = kSharpBits,
bool strict = true, bool expansion = true); bool strict = true, bool expansion = true);
#endif #endif
#if DECODE_AIWA_RC_T501 #if DECODE_AIWA_RC_T501
bool decodeAiwaRCT501(decode_results *results, bool decodeAiwaRCT501(decode_results *results,
uint16_t nbits = AIWA_RC_T501_BITS, bool strict = true); uint16_t nbits = kAiwaRcT501Bits, bool strict = true);
#endif #endif
#if DECODE_NIKAI #if DECODE_NIKAI
bool decodeNikai(decode_results *results, uint16_t nbits = NIKAI_BITS, bool decodeNikai(decode_results *results, uint16_t nbits = kNikaiBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_MAGIQUEST #if DECODE_MAGIQUEST
bool decodeMagiQuest(decode_results *results, uint16_t nbits = MAGIQUEST_BITS, bool decodeMagiQuest(decode_results *results, uint16_t nbits = kMagiquestBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_KELVINATOR #if DECODE_KELVINATOR
bool decodeKelvinator(decode_results *results, bool decodeKelvinator(decode_results *results,
uint16_t nbits = KELVINATOR_BITS, uint16_t nbits = kKelvinatorBits, bool strict = true);
bool strict = true);
#endif #endif
#if DECODE_DAIKIN #if DECODE_DAIKIN
bool decodeDaikin(decode_results *results, uint16_t nbits = DAIKIN_RAW_BITS, bool decodeDaikin(decode_results *results, uint16_t nbits = kDaikinRawBits,
bool strict = true);
#endif
#if DECODE_DAIKIN2
bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_TOSHIBA_AC #if DECODE_TOSHIBA_AC
bool decodeToshibaAC(decode_results *results, bool decodeToshibaAC(decode_results *results,
uint16_t nbytes = TOSHIBA_AC_BITS, uint16_t nbytes = kToshibaACBits, bool strict = true);
bool strict = true);
#endif #endif
#if DECODE_MIDEA #if DECODE_MIDEA
bool decodeMidea(decode_results *results, uint16_t nbits = MIDEA_BITS, bool decodeMidea(decode_results *results, uint16_t nbits = kMideaBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_FUJITSU_AC #if DECODE_FUJITSU_AC
bool decodeFujitsuAC(decode_results *results, bool decodeFujitsuAC(decode_results *results, uint16_t nbits = kFujitsuAcBits,
uint16_t nbits = FUJITSU_AC_BITS,
bool strict = false); bool strict = false);
#endif #endif
#if DECODE_LASERTAG #if DECODE_LASERTAG
bool decodeLasertag(decode_results *results, uint16_t nbits = LASERTAG_BITS, bool decodeLasertag(decode_results *results, uint16_t nbits = kLasertagBits,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_CARRIER_AC #if DECODE_CARRIER_AC
bool decodeCarrierAC(decode_results *results, bool decodeCarrierAC(decode_results *results, uint16_t nbits = kCarrierAcBits,
uint16_t nbits = CARRIER_AC_BITS,
bool strict = true); bool strict = true);
#endif #endif
#if DECODE_GREE #if DECODE_GREE
bool decodeGree(decode_results *results, bool decodeGree(decode_results *results, uint16_t nbits = kGreeBits,
uint16_t nbits = GREE_BITS, bool strict = true); bool strict = true);
#endif #endif
#if DECODE_HAIER_AC #if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02)
bool decodeHaierAC(decode_results *results, bool decodeHaierAC(decode_results *results, uint16_t nbits = kHaierACBits,
uint16_t nbits = HAIER_AC_BITS, bool strict = true); bool strict = true);
#endif
#if DECODE_HAIER_AC_YRW02
bool decodeHaierACYRW02(decode_results *results,
uint16_t nbits = kHaierACYRW02Bits,
bool strict = true);
#endif #endif
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2) #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2)
bool decodeHitachiAC(decode_results *results, bool decodeHitachiAC(decode_results *results, uint16_t nbits = kHitachiAcBits,
uint16_t nbits = HITACHI_AC_BITS, bool strict = true); bool strict = true);
#endif #endif
#if DECODE_HITACHI_AC1 #if DECODE_HITACHI_AC1
bool decodeHitachiAC1(decode_results *results, bool decodeHitachiAC1(decode_results *results,
uint16_t nbits = HITACHI_AC1_BITS, bool strict = true); uint16_t nbits = kHitachiAc1Bits, bool strict = true);
#endif #endif
#if DECODE_GICABLE #if DECODE_GICABLE
bool decodeGICable(decode_results *results, uint16_t nbits = GICABLE_BITS, bool decodeGICable(decode_results *results, uint16_t nbits = kGicableBits,
bool strict = true);
#endif
#if DECODE_WHIRLPOOL_AC
bool decodeWhirlpoolAC(decode_results *results,
uint16_t nbits = kWhirlpoolAcBits, bool strict = true);
#endif
#if DECODE_LUTRON
bool decodeLutron(decode_results *results, uint16_t nbits = kLutronBits,
bool strict = true);
#endif
#if DECODE_ELECTRA_AC
bool decodeElectraAC(decode_results *results, uint16_t nbits = kElectraAcBits,
bool strict = true);
#endif
#if DECODE_PANASONIC_AC
bool decodePanasonicAC(decode_results *results,
uint16_t nbits = kPanasonicAcBits, bool strict = true);
#endif
#if DECODE_PIONEER
bool decodePioneer(decode_results *results,
const uint16_t nbits = kPioneerBits,
const bool strict = true);
#endif
#if DECODE_MWM
bool decodeMWM(decode_results *results, uint16_t nbits = 24,
bool strict = true);
#endif
#if DECODE_VESTEL_AC
bool decodeVestelAC(decode_results *results, uint16_t nbits = kVestelACBits,
bool strict = true); bool strict = true);
#endif #endif
}; };
+225 -90
View File
@@ -33,7 +33,8 @@
* (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/) * (https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/)
* Fujitsu A/C code added by jonnygraham * Fujitsu A/C code added by jonnygraham
* Trotec AC code by stufisher * Trotec AC code by stufisher
* Carrier AC code by crankyoldgit * Carrier & Haier AC code by crankyoldgit
* Vestel AC code by Erdem U. Altınyurt
* *
* GPL license, all text above must be included in any redistribution * GPL license, all text above must be included in any redistribution
****************************************************/ ****************************************************/
@@ -48,7 +49,7 @@
#endif #endif
// Library Version // Library Version
#define _IRREMOTEESP8266_VERSION_ "2.4.2" #define _IRREMOTEESP8266_VERSION_ "2.5.5"
// Supported IR protocols // Supported IR protocols
// Each protocol you include costs memory and, during decode, costs time // Each protocol you include costs memory and, during decode, costs time
// Disable (set to false) all the protocols you do not need/want! // Disable (set to false) all the protocols you do not need/want!
@@ -85,6 +86,9 @@
#define DECODE_SAMSUNG true #define DECODE_SAMSUNG true
#define SEND_SAMSUNG true #define SEND_SAMSUNG true
#define DECODE_SAMSUNG_AC true
#define SEND_SAMSUNG_AC true
#define DECODE_WHYNTER true #define DECODE_WHYNTER true
#define SEND_WHYNTER true #define SEND_WHYNTER true
@@ -115,7 +119,7 @@
#define DECODE_KELVINATOR true #define DECODE_KELVINATOR true
#define SEND_KELVINATOR true #define SEND_KELVINATOR true
#define DECODE_MITSUBISHI_AC false // Not written. #define DECODE_MITSUBISHI_AC true // Beta.
#define SEND_MITSUBISHI_AC true #define SEND_MITSUBISHI_AC true
#define DECODE_FUJITSU_AC true #define DECODE_FUJITSU_AC true
@@ -175,10 +179,39 @@
#define DECODE_GICABLE true #define DECODE_GICABLE true
#define SEND_GICABLE true #define SEND_GICABLE true
#define DECODE_HAIER_AC_YRW02 true
#define SEND_HAIER_AC_YRW02 true
#define DECODE_WHIRLPOOL_AC true
#define SEND_WHIRLPOOL_AC true
#define DECODE_LUTRON true
#define SEND_LUTRON true
#define DECODE_ELECTRA_AC true
#define SEND_ELECTRA_AC true
#define DECODE_PANASONIC_AC true
#define SEND_PANASONIC_AC true
#define DECODE_MWM true
#define SEND_MWM true
#define DECODE_PIONEER true
#define SEND_PIONEER true
#define DECODE_DAIKIN2 true
#define SEND_DAIKIN2 true
#define DECODE_VESTEL_AC true
#define SEND_VESTEL_AC true
#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ #if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) 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 )
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else #else
#define DECODE_AC false // We don't need that infrastructure. #define DECODE_AC false // We don't need that infrastructure.
@@ -202,127 +235,229 @@ enum decode_type_t {
RC6, RC6,
NEC, NEC,
SONY, SONY,
PANASONIC, PANASONIC, // (5)
JVC, JVC,
SAMSUNG, SAMSUNG,
WHYNTER, WHYNTER,
AIWA_RC_T501, AIWA_RC_T501,
LG, LG, // (10)
SANYO, SANYO,
MITSUBISHI, MITSUBISHI,
DISH, DISH,
SHARP, SHARP,
COOLIX, COOLIX, // (15)
DAIKIN, DAIKIN,
DENON, DENON,
KELVINATOR, KELVINATOR,
SHERWOOD, SHERWOOD,
MITSUBISHI_AC, MITSUBISHI_AC, // (20)
RCMM, RCMM,
SANYO_LC7461, SANYO_LC7461,
RC5X, RC5X,
GREE, GREE,
PRONTO, // Technically not a protocol, but an encoding. PRONTO, // Technically not a protocol, but an encoding. (25)
NEC_LIKE, NEC_LIKE,
ARGO, ARGO,
TROTEC, TROTEC,
NIKAI, NIKAI,
RAW, // Technically not a protocol, but an encoding. RAW, // Technically not a protocol, but an encoding. (30)
GLOBALCACHE, // Technically not a protocol, but an encoding. GLOBALCACHE, // Technically not a protocol, but an encoding.
TOSHIBA_AC, TOSHIBA_AC,
FUJITSU_AC, FUJITSU_AC,
MIDEA, MIDEA,
MAGIQUEST, MAGIQUEST, // (35)
LASERTAG, LASERTAG,
CARRIER_AC, CARRIER_AC,
HAIER_AC, HAIER_AC,
MITSUBISHI2, MITSUBISHI2,
HITACHI_AC, HITACHI_AC, // (40)
HITACHI_AC1, HITACHI_AC1,
HITACHI_AC2, HITACHI_AC2,
GICABLE GICABLE,
HAIER_AC_YRW02,
WHIRLPOOL_AC, // (45)
SAMSUNG_AC,
LUTRON,
ELECTRA_AC,
PANASONIC_AC,
PIONEER, // 50
LG2,
MWM,
DAIKIN2,
VESTEL_AC,
}; };
// Message lengths & required repeat values // Message lengths & required repeat values
#define AIWA_RC_T501_BITS 15U const uint16_t kNoRepeat = 0;
#define AIWA_RC_T501_MIN_REPEAT 1U const uint16_t kSingleRepeat = 1;
#define COOLIX_BITS 24U
#define CARRIER_AC_BITS 32U const uint16_t kAiwaRcT501Bits = 15;
#define CARRIER_AC_MIN_REPEAT 0U 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 kCarrierAcBits = 32;
const uint16_t kCarrierAcMinRepeat = kNoRepeat;
// Daikin has a lot of static stuff that is discarded // Daikin has a lot of static stuff that is discarded
#define DAIKIN_RAW_BITS 583U const uint16_t kDaikinRawBits = 583;
#define DAIKIN_COMMAND_LENGTH 27U const uint16_t kDaikinStateLength = 27;
#define DAIKIN_BITS (DAIKIN_COMMAND_LENGTH * 8) const uint16_t kDaikinBits = kDaikinStateLength * 8;
#define DENON_BITS SHARP_BITS const uint16_t kDaikinDefaultRepeat = kNoRepeat;
#define DENON_48_BITS PANASONIC_BITS const uint16_t kDaikin2StateLength = 39;
#define DENON_LEGACY_BITS 14U const uint16_t kDaikin2Bits = kDaikin2StateLength * 8;
#define DISH_BITS 16U const uint16_t kDaikin2DefaultRepeat = kNoRepeat;
#define DISH_MIN_REPEAT 3U const uint16_t kDenonBits = 15;
#define GICABLE_BITS 16U const uint16_t kDenonLegacyBits = 14;
#define GICABLE_MIN_REPEAT 1U const uint16_t kDishBits = 16;
#define GREE_STATE_LENGTH 8U const uint16_t kDishMinRepeat = 3;
#define GREE_BITS (GREE_STATE_LENGTH * 8) const uint16_t kElectraAcStateLength = 13;
#define HAIER_AC_STATE_LENGTH 9U const uint16_t kElectraAcBits = kElectraAcStateLength * 8;
#define HAIER_AC_BITS (HAIER_AC_STATE_LENGTH * 8) const uint16_t kFujitsuAcMinRepeat = kNoRepeat;
#define HITACHI_AC_STATE_LENGTH 28U const uint16_t kFujitsuAcStateLength = 16;
#define HITACHI_AC_BITS (HITACHI_AC_STATE_LENGTH * 8) const uint16_t kFujitsuAcStateLengthShort = 7;
#define HITACHI_AC1_STATE_LENGTH 13U const uint16_t kFujitsuAcBits = kFujitsuAcStateLength * 8;
#define HITACHI_AC1_BITS (HITACHI_AC1_STATE_LENGTH * 8) const uint16_t kFujitsuAcMinBits = (kFujitsuAcStateLengthShort - 1) * 8;
#define HITACHI_AC2_STATE_LENGTH 53U const uint16_t kGicableBits = 16;
#define HITACHI_AC2_BITS (HITACHI_AC2_STATE_LENGTH * 8) const uint16_t kGicableMinRepeat = kSingleRepeat;
#define JVC_BITS 16U const uint16_t kGreeStateLength = 8;
#define KELVINATOR_STATE_LENGTH 16U const uint16_t kGreeBits = kGreeStateLength * 8;
#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8) const uint16_t kGreeDefaultRepeat = kNoRepeat;
#define LG_BITS 28U const uint16_t kHaierACStateLength = 9;
#define LG32_BITS 32U const uint16_t kHaierACBits = kHaierACStateLength * 8;
#define MITSUBISHI_BITS 16U const uint16_t kHaierAcDefaultRepeat = kNoRepeat;
const uint16_t kHaierACYRW02StateLength = 14;
const uint16_t kHaierACYRW02Bits = kHaierACYRW02StateLength * 8;
const uint16_t kHaierAcYrw02DefaultRepeat = kNoRepeat;
const uint16_t kHitachiAcStateLength = 28;
const uint16_t kHitachiAcBits = kHitachiAcStateLength * 8;
const uint16_t kHitachiAcDefaultRepeat = kNoRepeat;
const uint16_t kHitachiAc1StateLength = 13;
const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8;
const uint16_t kHitachiAc2StateLength = 53;
const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8;
const uint16_t kJvcBits = 16;
const uint16_t kKelvinatorStateLength = 16;
const uint16_t kKelvinatorBits = kKelvinatorStateLength * 8;
const uint16_t kKelvinatorDefaultRepeat = kNoRepeat;
const uint16_t kLasertagBits = 13;
const uint16_t kLasertagMinRepeat = kNoRepeat;
const uint16_t kLgBits = 28;
const uint16_t kLg32Bits = 32;
const uint16_t kLutronBits = 35;
const uint16_t kMagiquestBits = 56;
const uint16_t kMideaBits = 48;
const uint16_t kMideaMinRepeat = kNoRepeat;
const uint16_t kMitsubishiBits = 16;
// TODO(anyone): Verify that the Mitsubishi repeat is really needed. // TODO(anyone): Verify that the Mitsubishi repeat is really needed.
#define MITSUBISHI_MIN_REPEAT 1U // Based on marcosamarinho's code. // Based on marcosamarinho's code.
#define MITSUBISHI_AC_STATE_LENGTH 18U const uint16_t kMitsubishiMinRepeat = kSingleRepeat;
#define MITSUBISHI_AC_MIN_REPEAT 1U const uint16_t kMitsubishiACStateLength = 18;
#define FUJITSU_AC_MIN_REPEAT 0U const uint16_t kMitsubishiACBits = kMitsubishiACStateLength * 8;
#define FUJITSU_AC_STATE_LENGTH 16U const uint16_t kMitsubishiACMinRepeat = kSingleRepeat;
#define FUJITSU_AC_STATE_LENGTH_SHORT 7U const uint16_t kNikaiBits = 24;
#define FUJITSU_AC_BITS (FUJITSU_AC_STATE_LENGTH * 8) const uint16_t kNECBits = 32;
#define FUJITSU_AC_MIN_BITS ((FUJITSU_AC_STATE_LENGTH_SHORT - 1) * 8) const uint16_t kPanasonicBits = 48;
#define NEC_BITS 32U const uint32_t kPanasonicManufacturer = 0x4004;
#define PANASONIC_BITS 48U const uint16_t kPanasonicAcStateLength = 27;
#define PANASONIC_MANUFACTURER 0x4004ULL const uint16_t kPanasonicAcStateShortLength = 16;
#define PRONTO_MIN_LENGTH 6U const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8;
#define RC5_RAW_BITS 14U const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8;
#define RC5_BITS RC5_RAW_BITS - 2U const uint16_t kPanasonicAcDefaultRepeat = kNoRepeat;
#define RC5X_BITS RC5_RAW_BITS - 1U const uint16_t kPioneerBits = 64;
#define RC6_MODE0_BITS 20U // Excludes the 'start' bit. const uint16_t kProntoMinLength = 6;
#define RC6_36_BITS 36U // Excludes the 'start' bit. const uint16_t kRC5RawBits = 14;
#define RCMM_BITS 24U const uint16_t kRC5Bits = kRC5RawBits - 2;
#define SAMSUNG_BITS 32U const uint16_t kRC5XBits = kRC5RawBits - 1;
#define SANYO_SA8650B_BITS 12U const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit.
#define SANYO_LC7461_ADDRESS_BITS 13U const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit.
#define SANYO_LC7461_COMMAND_BITS 8U const uint16_t kRCMMBits = 24;
#define SANYO_LC7461_BITS ((SANYO_LC7461_ADDRESS_BITS + \ const uint16_t kSamsungBits = 32;
SANYO_LC7461_COMMAND_BITS) * 2) const uint16_t kSamsungAcStateLength = 14;
#define SHARP_ADDRESS_BITS 5U const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8;
#define SHARP_COMMAND_BITS 8U const uint16_t kSamsungAcExtendedStateLength = 21;
#define SHARP_BITS (SHARP_ADDRESS_BITS + SHARP_COMMAND_BITS + 2) // 15U const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8;
#define SHERWOOD_BITS NEC_BITS const uint16_t kSamsungAcDefaultRepeat = kNoRepeat;
#define SHERWOOD_MIN_REPEAT 1U const uint16_t kSanyoSA8650BBits = 12;
#define SONY_12_BITS 12U const uint16_t kSanyoLC7461AddressBits = 13;
#define SONY_15_BITS 15U const uint16_t kSanyoLC7461CommandBits = 8;
#define SONY_20_BITS 20U const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits +
#define SONY_MIN_BITS SONY_12_BITS kSanyoLC7461CommandBits) * 2;
#define SONY_MIN_REPEAT 2U const uint8_t kSharpAddressBits = 5;
#define TOSHIBA_AC_STATE_LENGTH 9U const uint8_t kSharpCommandBits = 8;
#define TOSHIBA_AC_BITS (TOSHIBA_AC_STATE_LENGTH * 8) const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15
#define TOSHIBA_AC_MIN_REPEAT 1U const uint8_t kSherwoodBits = kNECBits;
#define TROTEC_COMMAND_LENGTH 9U const uint16_t kSherwoodMinRepeat = kSingleRepeat;
#define WHYNTER_BITS 32U const uint16_t kSony12Bits = 12;
#define ARGO_COMMAND_LENGTH 12U const uint16_t kSony15Bits = 15;
#define NIKAI_BITS 24U const uint16_t kSony20Bits = 20;
#define MAGIQUEST_BITS 56U const uint16_t kSonyMinBits = 12;
#define MIDEA_BITS 48U const uint16_t kSonyMinRepeat = 2;
#define MIDEA_MIN_REPEAT 0U const uint16_t kToshibaACStateLength = 9;
#define LASERTAG_BITS 13U const uint16_t kToshibaACBits = kToshibaACStateLength * 8;
#define LASERTAG_MIN_REPEAT 0U const uint16_t kToshibaACMinRepeat = kSingleRepeat;
const uint16_t kTrotecStateLength = 9;
const uint16_t kTrotecDefaultRepeat = kNoRepeat;
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;
// Legacy defines. (Deprecated)
#define AIWA_RC_T501_BITS kAiwaRcT501Bits
#define ARGO_COMMAND_LENGTH kArgoStateLength
#define COOLIX_BITS kCoolixBits
#define CARRIER_AC_BITS kCarrierAcBits
#define DAIKIN_COMMAND_LENGTH kDaikinStateLength
#define DENON_BITS kDenonBits
#define DENON_48_BITS kPanasonicBits
#define DENON_LEGACY_BITS kDenonLegacyBits
#define DISH_BITS kDishBits
#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat
#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength
#define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort
#define FUJITSU_AC_BITS kFujitsuAcBits
#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits
#define GICABLE_BITS kGicableBits
#define GREE_STATE_LENGTH kGreeStateLength
#define HAIER_AC_STATE_LENGTH kHaierACStateLength
#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength
#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength
#define HITACHI_AC_BITS kHitachiAcBits
#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength
#define HITACHI_AC1_BITS kHitachiAc1Bits
#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength
#define HITACHI_AC2_BITS kHitachiAc2Bits
#define JVC_BITS kJvcBits
#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength
#define LASERTAG_BITS kLasertagBits
#define LG_BITS kLgBits
#define LG32_BITS kLg32Bits
#define MAGIQUEST_BITS kMagiquestBits
#define MIDEA_BITS kMideaBits
#define MITSUBISHI_BITS kMitsubishiBits
#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength
#define NEC_BITS kNECBits
#define NIKAI_BITS kNikaiBits
#define PANASONIC_BITS kPanasonicBits
#define RC5_BITS kRC5Bits
#define RC5X_BITS kRC5XBits
#define RC6_MODE0_BITS kRC6Mode0Bits
#define RC6_36_BITS kRC6_36Bits
#define RCMM_BITS kRCMMBits
#define SANYO_LC7461_BITS kSanyoLC7461Bits
#define SAMSUNG_BITS kSamsungBits
#define SANYO_SA8650B_BITS kSanyoSA8650BBits
#define SHARP_BITS kSharpBits
#define SHERWOOD_BITS kSherwoodBits
#define SONY_12_BITS kSony12Bits
#define SONY_15_BITS kSony15Bits
#define SONY_20_BITS kSony20Bits
#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength
#define TROTEC_COMMAND_LENGTH kTrotecStateLength
#define WHYNTER_BITS kWhynterBits
// Turn on Debugging information by uncommenting the following line. // Turn on Debugging information by uncommenting the following line.
// #define DEBUG 1 // #define DEBUG 1
+95 -42
View File
@@ -34,9 +34,8 @@
// to change the duty cycle etc. // to change the duty cycle etc.
// Returns: // Returns:
// An IRsend object. // An IRsend object.
IRsend::IRsend(uint16_t IRsendPin, bool inverted, IRsend::IRsend(uint16_t IRsendPin, bool inverted, bool use_modulation)
bool use_modulation) : IRpin(IRsendPin), : IRpin(IRsendPin), periodOffset(kPeriodOffset) {
periodOffset(PERIOD_OFFSET) {
if (inverted) { if (inverted) {
outputOn = LOW; outputOn = LOW;
outputOff = HIGH; outputOff = HIGH;
@@ -46,9 +45,9 @@ IRsend::IRsend(uint16_t IRsendPin, bool inverted,
} }
modulation = use_modulation; modulation = use_modulation;
if (modulation) if (modulation)
_dutycycle = DUTY_DEFAULT; _dutycycle = kDutyDefault;
else else
_dutycycle = DUTY_MAX; _dutycycle = kDutyMax;
} }
// Enable the pin for output. // Enable the pin for output.
@@ -82,7 +81,8 @@ void IRsend::ledOn() {
// nr. of uSeconds. // nr. of uSeconds.
uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) { uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) {
if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty. if (hz == 0) hz = 1; // Avoid Zero hz. Divide by Zero is nasty.
uint32_t period = (1000000UL + hz/2) / hz; // The equiv of round(1000000/hz). uint32_t period =
(1000000UL + hz / 2) / hz; // The equiv of round(1000000/hz).
// Apply the offset and ensure we don't result in a <= 0 value. // Apply the offset and ensure we don't result in a <= 0 value.
if (use_offset) if (use_offset)
return std::max((uint32_t)1, period + periodOffset); return std::max((uint32_t)1, period + periodOffset);
@@ -104,15 +104,15 @@ uint32_t IRsend::calcUSecPeriod(uint32_t hz, bool use_offset) {
void IRsend::enableIROut(uint32_t freq, uint8_t duty) { void IRsend::enableIROut(uint32_t freq, uint8_t duty) {
// Set the duty cycle to use if we want freq. modulation. // Set the duty cycle to use if we want freq. modulation.
if (modulation) { if (modulation) {
_dutycycle = std::min(duty, (uint8_t) DUTY_MAX); _dutycycle = std::min(duty, kDutyMax);
} else { } else {
_dutycycle = DUTY_MAX; _dutycycle = kDutyMax;
} }
if (freq < 1000) // Were we given kHz? Supports the old call usage. if (freq < 1000) // Were we given kHz? Supports the old call usage.
freq *= 1000; freq *= 1000;
uint32_t period = calcUSecPeriod(freq); uint32_t period = calcUSecPeriod(freq);
// Nr. of uSeconds the LED will be on per pulse. // Nr. of uSeconds the LED will be on per pulse.
onTimePeriod = (period * _dutycycle) / DUTY_MAX; onTimePeriod = (period * _dutycycle) / kDutyMax;
// Nr. of uSeconds the LED will be off per pulse. // Nr. of uSeconds the LED will be off per pulse.
offTimePeriod = period - onTimePeriod; offTimePeriod = period - onTimePeriod;
} }
@@ -124,7 +124,7 @@ void IRsend::enableIROut(uint32_t freq, uint8_t duty) {
void IRsend::_delayMicroseconds(uint32_t usec) { void IRsend::_delayMicroseconds(uint32_t usec) {
// delayMicroseconds() is only accurate to 16383us. // delayMicroseconds() is only accurate to 16383us.
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
if (usec <= MAX_ACCURATE_USEC_DELAY) { if (usec <= kMaxAccurateUsecDelay) {
#ifndef UNIT_TEST #ifndef UNIT_TEST
delayMicroseconds(usec); delayMicroseconds(usec);
#endif #endif
@@ -146,9 +146,9 @@ void IRsend::_delayMicroseconds(uint32_t usec) {
// NOTE: Use this only if you know what you are doing as it may cause the WDT // NOTE: Use this only if you know what you are doing as it may cause the WDT
// to reset the ESP8266. // to reset the ESP8266.
void IRsend::_delayMicroseconds(uint32_t usec) { void IRsend::_delayMicroseconds(uint32_t usec) {
for (; usec > MAX_ACCURATE_USEC_DELAY; usec -= MAX_ACCURATE_USEC_DELAY) for (; usec > kMaxAccurateUsecDelay; usec -= kMaxAccurateUsecDelay)
#ifndef UNIT_TEST #ifndef UNIT_TEST
delayMicroseconds(MAX_ACCURATE_USEC_DELAY); delayMicroseconds(kMaxAccurateUsecDelay);
delayMicroseconds(static_cast<uint16_t>(usec)); delayMicroseconds(static_cast<uint16_t>(usec));
#endif // UNIT_TEST #endif // UNIT_TEST
} }
@@ -196,8 +196,8 @@ uint16_t IRsend::mark(uint16_t usec) {
if (elapsed + onTimePeriod >= usec) if (elapsed + onTimePeriod >= usec)
return counter; // LED is now off & we've passed our allotted time. return counter; // LED is now off & we've passed our allotted time.
// Wait for the lesser of the rest of the duty cycle, or the time remaining. // Wait for the lesser of the rest of the duty cycle, or the time remaining.
_delayMicroseconds(std::min(usec - elapsed - onTimePeriod, _delayMicroseconds(
(uint32_t) offTimePeriod)); std::min(usec - elapsed - onTimePeriod, (uint32_t)offTimePeriod));
elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time. elapsed = usecTimer.elapsed(); // Update & recache the actual elapsed time.
} }
return counter; return counter;
@@ -268,9 +268,9 @@ int8_t IRsend::calibrate(uint16_t hz) {
// data: The data to be transmitted. // data: The data to be transmitted.
// nbits: Nr. of bits of data to be sent. // nbits: Nr. of bits of data to be sent.
// MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order. // MSBfirst: Flag for bit transmission order. Defaults to MSB->LSB order.
void IRsend::sendData(uint16_t onemark, uint32_t onespace, void IRsend::sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark,
uint16_t zeromark, uint32_t zerospace, uint32_t zerospace, uint64_t data, uint16_t nbits,
uint64_t data, uint16_t nbits, bool MSBfirst) { bool MSBfirst) {
if (nbits == 0) // If we are asked to send nothing, just return. if (nbits == 0) // If we are asked to send nothing, just return.
return; return;
if (MSBfirst) { // Send the MSB first. if (MSBfirst) { // Send the MSB first.
@@ -376,10 +376,10 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
const uint16_t onemark, const uint32_t onespace, const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace, const uint16_t zeromark, const uint32_t zerospace,
const uint16_t footermark, const uint32_t gap, const uint16_t footermark, const uint32_t gap,
const uint32_t mesgtime, const uint32_t mesgtime, const uint64_t data,
const uint64_t data, const uint16_t nbits, const uint16_t nbits, const uint16_t frequency,
const uint16_t frequency, const bool MSBfirst, const bool MSBfirst, const uint16_t repeat,
const uint16_t repeat, const uint8_t dutycycle) { const uint8_t dutycycle) {
// Setup // Setup
enableIROut(frequency, dutycycle); enableIROut(frequency, dutycycle);
IRtimer usecs = IRtimer(); IRtimer usecs = IRtimer();
@@ -450,8 +450,8 @@ void IRsend::sendGeneric(const uint16_t headermark, const uint32_t headerspace,
// Data // Data
for (uint16_t i = 0; i < nbytes; i++) for (uint16_t i = 0; i < nbytes; i++)
sendData(onemark, onespace, zeromark, zerospace, sendData(onemark, onespace, zeromark, zerospace, *(dataptr + i), 8,
*(dataptr + i), 8, MSBfirst); MSBfirst);
// Footer // Footer
if (footermark) mark(footermark); if (footermark) mark(footermark);
@@ -492,61 +492,114 @@ void IRsend::sendRaw(uint16_t buf[], uint16_t len, uint16_t hz) {
void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) { void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
switch (type) { switch (type) {
#if SEND_NEC #if SEND_NEC
case NEC: sendNEC(data, nbits); break; case NEC:
sendNEC(data, nbits);
break;
#endif #endif
#if SEND_SONY #if SEND_SONY
case SONY: sendSony(data, nbits); break; case SONY:
sendSony(data, nbits);
break;
#endif #endif
#if SEND_RC5 #if SEND_RC5
case RC5: sendRC5(data, nbits); break; case RC5:
sendRC5(data, nbits);
break;
#endif #endif
#if SEND_RC6 #if SEND_RC6
case RC6: sendRC6(data, nbits); break; case RC6:
sendRC6(data, nbits);
break;
#endif #endif
#if SEND_DISH #if SEND_DISH
case DISH: sendDISH(data, nbits); break; case DISH:
sendDISH(data, nbits);
break;
#endif #endif
#if SEND_JVC #if SEND_JVC
case JVC: sendJVC(data, nbits); break; case JVC:
sendJVC(data, nbits);
break;
#endif #endif
#if SEND_SAMSUNG #if SEND_SAMSUNG
case SAMSUNG: sendSAMSUNG(data, nbits); break; case SAMSUNG:
sendSAMSUNG(data, nbits);
break;
#endif #endif
#if SEND_LG #if SEND_LG
case LG: sendLG(data, nbits); break; case LG:
sendLG(data, nbits);
break;
#endif
#if SEND_LG
case LG2:
sendLG2(data, nbits);
break;
#endif #endif
#if SEND_WHYNTER #if SEND_WHYNTER
case WHYNTER: sendWhynter(data, nbits); break; case WHYNTER:
sendWhynter(data, nbits);
break;
#endif #endif
#if SEND_COOLIX #if SEND_COOLIX
case COOLIX: sendCOOLIX(data, nbits); break; case COOLIX:
sendCOOLIX(data, nbits);
break;
#endif #endif
#if SEND_DENON #if SEND_DENON
case DENON: sendDenon(data, nbits); break; case DENON:
sendDenon(data, nbits);
break;
#endif #endif
#if SEND_SHERWOOD #if SEND_SHERWOOD
case SHERWOOD: sendSherwood(data, nbits); break; case SHERWOOD:
sendSherwood(data, nbits);
break;
#endif #endif
#if SEND_RCMM #if SEND_RCMM
case RCMM: sendRCMM(data, nbits); break; case RCMM:
sendRCMM(data, nbits);
break;
#endif #endif
#if SEND_MITSUBISHI #if SEND_MITSUBISHI
case MITSUBISHI: sendMitsubishi(data, nbits); break; case MITSUBISHI:
sendMitsubishi(data, nbits);
break;
#endif #endif
#if SEND_MITSUBISHI2 #if SEND_MITSUBISHI2
case MITSUBISHI2: sendMitsubishi2(data, nbits); break; case MITSUBISHI2:
sendMitsubishi2(data, nbits);
break;
#endif #endif
#if SEND_SHARP #if SEND_SHARP
case SHARP: sendSharpRaw(data, nbits); break; case SHARP:
sendSharpRaw(data, nbits);
break;
#endif #endif
#if SEND_AIWA_RC_T501 #if SEND_AIWA_RC_T501
case AIWA_RC_T501: sendAiwaRCT501(data, nbits); break; case AIWA_RC_T501:
sendAiwaRCT501(data, nbits);
break;
#endif #endif
#if SEND_MIDEA #if SEND_MIDEA
case MIDEA: sendMidea(data, nbits); break; case MIDEA:
sendMidea(data, nbits);
break;
#endif #endif
#if SEND_GICABLE #if SEND_GICABLE
case GICABLE: sendGICable(data, nbits); break; case GICABLE:
sendGICable(data, nbits);
break;
#endif
#if SEND_PIONEER
case PIONEER:
sendPioneer(data, nbits);
break;
#endif
#if SEND_VESTEL_AC
case VESTEL_AC:
sendVestelAC(data, nbits);
break;
#endif #endif
} }
} }
+139 -90
View File
@@ -22,12 +22,12 @@
// Offset (in microseconds) to use in Period time calculations to account for // Offset (in microseconds) to use in Period time calculations to account for
// code excution time in producing the software PWM signal. // code excution time in producing the software PWM signal.
// Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core // Value was calculated on Wemos D1 mini using v2.4.1 with v2.4.0 ESP core
#define PERIOD_OFFSET -5 const int8_t kPeriodOffset = -5;
#define DUTY_DEFAULT 50 const uint8_t kDutyDefault = 50; // Percentage
#define DUTY_MAX 100 // Percentage const uint8_t kDutyMax = 100; // Percentage
// delayMicroseconds() is only accurate to 16383us. // delayMicroseconds() is only accurate to 16383us.
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds // Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
#define MAX_ACCURATE_USEC_DELAY 16383U const uint16_t kMaxAccurateUsecDelay = 16383;
// Classes // Classes
class IRsend { class IRsend {
@@ -35,7 +35,7 @@ class IRsend {
explicit IRsend(uint16_t IRsendPin, bool inverted = false, explicit IRsend(uint16_t IRsendPin, bool inverted = false,
bool use_modulation = true); bool use_modulation = true);
void begin(); void begin();
void enableIROut(uint32_t freq, uint8_t duty = DUTY_DEFAULT); void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault);
VIRTUAL void _delayMicroseconds(uint32_t usec); VIRTUAL void _delayMicroseconds(uint32_t usec);
VIRTUAL uint16_t mark(uint16_t usec); VIRTUAL uint16_t mark(uint16_t usec);
VIRTUAL void space(uint32_t usec); VIRTUAL void space(uint32_t usec);
@@ -55,10 +55,10 @@ class IRsend {
const uint16_t onemark, const uint32_t onespace, const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace, const uint16_t zeromark, const uint32_t zerospace,
const uint16_t footermark, const uint32_t gap, const uint16_t footermark, const uint32_t gap,
const uint32_t mesgtime, const uint32_t mesgtime, const uint64_t data,
const uint64_t data, const uint16_t nbits, const uint16_t nbits, const uint16_t frequency,
const uint16_t frequency, const bool MSBfirst, const bool MSBfirst, const uint16_t repeat,
const uint16_t repeat, const uint8_t dutycycle); const uint8_t dutycycle);
void sendGeneric(const uint16_t headermark, const uint32_t headerspace, void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
const uint16_t onemark, const uint32_t onespace, const uint16_t onemark, const uint32_t onespace,
const uint16_t zeromark, const uint32_t zerospace, const uint16_t zeromark, const uint32_t zerospace,
@@ -68,7 +68,8 @@ class IRsend {
const uint16_t repeat, const uint8_t dutycycle); const uint16_t repeat, const uint8_t dutycycle);
void send(uint16_t type, uint64_t data, uint16_t nbits); void send(uint16_t type, uint64_t data, uint16_t nbits);
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO) #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
void sendNEC(uint64_t data, uint16_t nbits = NEC_BITS, uint16_t repeat = 0); void sendNEC(uint64_t data, uint16_t nbits = kNECBits,
uint16_t repeat = kNoRepeat);
uint32_t encodeNEC(uint16_t address, uint16_t command); uint32_t encodeNEC(uint16_t address, uint16_t command);
#endif #endif
#if SEND_SONY #if SEND_SONY
@@ -77,22 +78,30 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
// Legacy use of this procedure was to only send a single code so call it with // Legacy use of this procedure was to only send a single code so call it with
// repeat=0 for backward compatibility. As of v2.0 it defaults to sending // repeat=0 for backward compatibility. As of v2.0 it defaults to sending
// a Sony command that will be accepted be a device. // a Sony command that will be accepted be a device.
void sendSony(uint64_t data, uint16_t nbits = SONY_20_BITS, void sendSony(uint64_t data, uint16_t nbits = kSony20Bits,
uint16_t repeat = SONY_MIN_REPEAT); uint16_t repeat = kSonyMinRepeat);
uint32_t encodeSony(uint16_t nbits, uint16_t command, uint16_t address, uint32_t encodeSony(uint16_t nbits, uint16_t command, uint16_t address,
uint16_t extended = 0); uint16_t extended = 0);
#endif #endif
#if SEND_SHERWOOD #if SEND_SHERWOOD
void sendSherwood(uint64_t data, uint16_t nbits = SHERWOOD_BITS, void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits,
uint16_t repeat = SHERWOOD_MIN_REPEAT); uint16_t repeat = kSherwoodMinRepeat);
#endif #endif
#if SEND_SAMSUNG #if SEND_SAMSUNG
void sendSAMSUNG(uint64_t data, uint16_t nbits = SAMSUNG_BITS, void sendSAMSUNG(uint64_t data, uint16_t nbits = kSamsungBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command); uint32_t encodeSAMSUNG(uint8_t customer, uint8_t command);
#endif #endif
#if SEND_SAMSUNG_AC
void sendSamsungAC(unsigned char data[],
uint16_t nbytes = kSamsungAcStateLength,
uint16_t repeat = kSamsungAcDefaultRepeat);
#endif
#if SEND_LG #if SEND_LG
void sendLG(uint64_t data, uint16_t nbits = LG_BITS, uint16_t repeat = 0); void sendLG(uint64_t data, uint16_t nbits = kLgBits,
uint16_t repeat = kNoRepeat);
void sendLG2(uint64_t data, uint16_t nbits = kLgBits,
uint16_t repeat = kNoRepeat);
uint32_t encodeLG(uint16_t address, uint16_t command); uint32_t encodeLG(uint16_t address, uint16_t command);
#endif #endif
#if (SEND_SHARP || SEND_DENON) #if (SEND_SHARP || SEND_DENON)
@@ -100,41 +109,44 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
uint16_t expansion = 1, uint16_t check = 0, uint16_t expansion = 1, uint16_t check = 0,
bool MSBfirst = false); bool MSBfirst = false);
void sendSharp(uint16_t address, uint16_t command, void sendSharp(uint16_t address, uint16_t command,
uint16_t nbits = SHARP_BITS, uint16_t repeat = 0); uint16_t nbits = kSharpBits, uint16_t repeat = kNoRepeat);
void sendSharpRaw(uint64_t data, uint16_t nbits = SHARP_BITS, void sendSharpRaw(uint64_t data, uint16_t nbits = kSharpBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_JVC #if SEND_JVC
void sendJVC(uint64_t data, uint16_t nbits = JVC_BITS, uint16_t repeat = 0); void sendJVC(uint64_t data, uint16_t nbits = kJvcBits,
uint16_t repeat = kNoRepeat);
uint16_t encodeJVC(uint8_t address, uint8_t command); uint16_t encodeJVC(uint8_t address, uint8_t command);
#endif #endif
#if SEND_DENON #if SEND_DENON
void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS, void sendDenon(uint64_t data, uint16_t nbits = DENON_BITS,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_SANYO #if SEND_SANYO
uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command); uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command);
void sendSanyoLC7461(uint64_t data, uint16_t nbits = SANYO_LC7461_BITS, void sendSanyoLC7461(uint64_t data, uint16_t nbits = kSanyoLC7461Bits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_DISH #if SEND_DISH
// sendDISH() should typically be called with repeat=3 as DISH devices // sendDISH() should typically be called with repeat=3 as DISH devices
// expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes) // expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes)
// Legacy use of this procedure was only to send a single code // Legacy use of this procedure was only to send a single code
// so use repeat=0 for backward compatibility. // so use repeat=0 for backward compatibility.
void sendDISH(uint64_t data, uint16_t nbits = DISH_BITS, void sendDISH(uint64_t data, uint16_t nbits = kDishBits,
uint16_t repeat = DISH_MIN_REPEAT); uint16_t repeat = kDishMinRepeat);
#endif #endif
#if (SEND_PANASONIC || SEND_DENON) #if (SEND_PANASONIC || SEND_DENON)
void sendPanasonic64(uint64_t data, uint16_t nbits = PANASONIC_BITS, void sendPanasonic64(uint64_t data, uint16_t nbits = kPanasonicBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
void sendPanasonic(uint16_t address, uint32_t data, void sendPanasonic(uint16_t address, uint32_t data,
uint16_t nbits = PANASONIC_BITS, uint16_t repeat = 0); uint16_t nbits = kPanasonicBits,
uint16_t repeat = kNoRepeat);
uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device, uint64_t encodePanasonic(uint16_t manufacturer, uint8_t device,
uint8_t subdevice, uint8_t function); uint8_t subdevice, uint8_t function);
#endif #endif
#if SEND_RC5 #if SEND_RC5
void sendRC5(uint64_t data, uint16_t nbits = RC5X_BITS, uint16_t repeat = 0); void sendRC5(uint64_t data, uint16_t nbits = kRC5XBits,
uint16_t repeat = kNoRepeat);
uint16_t encodeRC5(uint8_t address, uint8_t command, uint16_t encodeRC5(uint8_t address, uint8_t command,
bool key_released = false); bool key_released = false);
uint16_t encodeRC5X(uint8_t address, uint8_t command, uint16_t encodeRC5X(uint8_t address, uint8_t command,
@@ -142,126 +154,163 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
uint64_t toggleRC5(uint64_t data); uint64_t toggleRC5(uint64_t data);
#endif #endif
#if SEND_RC6 #if SEND_RC6
void sendRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS, void sendRC6(uint64_t data, uint16_t nbits = kRC6Mode0Bits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
uint64_t encodeRC6(uint32_t address, uint8_t command, uint64_t encodeRC6(uint32_t address, uint8_t command,
uint16_t mode = RC6_MODE0_BITS); uint16_t mode = kRC6Mode0Bits);
uint64_t toggleRC6(uint64_t data, uint16_t nbits = RC6_MODE0_BITS); uint64_t toggleRC6(uint64_t data, uint16_t nbits = kRC6Mode0Bits);
#endif #endif
#if SEND_RCMM #if SEND_RCMM
void sendRCMM(uint64_t data, uint16_t nbits = RCMM_BITS, uint16_t repeat = 0); void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits,
uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_COOLIX #if SEND_COOLIX
void sendCOOLIX(uint64_t data, uint16_t nbits = COOLIX_BITS, void sendCOOLIX(uint64_t data, uint16_t nbits = kCoolixBits,
uint16_t repeat = 0); uint16_t repeat = kCoolixDefaultRepeat);
#endif #endif
#if SEND_WHYNTER #if SEND_WHYNTER
void sendWhynter(uint64_t data, uint16_t nbits = WHYNTER_BITS, void sendWhynter(uint64_t data, uint16_t nbits = kWhynterBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_MITSUBISHI #if SEND_MITSUBISHI
void sendMitsubishi(uint64_t data, uint16_t nbits = MITSUBISHI_BITS, void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits,
uint16_t repeat = MITSUBISHI_MIN_REPEAT); uint16_t repeat = kMitsubishiMinRepeat);
#endif #endif
#if SEND_MITSUBISHI2 #if SEND_MITSUBISHI2
void sendMitsubishi2(uint64_t data, uint16_t nbits = MITSUBISHI_BITS, void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits,
uint16_t repeat = MITSUBISHI_MIN_REPEAT); uint16_t repeat = kMitsubishiMinRepeat);
#endif #endif
#if SEND_MITSUBISHI_AC #if SEND_MITSUBISHI_AC
void sendMitsubishiAC(unsigned char data[], void sendMitsubishiAC(unsigned char data[],
uint16_t nbytes = MITSUBISHI_AC_STATE_LENGTH, uint16_t nbytes = kMitsubishiACStateLength,
uint16_t repeat = MITSUBISHI_AC_MIN_REPEAT); uint16_t repeat = kMitsubishiACMinRepeat);
#endif #endif
#if SEND_FUJITSU_AC #if SEND_FUJITSU_AC
void sendFujitsuAC(unsigned char data[], void sendFujitsuAC(unsigned char data[], uint16_t nbytes,
uint16_t nbytes, uint16_t repeat = kFujitsuAcMinRepeat);
uint16_t repeat = FUJITSU_AC_MIN_REPEAT);
#endif #endif
#if SEND_GLOBALCACHE #if SEND_GLOBALCACHE
void sendGC(uint16_t buf[], uint16_t len); void sendGC(uint16_t buf[], uint16_t len);
#endif #endif
#if SEND_KELVINATOR #if SEND_KELVINATOR
void sendKelvinator(unsigned char data[], void sendKelvinator(unsigned char data[],
uint16_t nbytes = KELVINATOR_STATE_LENGTH, uint16_t nbytes = kKelvinatorStateLength,
uint16_t repeat = 0); uint16_t repeat = kKelvinatorDefaultRepeat);
#endif #endif
#if SEND_DAIKIN #if SEND_DAIKIN
void sendDaikin(unsigned char data[], void sendDaikin(unsigned char data[], uint16_t nbytes = kDaikinStateLength,
uint16_t nbytes = DAIKIN_COMMAND_LENGTH, uint16_t repeat = kDaikinDefaultRepeat);
uint16_t repeat = 0); #endif
void sendDaikinGapHeader(); #if SEND_DAIKIN2
void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength,
uint16_t repeat = kDaikin2DefaultRepeat);
#endif #endif
#if SEND_AIWA_RC_T501 #if SEND_AIWA_RC_T501
void sendAiwaRCT501(uint64_t data, uint16_t nbits = AIWA_RC_T501_BITS, void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits,
uint16_t repeat = AIWA_RC_T501_MIN_REPEAT); uint16_t repeat = kAiwaRcT501MinRepeats);
#endif #endif
#if SEND_GREE #if SEND_GREE
void sendGree(uint64_t data, uint16_t nbits = GREE_BITS, uint16_t repeat = 0); void sendGree(uint64_t data, uint16_t nbits = kGreeBits,
void sendGree(uint8_t data[], uint16_t nbytes = GREE_STATE_LENGTH, uint16_t repeat = kGreeDefaultRepeat);
uint16_t repeat = 0); void sendGree(uint8_t data[], uint16_t nbytes = kGreeStateLength,
uint16_t repeat = kGreeDefaultRepeat);
#endif #endif
#if SEND_PRONTO #if SEND_PRONTO
void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = 0); void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_ARGO #if SEND_ARGO
void sendArgo(unsigned char data[], void sendArgo(unsigned char data[], uint16_t nbytes = kArgoStateLength,
uint16_t nbytes = ARGO_COMMAND_LENGTH, uint16_t repeat = kArgoDefaultRepeat);
uint16_t repeat = 0);
#endif #endif
#if SEND_TROTEC #if SEND_TROTEC
void sendTrotec(unsigned char data[], void sendTrotec(unsigned char data[], uint16_t nbytes = kTrotecStateLength,
uint16_t nbytes = TROTEC_COMMAND_LENGTH, uint16_t repeat = kTrotecDefaultRepeat);
uint16_t repeat = 0);
#endif #endif
#if SEND_NIKAI #if SEND_NIKAI
void sendNikai(uint64_t data, uint16_t nbits = NIKAI_BITS, void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_TOSHIBA_AC #if SEND_TOSHIBA_AC
void sendToshibaAC(unsigned char data[], void sendToshibaAC(unsigned char data[],
uint16_t nbytes = TOSHIBA_AC_STATE_LENGTH, uint16_t nbytes = kToshibaACStateLength,
uint16_t repeat = TOSHIBA_AC_MIN_REPEAT); uint16_t repeat = kToshibaACMinRepeat);
#endif #endif
#if SEND_MIDEA #if SEND_MIDEA
void sendMidea(uint64_t data, uint16_t nbits = MIDEA_BITS, void sendMidea(uint64_t data, uint16_t nbits = kMideaBits,
uint16_t repeat = MIDEA_MIN_REPEAT); uint16_t repeat = kMideaMinRepeat);
#endif #endif
#if SEND_MAGIQUEST #if SEND_MAGIQUEST
void sendMagiQuest(uint64_t data, uint16_t nbits = MAGIQUEST_BITS, void sendMagiQuest(uint64_t data, uint16_t nbits = kMagiquestBits,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
uint64_t encodeMagiQuest(uint32_t wand_id, uint16_t magnitude); uint64_t encodeMagiQuest(uint32_t wand_id, uint16_t magnitude);
#endif #endif
#if SEND_LASERTAG #if SEND_LASERTAG
void sendLasertag(uint64_t data, uint16_t nbits = LASERTAG_BITS, void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits,
uint16_t repeat = LASERTAG_MIN_REPEAT); uint16_t repeat = kLasertagMinRepeat);
#endif #endif
#if SEND_CARRIER_AC #if SEND_CARRIER_AC
void sendCarrierAC(uint64_t data, uint16_t nbits = CARRIER_AC_BITS, void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits,
uint16_t repeat = CARRIER_AC_MIN_REPEAT); uint16_t repeat = kCarrierAcMinRepeat);
#endif #endif
#if SEND_HAIER_AC #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
void sendHaierAC(unsigned char data[], void sendHaierAC(unsigned char data[], uint16_t nbytes = kHaierACStateLength,
uint16_t nbytes = HAIER_AC_STATE_LENGTH, uint16_t repeat = kHaierAcDefaultRepeat);
uint16_t repeat = 0); #endif
#if SEND_HAIER_AC_YRW02
void sendHaierACYRW02(unsigned char data[],
uint16_t nbytes = kHaierACYRW02StateLength,
uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif #endif
#if SEND_HITACHI_AC #if SEND_HITACHI_AC
void sendHitachiAC(unsigned char data[], void sendHitachiAC(unsigned char data[],
uint16_t nbytes = HITACHI_AC_STATE_LENGTH, uint16_t nbytes = kHitachiAcStateLength,
uint16_t repeat = 0); uint16_t repeat = kHitachiAcDefaultRepeat);
#endif #endif
#if SEND_HITACHI_AC1 #if SEND_HITACHI_AC1
void sendHitachiAC1(unsigned char data[], void sendHitachiAC1(unsigned char data[],
uint16_t nbytes = HITACHI_AC1_STATE_LENGTH, uint16_t nbytes = kHitachiAc1StateLength,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_HITACHI_AC2 #if SEND_HITACHI_AC2
void sendHitachiAC2(unsigned char data[], void sendHitachiAC2(unsigned char data[],
uint16_t nbytes = HITACHI_AC2_STATE_LENGTH, uint16_t nbytes = kHitachiAc2StateLength,
uint16_t repeat = 0); uint16_t repeat = kNoRepeat);
#endif #endif
#if SEND_GICABLE #if SEND_GICABLE
void sendGICable(uint64_t data, uint16_t nbits = GICABLE_BITS, void sendGICable(uint64_t data, uint16_t nbits = kGicableBits,
uint16_t repeat = GICABLE_MIN_REPEAT); uint16_t repeat = kGicableMinRepeat);
#endif
#if SEND_WHIRLPOOL_AC
void sendWhirlpoolAC(unsigned char data[],
uint16_t nbytes = kWhirlpoolAcStateLength,
uint16_t repeat = kWhirlpoolAcDefaultRepeat);
#endif
#if SEND_LUTRON
void sendLutron(uint64_t data, uint16_t nbits = kLutronBits,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_ELECTRA_AC
void sendElectraAC(unsigned char data[],
uint16_t nbytes = kElectraAcStateLength,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_PANASONIC_AC
void sendPanasonicAC(unsigned char data[],
uint16_t nbytes = kPanasonicAcStateLength,
uint16_t repeat = kPanasonicAcDefaultRepeat);
#endif
#if SEND_PIONEER
void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits,
const uint16_t repeat = kNoRepeat);
uint64_t encodePioneer(uint16_t address, uint16_t command);
#endif
#if SEND_MWM
void sendMWM(unsigned char data[], uint16_t nbytes,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_VESTEL_AC
void sendVestelAC(const uint64_t data, const uint16_t nbits = kVestelACBits,
const uint16_t repeat = kNoRepeat);
#endif #endif
protected: protected:
+2 -6
View File
@@ -13,9 +13,7 @@ extern uint32_t _IRtimer_unittest_now;
// This class performs a simple time in useconds since instantiated. // This class performs a simple time in useconds since instantiated.
// Handles when the system timer wraps around (once). // Handles when the system timer wraps around (once).
IRtimer::IRtimer() { IRtimer::IRtimer() { reset(); }
reset();
}
void IRtimer::reset() { void IRtimer::reset() {
#ifndef UNIT_TEST #ifndef UNIT_TEST
@@ -39,7 +37,5 @@ uint32_t IRtimer::elapsed() {
// Only used in unit testing. // Only used in unit testing.
#ifdef UNIT_TEST #ifdef UNIT_TEST
void IRtimer::add(uint32_t usecs) { void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; }
_IRtimer_unittest_now += usecs;
}
#endif // UNIT_TEST #endif // UNIT_TEST
+262 -75
View File
@@ -21,8 +21,7 @@
// Returns: // Returns:
// The reversed bit pattern. // The reversed bit pattern.
uint64_t reverseBits(uint64_t input, uint16_t nbits) { uint64_t reverseBits(uint64_t input, uint16_t nbits) {
if (nbits <= 1) if (nbits <= 1) return input; // Reversing <= 1 bits makes no change at all.
return input; // Reversing <= 1 bits makes no change at all.
// Cap the nr. of bits to rotate to the max nr. of bits in the input. // Cap the nr. of bits to rotate to the max nr. of bits in the input.
nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8)); nbits = std::min(nbits, (uint16_t)(sizeof(input) * 8));
uint64_t output = 0; uint64_t output = 0;
@@ -92,57 +91,179 @@ void serialPrintUint64(uint64_t input, uint8_t base) {
String typeToString(const decode_type_t protocol, const bool isRepeat) { String typeToString(const decode_type_t protocol, const bool isRepeat) {
String result = ""; String result = "";
#else #else
std::string typeToString(const decode_type_t protocol, std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
const bool isRepeat) {
std::string result = ""; std::string result = "";
#endif #endif
switch (protocol) { switch (protocol) {
default: default:
case UNKNOWN: result = "UNKNOWN"; break; case UNKNOWN:
case UNUSED: result = "UNUSED"; break; result = "UNKNOWN";
case AIWA_RC_T501: result = "AIWA_RC_T501"; break; break;
case ARGO: result = "ARGO"; break; case UNUSED:
case CARRIER_AC: result = "CARRIER_AC"; break; result = "UNUSED";
case COOLIX: result = "COOLIX"; break; break;
case DAIKIN: result = "DAIKIN"; break; case AIWA_RC_T501:
case DENON: result = "DENON"; break; result = "AIWA_RC_T501";
case DISH: result = "DISH"; break; break;
case FUJITSU_AC: result = "FUJITSU_AC"; break; case ARGO:
case GICABLE: result = "GICABLE"; break; result = "ARGO";
case GLOBALCACHE: result = "GLOBALCACHE"; break; break;
case GREE: result = "GREE"; break; case CARRIER_AC:
case HAIER_AC: result = "HAIER_AC"; break; result = "CARRIER_AC";
case HITACHI_AC: result = "HITACHI_AC"; break; break;
case HITACHI_AC1: result = "HITACHI_AC1"; break; case COOLIX:
case HITACHI_AC2: result = "HITACHI_AC2"; break; result = "COOLIX";
case JVC: result = "JVC"; break; break;
case KELVINATOR: result = "KELVINATOR"; break; case DAIKIN:
case LG: result = "LG"; break; result = "DAIKIN";
case LASERTAG: result = "LASERTAG"; break; break;
case MAGIQUEST: result = "MAGIQUEST"; break; case DAIKIN2:
case MIDEA: result = "MIDEA"; break; result = "DAIKIN2";
case MITSUBISHI: result = "MITSUBISHI"; break; break;
case MITSUBISHI2: result = "MITSUBISHI2"; break; case DENON:
case MITSUBISHI_AC: result = "MITSUBISHI_AC"; break; result = "DENON";
case NEC: result = "NEC"; break; break;
case NEC_LIKE: result = "NEC (non-strict)"; break; case DISH:
case NIKAI: result = "NIKAI"; break; result = "DISH";
case PANASONIC: result = "PANASONIC"; break; break;
case PRONTO: result = "PRONTO"; break; case ELECTRA_AC:
case RAW: result = "RAW"; break; result = "ELECTRA_AC";
case RC5: result = "RC5"; break; break;
case RC5X: result = "RC5X"; break; case FUJITSU_AC:
case RC6: result = "RC6"; break; result = "FUJITSU_AC";
case RCMM: result = "RCMM"; break; break;
case SAMSUNG: result = "SAMSUNG"; break; case GICABLE:
case SANYO: result = "SANYO"; break; result = "GICABLE";
case SANYO_LC7461: result = "SANYO_LC7461"; break; break;
case SHARP: result = "SHARP"; break; case GLOBALCACHE:
case SHERWOOD: result = "SHERWOOD"; break; result = "GLOBALCACHE";
case SONY: result = "SONY"; break; break;
case TOSHIBA_AC: result = "TOSHIBA_AC"; break; case GREE:
case TROTEC: result = "TROTEC"; break; result = "GREE";
case WHYNTER: result = "WHYNTER"; break; break;
case HAIER_AC:
result = "HAIER_AC";
break;
case HAIER_AC_YRW02:
result = "HAIER_AC_YRW02";
break;
case HITACHI_AC:
result = "HITACHI_AC";
break;
case HITACHI_AC1:
result = "HITACHI_AC1";
break;
case HITACHI_AC2:
result = "HITACHI_AC2";
break;
case JVC:
result = "JVC";
break;
case KELVINATOR:
result = "KELVINATOR";
break;
case LG:
result = "LG";
break;
case LG2:
result = "LG2";
break;
case LASERTAG:
result = "LASERTAG";
break;
case LUTRON:
result = "LUTRON";
break;
case MAGIQUEST:
result = "MAGIQUEST";
break;
case MIDEA:
result = "MIDEA";
break;
case MITSUBISHI:
result = "MITSUBISHI";
break;
case MITSUBISHI2:
result = "MITSUBISHI2";
break;
case MITSUBISHI_AC:
result = "MITSUBISHI_AC";
break;
case MWM:
result = "MWM";
break;
case NEC:
result = "NEC";
break;
case NEC_LIKE:
result = "NEC (non-strict)";
break;
case NIKAI:
result = "NIKAI";
break;
case PANASONIC:
result = "PANASONIC";
break;
case PANASONIC_AC:
result = "PANASONIC_AC";
break;
case PIONEER:
result = "PIONEER";
break;
case PRONTO:
result = "PRONTO";
break;
case RAW:
result = "RAW";
break;
case RC5:
result = "RC5";
break;
case RC5X:
result = "RC5X";
break;
case RC6:
result = "RC6";
break;
case RCMM:
result = "RCMM";
break;
case SAMSUNG:
result = "SAMSUNG";
break;
case SAMSUNG_AC:
result = "SAMSUNG_AC";
break;
case SANYO:
result = "SANYO";
break;
case SANYO_LC7461:
result = "SANYO_LC7461";
break;
case SHARP:
result = "SHARP";
break;
case SHERWOOD:
result = "SHERWOOD";
break;
case SONY:
result = "SONY";
break;
case TOSHIBA_AC:
result = "TOSHIBA_AC";
break;
case TROTEC:
result = "TROTEC";
break;
case VESTEL_AC:
result = "VESTEL_AC";
break;
case WHIRLPOOL_AC:
result = "WHIRLPOOL_AC";
break;
case WHYNTER:
result = "WHYNTER";
break;
} }
if (isRepeat) result += " (Repeat)"; if (isRepeat) result += " (Repeat)";
return result; return result;
@@ -152,15 +273,22 @@ std::string typeToString(const decode_type_t protocol,
bool hasACState(const decode_type_t protocol) { bool hasACState(const decode_type_t protocol) {
switch (protocol) { switch (protocol) {
case DAIKIN: case DAIKIN:
case DAIKIN2:
case ELECTRA_AC:
case FUJITSU_AC: case FUJITSU_AC:
case GREE: case GREE:
case HAIER_AC: case HAIER_AC:
case HAIER_AC_YRW02:
case HITACHI_AC: case HITACHI_AC:
case HITACHI_AC1: case HITACHI_AC1:
case HITACHI_AC2: case HITACHI_AC2:
case KELVINATOR: case KELVINATOR:
case MITSUBISHI_AC: case MITSUBISHI_AC:
case MWM:
case PANASONIC_AC:
case SAMSUNG_AC:
case TOSHIBA_AC: case TOSHIBA_AC:
case WHIRLPOOL_AC:
return true; return true;
default: default:
return false; return false;
@@ -176,7 +304,7 @@ bool hasACState(const decode_type_t protocol) {
uint16_t getCorrectedRawLength(const decode_results *results) { uint16_t getCorrectedRawLength(const decode_results *results) {
uint16_t extended_length = results->rawlen - 1; uint16_t extended_length = results->rawlen - 1;
for (uint16_t i = 0; i < results->rawlen - 1; i++) { for (uint16_t i = 0; i < results->rawlen - 1; i++) {
uint32_t usecs = results->rawbuf[i] * RAWTICK; uint32_t usecs = results->rawbuf[i] * kRawTick;
// Add two extra entries for multiple larger than UINT16_MAX it is. // Add two extra entries for multiple larger than UINT16_MAX it is.
extended_length += (usecs / (UINT16_MAX + 1)) * 2; extended_length += (usecs / (UINT16_MAX + 1)) * 2;
} }
@@ -202,8 +330,7 @@ std::string resultToSourceCode(const decode_results *results) {
// Dump data // Dump data
for (uint16_t i = 1; i < results->rawlen; i++) { for (uint16_t i = 1; i < results->rawlen; i++) {
uint32_t usecs; uint32_t usecs;
for (usecs = results->rawbuf[i] * RAWTICK; for (usecs = results->rawbuf[i] * kRawTick; usecs > UINT16_MAX;
usecs > UINT16_MAX;
usecs -= UINT16_MAX) { usecs -= UINT16_MAX) {
output += uint64ToString(UINT16_MAX); output += uint64ToString(UINT16_MAX);
if (i % 2) if (i % 2)
@@ -237,8 +364,7 @@ std::string resultToSourceCode(const decode_results *results) {
output += "0x"; output += "0x";
if (results->state[i] < 0x10) output += "0"; if (results->state[i] < 0x10) output += "0";
output += uint64ToString(results->state[i], 16); output += uint64ToString(results->state[i], 16);
if (i < nbytes - 1) if (i < nbytes - 1) output += ", ";
output += ", ";
} }
output += "};\n"; output += "};\n";
#endif // DECODE_AC #endif // DECODE_AC
@@ -254,8 +380,8 @@ std::string resultToSourceCode(const decode_results *results) {
uint64ToString(results->command, 16) + ";\n"; uint64ToString(results->command, 16) + ";\n";
} }
// Most protocols have data // Most protocols have data
output += "uint64_t data = 0x" + uint64ToString(results->value, 16) + output +=
";\n"; "uint64_t data = 0x" + uint64ToString(results->value, 16) + ";\n";
} }
} }
return output; return output;
@@ -279,19 +405,39 @@ std::string resultToTimingInfo(const decode_results *results) {
output += "-"; // even output += "-"; // even
else else
output += " +"; // odd output += " +"; // odd
value = uint64ToString(results->rawbuf[i] * RAWTICK); value = uint64ToString(results->rawbuf[i] * kRawTick);
// Space pad the value till it is at least 6 chars long. // Space pad the value till it is at least 6 chars long.
while (value.length() < 6) while (value.length() < 6) value = " " + value;
value = " " + value;
output += value; output += value;
if (i < results->rawlen - 1) if (i < results->rawlen - 1) output += ", "; // ',' not needed for last one
output += ", "; // ',' not needed for last one
if (!(i % 8)) output += "\n"; // Newline every 8 entries. if (!(i % 8)) output += "\n"; // Newline every 8 entries.
} }
output += "\n"; output += "\n";
return output; return output;
} }
// Convert the decode_results structure's value/state to simple hexadecimal.
//
#ifdef ARDUINO
String resultToHexidecimal(const decode_results *result) {
String output = "";
#else
std::string resultToHexidecimal(const decode_results *result) {
std::string output = "";
#endif
if (hasACState(result->decode_type)) {
#if DECODE_AC
for (uint16_t i = 0; result->bits > i * 8; i++) {
if (result->state[i] < 0x10) output += "0"; // Zero pad
output += uint64ToString(result->state[i], 16);
}
#endif // DECODE_AC
} else {
output += uint64ToString(result->value, 16);
}
return output;
}
// Dump out the decode_results structure. // Dump out the decode_results structure.
// //
#ifdef ARDUINO #ifdef ARDUINO
@@ -302,21 +448,13 @@ std::string resultToHumanReadableBasic(const decode_results *results) {
std::string output = ""; std::string output = "";
#endif #endif
// Show Encoding standard // Show Encoding standard
output += "Encoding : " + output +=
typeToString(results->decode_type, results->repeat) + "\n"; "Encoding : " + typeToString(results->decode_type, results->repeat) +
"\n";
// Show Code & length // Show Code & length
output += "Code : "; output += "Code : ";
if (hasACState(results->decode_type)) { output += resultToHexidecimal(results);
#if DECODE_AC
for (uint16_t i = 0; results->bits > i * 8; i++) {
if (results->state[i] < 0x10) output += "0"; // Zero pad
output += uint64ToString(results->state[i], 16);
}
#endif // DECODE_AC
} else {
output += uint64ToString(results->value, 16);
}
output += " (" + uint64ToString(results->bits) + " bits)\n"; output += " (" + uint64ToString(results->bits) + " bits)\n";
return output; return output;
} }
@@ -324,11 +462,60 @@ std::string resultToHumanReadableBasic(const decode_results *results) {
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) { uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
uint8_t checksum = init; uint8_t checksum = init;
uint8_t *ptr; uint8_t *ptr;
for (ptr = start; ptr - start < length; ptr++) for (ptr = start; ptr - start < length; ptr++) checksum += *ptr;
checksum += *ptr;
return checksum; return checksum;
} }
uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init) {
uint8_t checksum = init;
uint8_t *ptr;
for (ptr = start; ptr - start < length; ptr++) checksum ^= *ptr;
return checksum;
}
// Count the number of bits of a certain type.
// Args:
// start: Ptr to the start of data to count bits in.
// length: How many bytes to count.
// ones: Count the binary 1 bits. False for counting the 0 bits.
// init: Start the counting from this value.
// Returns:
// Nr. of bits found.
uint16_t countBits(const uint8_t *start, const uint16_t length, const bool ones,
const uint16_t init) {
uint16_t count = init;
for (uint16_t offset = 0; offset < length; offset++)
for (uint8_t currentbyte = *(start + offset);
currentbyte;
currentbyte >>= 1)
if (currentbyte & 1) count++;
if (ones || length == 0)
return count;
else
return (length * 8) - count;
}
// Count the number of bits of a certain type.
// Args:
// data: The value you want bits counted for, starting from the LSB.
// length: How many bits to count.
// ones: Count the binary 1 bits. False for counting the 0 bits.
// init: Start the counting from this value.
// Returns:
// Nr. of bits found.
uint16_t countBits(const uint64_t data, const uint8_t length, const bool ones,
const uint16_t init) {
uint16_t count = init;
uint8_t bitsSoFar = length;
for (uint64_t remainder = data; remainder && bitsSoFar;
remainder >>= 1, bitsSoFar--)
if (remainder & 1) count++;
if (ones || length == 0)
return count;
else
return length - count;
}
uint64_t invertBits(const uint64_t data, const uint16_t nbits) { uint64_t invertBits(const uint64_t data, const uint16_t nbits) {
// No change if we are asked to invert no bits. // No change if we are asked to invert no bits.
if (nbits == 0) return data; if (nbits == 0) return data;
+7
View File
@@ -23,6 +23,7 @@ void serialPrintUint64(uint64_t input, uint8_t base = 10);
String resultToSourceCode(const decode_results *results); String resultToSourceCode(const decode_results *results);
String resultToTimingInfo(const decode_results *results); String resultToTimingInfo(const decode_results *results);
String resultToHumanReadableBasic(const decode_results *results); String resultToHumanReadableBasic(const decode_results *results);
String resultToHexidecimal(const decode_results *result);
#else #else
std::string uint64ToString(uint64_t input, uint8_t base = 10); std::string uint64ToString(uint64_t input, uint8_t base = 10);
std::string typeToString(const decode_type_t protocol, std::string typeToString(const decode_type_t protocol,
@@ -30,10 +31,16 @@ std::string typeToString(const decode_type_t protocol,
std::string resultToSourceCode(const decode_results *results); std::string resultToSourceCode(const decode_results *results);
std::string resultToTimingInfo(const decode_results *results); std::string resultToTimingInfo(const decode_results *results);
std::string resultToHumanReadableBasic(const decode_results *results); std::string resultToHumanReadableBasic(const decode_results *results);
std::string resultToHexidecimal(const decode_results *result);
#endif #endif
bool hasACState(const decode_type_t protocol); bool hasACState(const decode_type_t protocol);
uint16_t getCorrectedRawLength(const decode_results *results); uint16_t getCorrectedRawLength(const decode_results *results);
uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0); uint8_t sumBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0);
uint8_t xorBytes(uint8_t *start, const uint16_t length, const uint8_t init = 0);
uint16_t countBits(const uint8_t *start, const uint16_t length,
const bool ones = true, const uint16_t init = 0);
uint16_t countBits(const uint64_t data, const uint8_t length,
const bool ones = true, const uint16_t init = 0);
uint64_t invertBits(const uint64_t data, const uint16_t nbits); uint64_t invertBits(const uint64_t data, const uint16_t nbits);
#endif // IRUTILS_H_ #endif // IRUTILS_H_
+17 -20
View File
@@ -13,12 +13,12 @@
// Added by David Conran. (Inspired by IRremoteESP8266's implementation: // Added by David Conran. (Inspired by IRremoteESP8266's implementation:
// https://github.com/z3t0/Arduino-IRremote) // https://github.com/z3t0/Arduino-IRremote)
#define AIWA_RC_T501_PRE_BITS 26U const uint16_t kAiwaRcT501PreBits = 26;
#define AIWA_RC_T501_POST_BITS 1U const uint16_t kAiwaRcT501PostBits = 1;
// NOTE: These are the compliment (inverted) of lirc values as // NOTE: These are the compliment (inverted) of lirc values as
// lirc uses a '0' for a mark, and a '1' for a space. // lirc uses a '0' for a mark, and a '1' for a space.
#define AIWA_RC_T501_PRE_DATA 0x1D8113FULL // 26-bits const uint64_t kAiwaRcT501PreData = 0x1D8113FULL; // 26-bits
#define AIWA_RC_T501_POST_DATA 1ULL const uint64_t kAiwaRcT501PostData = 1ULL;
#if SEND_AIWA_RC_T501 #if SEND_AIWA_RC_T501
// Send an Aiwa RC T501 formatted message. // Send an Aiwa RC T501 formatted message.
@@ -26,7 +26,7 @@
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The number of bits of the message to be sent. // nbits: The number of bits of the message to be sent.
// Typically AIWA_RC_T501_BITS. Max is 37 = (64 - 27) // Typically kAiwaRcT501Bits. Max is 37 = (64 - 27)
// repeat: The number of times the command is to be repeated. // repeat: The number of times the command is to be repeated.
// //
// Status: BETA / Should work. // Status: BETA / Should work.
@@ -37,10 +37,9 @@ void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Appears to be an extended NEC1 protocol. i.e. 42 bits instead of 32 bits. // Appears to be an extended NEC1 protocol. i.e. 42 bits instead of 32 bits.
// So use sendNEC instead, however the twist is it has a fixed 26 bit // So use sendNEC instead, however the twist is it has a fixed 26 bit
// prefix, and a fixed postfix bit. // prefix, and a fixed postfix bit.
uint64_t new_data = ( uint64_t new_data = ((kAiwaRcT501PreData << (nbits + kAiwaRcT501PostBits)) |
(AIWA_RC_T501_PRE_DATA << (nbits + AIWA_RC_T501_POST_BITS)) | (data << kAiwaRcT501PostBits) | kAiwaRcT501PostData);
(data << AIWA_RC_T501_POST_BITS) | AIWA_RC_T501_POST_DATA); nbits += kAiwaRcT501PreBits + kAiwaRcT501PostBits;
nbits += AIWA_RC_T501_PRE_BITS + AIWA_RC_T501_POST_BITS;
if (nbits > sizeof(new_data) * 8) if (nbits > sizeof(new_data) * 8)
return; // We are overflowing. Abort, and don't send. return; // We are overflowing. Abort, and don't send.
sendNEC(new_data, nbits, repeat); sendNEC(new_data, nbits, repeat);
@@ -52,7 +51,7 @@ void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically AIWA_RC_T501_BITS. // nbits: The number of data bits to expect. Typically kAiwaRcT501Bits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -71,12 +70,11 @@ void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) {
bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t nbits, bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
// Compliance // Compliance
if (strict && nbits != AIWA_RC_T501_BITS) if (strict && nbits != kAiwaRcT501Bits)
return false; // Doesn't match our protocol defn. return false; // Doesn't match our protocol defn.
// Add on the pre & post bits to our requested bit length. // Add on the pre & post bits to our requested bit length.
uint16_t expected_nbits = nbits + AIWA_RC_T501_PRE_BITS + uint16_t expected_nbits = nbits + kAiwaRcT501PreBits + kAiwaRcT501PostBits;
AIWA_RC_T501_POST_BITS;
uint64_t new_data; uint64_t new_data;
if (expected_nbits > sizeof(new_data) * 8) if (expected_nbits > sizeof(new_data) * 8)
return false; // We can't possibly match something that big. return false; // We can't possibly match something that big.
@@ -88,24 +86,23 @@ bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t nbits,
new_data = results->value; new_data = results->value;
if (actual_bits < expected_nbits) if (actual_bits < expected_nbits)
return false; // The data we caught was undersized. Throw it back. return false; // The data we caught was undersized. Throw it back.
if ((new_data & 0x1ULL) != AIWA_RC_T501_POST_DATA) if ((new_data & 0x1ULL) != kAiwaRcT501PostData)
return false; // The post data doesn't match, so it can't be this protocol. return false; // The post data doesn't match, so it can't be this protocol.
// Trim off the post data bit. // Trim off the post data bit.
new_data >>= AIWA_RC_T501_POST_BITS; new_data >>= kAiwaRcT501PostBits;
actual_bits -= AIWA_RC_T501_POST_BITS; actual_bits -= kAiwaRcT501PostBits;
// Extract out our likely new value and put it back in the results. // Extract out our likely new value and put it back in the results.
actual_bits -= AIWA_RC_T501_PRE_BITS; actual_bits -= kAiwaRcT501PreBits;
results->value = new_data & ((1ULL << actual_bits) - 1); results->value = new_data & ((1ULL << actual_bits) - 1);
// Check the prefix data matches. // Check the prefix data matches.
new_data >>= actual_bits; // Trim off the new data to expose the prefix. new_data >>= actual_bits; // Trim off the new data to expose the prefix.
if (new_data != AIWA_RC_T501_PRE_DATA) // Check the prefix. if (new_data != kAiwaRcT501PreData) // Check the prefix.
return false; return false;
// Compliance // Compliance
if (strict && results->bits != expected_nbits) if (strict && results->bits != expected_nbits) return false;
return false;
// Success // Success
results->decode_type = AIWA_RC_T501; results->decode_type = AIWA_RC_T501;
+31 -57
View File
@@ -11,44 +11,38 @@ Copyright 2017 Schmolders
// Constants // Constants
// using SPACE modulation. MARK is always const 400u // using SPACE modulation. MARK is always const 400u
#define ARGO_HDR_MARK 6400U // Mark const uint16_t kArgoHdrMark = 6400;
#define ARGO_HDR_SPACE 3300U // Space const uint16_t kArgoHdrSpace = 3300;
#define ARGO_BIT_MARK 400U const uint16_t kArgoBitMark = 400;
#define ARGO_ONE_SPACE 2200U const uint16_t kArgoOneSpace = 2200;
#define ARGO_ZERO_SPACE 900U const uint16_t kArgoZeroSpace = 900;
#if SEND_ARGO #if SEND_ARGO
// Send an Argo A/C message. // Send an Argo A/C message.
// //
// Args: // Args:
// data: An array of ARGO_COMMAND_LENGTH bytes containing the IR command. // data: An array of kArgoStateLength bytes containing the IR command.
// //
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
// Check if we have enough bytes to send a proper message. // Check if we have enough bytes to send a proper message.
if (nbytes < ARGO_COMMAND_LENGTH) return; if (nbytes < kArgoStateLength) return;
// TODO(kaschmo): validate // TODO(kaschmo): validate
sendGeneric(ARGO_HDR_MARK, ARGO_HDR_SPACE, sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace,
ARGO_BIT_MARK, ARGO_ONE_SPACE, kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer.
ARGO_BIT_MARK, ARGO_ZERO_SPACE, data, nbytes, 38, false, repeat, kDutyDefault);
0, 0, // No Footer.
data, nbytes, 38, false, repeat, 50);
} }
#endif // SEND_ARGO #endif // SEND_ARGO
IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
void IRArgoAC::begin() { void IRArgoAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_ARGO #if SEND_ARGO
void IRArgoAC::send() { void IRArgoAC::send(const uint16_t repeat) {
checksum(); // Create valid checksum before sending checksum(); // Create valid checksum before sending
_irsend.sendArgo(argo); _irsend.sendArgo(argo, kArgoStateLength, repeat);
} }
#endif // SEND_ARGO #endif // SEND_ARGO
@@ -58,8 +52,7 @@ void IRArgoAC::checksum() {
// Only add up bytes to 9. byte 10 is 0b01 constant anyway. // Only add up bytes to 9. byte 10 is 0b01 constant anyway.
// Assume that argo array is MSB first (left) // Assume that argo array is MSB first (left)
for (i = 0; i < 10; i++) for (i = 0; i < 10; i++) sum += argo[i];
sum += argo[i];
sum = sum % 256; // modulo 256 sum = sum % 256; // modulo 256
// Append sum to end of array // Append sum to end of array
@@ -70,8 +63,7 @@ void IRArgoAC::checksum() {
} }
void IRArgoAC::stateReset() { void IRArgoAC::stateReset() {
for (uint8_t i = 0; i < ARGO_COMMAND_LENGTH; i++) for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0;
argo[i] = 0x0;
// Argo Message. Store MSB left. // Argo Message. Store MSB left.
// Default message: // Default message:
@@ -84,8 +76,8 @@ void IRArgoAC::stateReset() {
this->off(); this->off();
this->setTemp(20); this->setTemp(20);
this->setRoomTemp(25); this->setRoomTemp(25);
this->setCoolMode(ARGO_COOL_AUTO); this->setCoolMode(kArgoCoolAuto);
this->setFan(ARGO_FAN_AUTO); this->setFan(kArgoFanAuto);
} }
uint8_t* IRArgoAC::getRaw() { uint8_t* IRArgoAC::getRaw() {
@@ -116,9 +108,7 @@ void IRArgoAC::setPower(bool state) {
off(); off();
} }
uint8_t IRArgoAC::getPower() { uint8_t IRArgoAC::getPower() { return ac_state; }
return ac_state;
}
void IRArgoAC::setMax(bool state) { void IRArgoAC::setMax(bool state) {
max_mode = state; max_mode = state;
@@ -128,17 +118,15 @@ void IRArgoAC::setMax(bool state) {
argo[9] &= 0b11110111; argo[9] &= 0b11110111;
} }
bool IRArgoAC::getMax() { bool IRArgoAC::getMax() { return max_mode; }
return max_mode;
}
// Set the temp in deg C // Set the temp in deg C
// Sending 0 equals +4 // Sending 0 equals +4
void IRArgoAC::setTemp(uint8_t temp) { void IRArgoAC::setTemp(uint8_t temp) {
if (temp < ARGO_MIN_TEMP) if (temp < kArgoMinTemp)
temp = ARGO_MIN_TEMP; temp = kArgoMinTemp;
else if (temp > ARGO_MAX_TEMP) else if (temp > kArgoMaxTemp)
temp = ARGO_MAX_TEMP; temp = kArgoMaxTemp;
// Store in attributes // Store in attributes
set_temp = temp; set_temp = temp;
@@ -154,9 +142,7 @@ void IRArgoAC::setTemp(uint8_t temp) {
argo[3] += temp >> 2; // remove lowest to bits and append in 0-2 argo[3] += temp >> 2; // remove lowest to bits and append in 0-2
} }
uint8_t IRArgoAC::getTemp() { uint8_t IRArgoAC::getTemp() { return set_temp; }
return set_temp;
}
// Set the speed of the fan // Set the speed of the fan
void IRArgoAC::setFan(uint8_t fan) { void IRArgoAC::setFan(uint8_t fan) {
@@ -168,18 +154,14 @@ void IRArgoAC::setFan(uint8_t fan) {
argo[3] += fan << 3; argo[3] += fan << 3;
} }
uint8_t IRArgoAC::getFan() { uint8_t IRArgoAC::getFan() { return fan_mode; }
return fan_mode;
}
void IRArgoAC::setFlap(uint8_t flap) { void IRArgoAC::setFlap(uint8_t flap) {
flap_mode = flap; flap_mode = flap;
// TODO(kaschmo): set correct bits for flap mode // TODO(kaschmo): set correct bits for flap mode
} }
uint8_t IRArgoAC::getFlap() { uint8_t IRArgoAC::getFlap() { return flap_mode; }
return flap_mode;
}
uint8_t IRArgoAC::getMode() { uint8_t IRArgoAC::getMode() {
// return cooling 0, heating 1 // return cooling 0, heating 1
@@ -196,9 +178,7 @@ void IRArgoAC::setCoolMode(uint8_t mode) {
argo[2] += mode << 3; argo[2] += mode << 3;
} }
uint8_t IRArgoAC::getCoolMode() { uint8_t IRArgoAC::getCoolMode() { return cool_mode; }
return cool_mode;
}
void IRArgoAC::setHeatMode(uint8_t mode) { void IRArgoAC::setHeatMode(uint8_t mode) {
ac_mode = 1; // Set ac mode to heating ac_mode = 1; // Set ac mode to heating
@@ -211,9 +191,7 @@ void IRArgoAC::setHeatMode(uint8_t mode) {
argo[2] += mode << 3; argo[2] += mode << 3;
} }
uint8_t IRArgoAC::getHeatMode() { uint8_t IRArgoAC::getHeatMode() { return heat_mode; }
return heat_mode;
}
void IRArgoAC::setNight(bool state) { void IRArgoAC::setNight(bool state) {
night_mode = state; night_mode = state;
@@ -224,9 +202,7 @@ void IRArgoAC::setNight(bool state) {
argo[9] &= 0b11111011; argo[9] &= 0b11111011;
} }
bool IRArgoAC::getNight() { bool IRArgoAC::getNight() { return night_mode; }
return night_mode;
}
void IRArgoAC::setiFeel(bool state) { void IRArgoAC::setiFeel(bool state) {
ifeel_mode = state; ifeel_mode = state;
@@ -237,9 +213,7 @@ void IRArgoAC::setiFeel(bool state) {
argo[9] &= 0b01111111; argo[9] &= 0b01111111;
} }
bool IRArgoAC::getiFeel() { bool IRArgoAC::getiFeel() { return ifeel_mode; }
return ifeel_mode;
}
void IRArgoAC::setTime() { void IRArgoAC::setTime() {
// TODO(kaschmo): use function call from checksum to set time first // TODO(kaschmo): use function call from checksum to set time first
+45 -27
View File
@@ -29,32 +29,50 @@
// Constants. Store MSB left. // Constants. Store MSB left.
#define ARGO_COOL_ON 0U // 0b000 const uint8_t kArgoCoolOn = 0; // 0b000
#define ARGO_COOL_OFF 3U // 0b110 const uint8_t kArgoCoolOff = 3; // 0b110
#define ARGO_COOL_AUTO 2U // 0b010 const uint8_t kArgoCoolAuto = 2; // 0b010
#define ARGO_COOl_HUM 1U // 0b100 const uint8_t kArgoCoolHum = 1; // 0b100
const uint8_t kArgoHeatOn = 0; // 0b001
const uint8_t kArgoHeatAuto = 1; // 0b101
const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is
const uint8_t kArgoMinTemp = 10; // Celsius offset +4
const uint8_t kArgoMaxTemp = 32; // Celsius
const uint8_t kArgoFanAuto = 0; // 0b00
const uint8_t kArgoFan3 = 3; // 0b11
const uint8_t kArgoFan2 = 2; // 0b01
const uint8_t kArgoFan1 = 1; // 0b10
const uint8_t kArgoFlapAuto = 0; // 0b000
const uint8_t kArgoFlap1 = 1; // 0b100
const uint8_t kArgoFlap2 = 2; // 0b010
const uint8_t kArgoFlap3 = 3; // 0b110
const uint8_t kArgoFlap4 = 4; // 0b001
const uint8_t kArgoFlap5 = 5; // 0b101
const uint8_t kArgoFlap6 = 6; // 0b011
const uint8_t kArgoFlapFull = 7; // 0b111
#define ARGO_HEAT_ON 0U // 0b001 // Legacy defines. (Deperecated)
#define ARGO_HEAT_AUTO 1U // 0b101 #define ARGO_COOL_ON kArgoCoolOn
#define ARGO_HEAT_BLINK 2U // 0b011 // ??no idea what mode that is #define ARGO_COOL_OFF kArgoCoolOff
#define ARGO_COOL_AUTO kArgoCoolAuto
#define ARGO_COOl_HUM kArgoCoolHum
#define ARGO_MIN_TEMP 10U // Celsius offset +4 #define ARGO_HEAT_ON kArgoHeatOn
#define ARGO_MAX_TEMP 32U // Celsius #define ARGO_HEAT_AUTO kArgoHeatAuto
#define ARGO_HEAT_BLINK kArgoHeatBlink
#define ARGO_FAN_AUTO 0U // 0b00 #define ARGO_MIN_TEMP kArgoMinTemp
#define ARGO_FAN_3 3U // 0b11 #define ARGO_MAX_TEMP kArgoMaxTemp
#define ARGO_FAN_2 2U // 0b01 #define ARGO_FAN_AUTO kArgoFanAuto
#define ARGO_FAN_1 1U // 0b10 #define ARGO_FAN_3 kArgoFan3
#define ARGO_FAN_2 kArgoFan2
#define ARGO_FLAP_AUTO 0U // 0b000 #define ARGO_FAN_1 kArgoFan1
#define ARGO_FLAP_1 1U // 0b100 #define ARGO_FLAP_AUTO kArgoFlapAuto
#define ARGO_FLAP_2 2U // 0b010 #define ARGO_FLAP_1 kArgoFlap1
#define ARGO_FLAP_3 3U // 0b110 #define ARGO_FLAP_2 kArgoFlap2
#define ARGO_FLAP_4 4U // 0b001 #define ARGO_FLAP_3 kArgoFlap3
#define ARGO_FLAP_5 5U // 0b101 #define ARGO_FLAP_4 kArgoFlap4
#define ARGO_FLAP_6 6U // 0b011 #define ARGO_FLAP_5 kArgoFlap5
#define ARGO_FLAP_FULL 7U // 0b111 #define ARGO_FLAP_6 kArgoFlap6
#define ARGO_FLAP_FULL kArgoFlapFull
class IRArgoAC { class IRArgoAC {
@@ -62,7 +80,7 @@ class IRArgoAC {
explicit IRArgoAC(uint16_t pin); explicit IRArgoAC(uint16_t pin);
#if SEND_ARGO #if SEND_ARGO
void send(); void send(const uint16_t repeat = kArgoDefaultRepeat);
#endif // SEND_ARGO #endif // SEND_ARGO
void begin(); void begin();
void on(); void on();
@@ -103,7 +121,7 @@ class IRArgoAC {
private: private:
// # of bytes per command // # of bytes per command
uint8_t argo[ARGO_COMMAND_LENGTH]; // Defined in IRremoteESP8266.h uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h
void stateReset(); void stateReset();
void checksum(); void checksum();
IRsend _irsend; // instance of the IR send class IRsend _irsend; // instance of the IR send class
+22 -27
View File
@@ -18,19 +18,19 @@
// Constants // Constants
// Ref: // Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/385 // https://github.com/markszabo/IRremoteESP8266/issues/385
#define CARRIER_AC_HDR_MARK 8532U const uint16_t kCarrierAcHdrMark = 8532;
#define CARRIER_AC_HDR_SPACE 4228U const uint16_t kCarrierAcHdrSpace = 4228;
#define CARRIER_AC_BIT_MARK 628U const uint16_t kCarrierAcBitMark = 628;
#define CARRIER_AC_ONE_SPACE 1320U const uint16_t kCarrierAcOneSpace = 1320;
#define CARRIER_AC_ZERO_SPACE 532U const uint16_t kCarrierAcZeroSpace = 532;
#define CARRIER_AC_GAP 20000U const uint16_t kCarrierAcGap = 20000;
#if SEND_CARRIER_AC #if SEND_CARRIER_AC
// Send a Carrier HVAC formatted message. // Send a Carrier HVAC formatted message.
// //
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The bit size of the message being sent. typically CARRIER_AC_BITS. // nbits: The bit size of the message being sent. typically kCarrierAcBits.
// repeat: The number of times the message is to be repeated. // repeat: The number of times the message is to be repeated.
// //
// Status: BETA / Appears to work on real devices. // Status: BETA / Appears to work on real devices.
@@ -40,11 +40,10 @@ void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint64_t temp_data = data; uint64_t temp_data = data;
// Carrier sends the data block three times. normal + inverted + normal. // Carrier sends the data block three times. normal + inverted + normal.
for (uint16_t i = 0; i < 3; i++) { for (uint16_t i = 0; i < 3; i++) {
sendGeneric(CARRIER_AC_HDR_MARK, CARRIER_AC_HDR_SPACE, sendGeneric(kCarrierAcHdrMark, kCarrierAcHdrSpace, kCarrierAcBitMark,
CARRIER_AC_BIT_MARK, CARRIER_AC_ONE_SPACE, kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace,
CARRIER_AC_BIT_MARK, CARRIER_AC_ZERO_SPACE, kCarrierAcBitMark, kCarrierAcGap, temp_data, nbits, 38, true,
CARRIER_AC_BIT_MARK, CARRIER_AC_GAP, 0, kDutyDefault);
temp_data, nbits, 38, true, 0, 50);
temp_data = invertBits(temp_data, nbits); temp_data = invertBits(temp_data, nbits);
} }
} }
@@ -58,7 +57,7 @@ void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion. // nbits: Nr. of bits to expect in the data portion.
// Typically CARRIER_AC_BITS. // Typically kCarrierAcBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -67,36 +66,32 @@ void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) {
// //
bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits, bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < ((2 * nbits + HEADER + FOOTER) * 3) - 1) if (results->rawlen < ((2 * nbits + kHeader + kFooter) * 3) - 1)
return false; // Can't possibly be a valid Carrier message. return false; // Can't possibly be a valid Carrier message.
if (strict && nbits != CARRIER_AC_BITS) if (strict && nbits != kCarrierAcBits)
return false; // We expect Carrier to be 32 bits of message. return false; // We expect Carrier to be 32 bits of message.
uint64_t data = 0; uint64_t data = 0;
uint64_t prev_data = 0; uint64_t prev_data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
for (uint8_t i = 0; i < 3; i++) { for (uint8_t i = 0; i < 3; i++) {
prev_data = data; prev_data = data;
// Header // Header
if (!matchMark(results->rawbuf[offset++], CARRIER_AC_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kCarrierAcHdrMark)) return false;
return false; if (!matchSpace(results->rawbuf[offset++], kCarrierAcHdrSpace))
if (!matchSpace(results->rawbuf[offset++], CARRIER_AC_HDR_SPACE))
return false; return false;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
CARRIER_AC_BIT_MARK, matchData(&(results->rawbuf[offset]), nbits, kCarrierAcBitMark,
CARRIER_AC_ONE_SPACE, kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace);
CARRIER_AC_BIT_MARK,
CARRIER_AC_ZERO_SPACE);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], CARRIER_AC_BIT_MARK)) if (!matchMark(results->rawbuf[offset++], kCarrierAcBitMark)) return false;
return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], CARRIER_AC_GAP)) !matchAtLeast(results->rawbuf[offset++], kCarrierAcGap))
return false; return false;
// Compliance. // Compliance.
if (strict) { if (strict) {
+306 -43
View File
@@ -1,6 +1,11 @@
// Copyright bakrus // Copyright bakrus
// Copyright 2017 David Conran // Copyright 2017 David Conran
#include "ir_Coolix.h"
#include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h" #include "IRrecv.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
@@ -12,32 +17,36 @@
// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX // CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX
// Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit // Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit
//
// Supports:
// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon.
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/484
// Constants // Constants
// Pulse parms are *50-100 for the Mark and *50+100 for the space // Pulse parms are *50-100 for the Mark and *50+100 for the space
// First MARK is the one after the long gap // First MARK is the one after the long gap
// pulse parameters in usec // pulse parameters in usec
#define COOLIX_TICK 560U // Approximately 21 cycles at 38kHz const uint16_t kCoolixTick = 560; // Approximately 21 cycles at 38kHz
#define COOLIX_BIT_MARK_TICKS 1U const uint16_t kCoolixBitMarkTicks = 1;
#define COOLIX_BIT_MARK (COOLIX_BIT_MARK_TICKS * COOLIX_TICK) const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick;
#define COOLIX_ONE_SPACE_TICKS 3U const uint16_t kCoolixOneSpaceTicks = 3;
#define COOLIX_ONE_SPACE (COOLIX_ONE_SPACE_TICKS * COOLIX_TICK) const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick;
#define COOLIX_ZERO_SPACE_TICKS 1U const uint16_t kCoolixZeroSpaceTicks = 1;
#define COOLIX_ZERO_SPACE (COOLIX_ZERO_SPACE_TICKS * COOLIX_TICK) const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick;
#define COOLIX_HDR_MARK_TICKS 8U const uint16_t kCoolixHdrMarkTicks = 8;
#define COOLIX_HDR_MARK (COOLIX_HDR_MARK_TICKS * COOLIX_TICK) const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick;
#define COOLIX_HDR_SPACE_TICKS 8U const uint16_t kCoolixHdrSpaceTicks = 8;
#define COOLIX_HDR_SPACE (COOLIX_HDR_SPACE_TICKS * COOLIX_TICK) const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick;
#define COOLIX_MIN_GAP_TICKS (COOLIX_HDR_MARK_TICKS + \ const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks;
COOLIX_ZERO_SPACE_TICKS) const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick;
#define COOLIX_MIN_GAP (COOLIX_MIN_GAP_TICKS * COOLIX_TICK)
#if SEND_COOLIX #if SEND_COOLIX
// Send a Coolix message // Send a Coolix message
// //
// Args: // Args:
// data: Contents of the message to be sent. // data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically COOLIX_BITS. // nbits: Nr. of bits of data to be sent. Typically kCoolixBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: BETA / Probably works. // Status: BETA / Probably works.
@@ -46,16 +55,15 @@
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp
// TODO(anyone): Verify repeat functionality against a real unit. // TODO(anyone): Verify repeat functionality against a real unit.
void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits % 8 != 0) if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.
return; // nbits is required to be a multiple of 8.
// Set IR carrier frequency // Set IR carrier frequency
enableIROut(38); enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
// Header // Header
mark(COOLIX_HDR_MARK); mark(kCoolixHdrMark);
space(COOLIX_HDR_SPACE); space(kCoolixHdrSpace);
// Data // Data
// Break data into byte segments, starting at the Most Significant // Break data into byte segments, starting at the Most Significant
@@ -64,28 +72,285 @@ void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Grab a bytes worth of data. // Grab a bytes worth of data.
uint8_t segment = (data >> (nbits - i)) & 0xFF; uint8_t segment = (data >> (nbits - i)) & 0xFF;
// Normal // Normal
sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE, sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark,
COOLIX_BIT_MARK, COOLIX_ZERO_SPACE, kCoolixZeroSpace, segment, 8, true);
segment, 8, true);
// Inverted. // Inverted.
sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE, sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark,
COOLIX_BIT_MARK, COOLIX_ZERO_SPACE, kCoolixZeroSpace, segment ^ 0xFF, 8, true);
segment ^ 0xFF, 8, true);
} }
// Footer // Footer
mark(COOLIX_BIT_MARK); mark(kCoolixBitMark);
space(COOLIX_MIN_GAP); // Pause before repeating space(kCoolixMinGap); // Pause before repeating
} }
} }
#endif #endif
// IRCoolixAC class
// Supports:
// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon.
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/484
IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRCoolixAC::stateReset() { remote_state = kCoolixDefaultState; }
void IRCoolixAC::begin() { _irsend.begin(); }
#if SEND_COOLIX
void IRCoolixAC::send(const uint16_t repeat) {
_irsend.sendCOOLIX(remote_state, kCoolixBits, repeat);
}
#endif // SEND_COOLIX
uint32_t IRCoolixAC::getRaw() { return remote_state; }
void IRCoolixAC::setRaw(const uint32_t new_code) { remote_state = new_code; }
void IRCoolixAC::setTempRaw(const uint8_t code) {
remote_state &= ~kCoolixTempMask; // Clear the old temp.
remote_state |= (code << 4);
}
uint8_t IRCoolixAC::getTempRaw() {
return (remote_state & kCoolixTempMask) >> 4;
}
void IRCoolixAC::setTemp(const uint8_t desired) {
// Range check.
uint8_t temp = std::min(desired, kCoolixTempMax);
temp = std::max(temp, kCoolixTempMin);
setTempRaw(kCoolixTempMap[temp - kCoolixTempMin]);
}
uint8_t IRCoolixAC::getTemp() {
uint8_t code = getTempRaw();
uint8_t i;
for (i = 0; i < kCoolixTempRange; i++)
if (kCoolixTempMap[i] == code) return kCoolixTempMin + i;
return kCoolixUnknown; // Not a temp we expected.
}
void IRCoolixAC::setSensorTempRaw(const uint8_t code) {
remote_state &= ~kCoolixSensorTempMask; // Clear previous sensor temp.
remote_state |= ((code & 0xF) << 8);
}
void IRCoolixAC::setSensorTemp(const uint8_t desired) {
uint8_t temp = desired;
temp = std::min(temp, kCoolixSensorTempMax);
temp = std::max(temp, kCoolixSensorTempMin);
setSensorTempRaw(temp - kCoolixSensorTempMin);
setZoneFollow(true); // Setting a Sensor temp means you want to Zone Follow.
}
uint8_t IRCoolixAC::getSensorTemp() {
return ((remote_state & kCoolixSensorTempMask) >> 8) + kCoolixSensorTempMin;
}
bool IRCoolixAC::getPower() {
// There is only an off state. Everything else is "on".
return remote_state != kCoolixOff;
}
void IRCoolixAC::setPower(const bool power) {
if (!power) remote_state = kCoolixOff;
// There really is no distinct "on" setting, so do nothing.
}
bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; }
void IRCoolixAC::setSwing() {
// Assumes that repeated sending "swing" toggles the action on the device.
remote_state = kCoolixSwing;
}
bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; }
void IRCoolixAC::setSleep() { remote_state = kCoolixSleep; }
bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; }
void IRCoolixAC::setTurbo() {
// Assumes that repeated sending "turbo" toggles the action on the device.
remote_state = kCoolixTurbo;
}
bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; }
void IRCoolixAC::setLed() {
// Assumes that repeated sending "Led" toggles the action on the device.
remote_state = kCoolixLed;
}
bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; }
void IRCoolixAC::setClean() { remote_state = kCoolixClean; }
bool IRCoolixAC::getZoneFollow() {
return remote_state & kCoolixZoneFollowMask;
}
// Internal use only.
void IRCoolixAC::setZoneFollow(bool state) {
if (state) {
remote_state |= kCoolixZoneFollowMask;
} else {
remote_state &= ~kCoolixZoneFollowMask;
}
}
void IRCoolixAC::clearSensorTemp() {
setZoneFollow(false);
setSensorTempRaw(kCoolixSensorTempIgnoreCode);
}
void IRCoolixAC::setMode(const uint8_t mode) {
uint32_t actualmode = mode;
// Fan mode is a special case of Dry.
if (mode == kCoolixFan) actualmode = kCoolixDry;
switch (actualmode) {
case kCoolixCool:
case kCoolixAuto:
case kCoolixHeat:
case kCoolixDry:
remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2);
// Force the temp into a known-good state.
setTemp(getTemp());
}
if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode);
}
uint8_t IRCoolixAC::getMode() {
uint8_t mode = (remote_state & kCoolixModeMask) >> 2;
if (mode == kCoolixDry)
if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan;
return mode;
}
uint8_t IRCoolixAC::getFan() { return (remote_state & kCoolixFanMask) >> 13; }
void IRCoolixAC::setFan(const uint8_t speed) {
uint8_t newspeed = speed;
switch (speed) {
case kCoolixFanMin:
case kCoolixFanMed:
case kCoolixFanMax:
case kCoolixFanAuto:
case kCoolixFanAuto0:
case kCoolixFanZoneFollow:
case kCoolixFanFixed:
break;
default: // Unknown speed requested.
newspeed = kCoolixFanAuto;
}
remote_state &= ~kCoolixFanMask;
remote_state |= ((newspeed << 13) & kCoolixFanMask);
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRCoolixAC::toString() {
String result = "";
#else
std::string IRCoolixAC::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
if (getPower()) {
result += "On";
} else {
result += "Off";
return result; // If it's off, there is no other info.
}
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kCoolixFanAuto:
result += " (AUTO)";
break;
case kCoolixFanAuto0:
result += " (AUTO0)";
break;
case kCoolixFanMax:
result += " (MAX)";
break;
case kCoolixFanMin:
result += " (MIN)";
break;
case kCoolixFanMed:
result += " (MED)";
break;
case kCoolixFanZoneFollow:
result += " (ZONEFOLLOW)";
break;
case kCoolixFanFixed:
result += " (FIXED)";
break;
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: ";
if (getZoneFollow())
result += "On";
else
result += "Off";
result += ", Sensor Temp: ";
if (getSensorTemp() > kCoolixSensorTempMax)
result += "Ignored";
else
result += uint64ToString(getSensorTemp()) + "C";
return result;
}
#if DECODE_COOLIX #if DECODE_COOLIX
// Decode the supplied Coolix message. // Decode the supplied Coolix message.
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically COOLIX_BITS. // nbits: The number of data bits to expect. Typically kCoolixBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -95,42 +360,41 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
// The protocol sends the data normal + inverted, alternating on // The protocol sends the data normal + inverted, alternating on
// each byte. Hence twice the number of expected data bits. // each byte. Hence twice the number of expected data bits.
if (results->rawlen < 2 * 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid COOLIX message. return false; // Can't possibly be a valid COOLIX message.
if (strict && nbits != COOLIX_BITS) if (strict && nbits != kCoolixBits)
return false; // Not strictly a COOLIX message. return false; // Not strictly a COOLIX message.
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
return false; return false;
uint64_t data = 0; uint64_t data = 0;
uint64_t inverted = 0; uint64_t inverted = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
if (nbits > sizeof(data) * 8) if (nbits > sizeof(data) * 8)
return false; // We can't possibly capture a Coolix packet that big. return false; // We can't possibly capture a Coolix packet that big.
// Header // Header
if (!matchMark(results->rawbuf[offset], COOLIX_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kCoolixHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / COOLIX_HDR_MARK_TICKS; uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], COOLIX_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset], kCoolixHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrSpaceTicks;
COOLIX_HDR_SPACE_TICKS;
// Data // Data
// Twice as many bits as there are normal plus inverted bits. // Twice as many bits as there are normal plus inverted bits.
for (uint16_t i = 0; i < nbits * 2; i++, offset++) { for (uint16_t i = 0; i < nbits * 2; i++, offset++) {
bool flip = (i / 8) % 2; bool flip = (i / 8) % 2;
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick))
return false; return false;
if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE_TICKS * s_tick)) { if (matchSpace(results->rawbuf[offset], kCoolixOneSpaceTicks * s_tick)) {
if (flip) if (flip)
inverted = (inverted << 1) | 1; inverted = (inverted << 1) | 1;
else else
data = (data << 1) | 1; data = (data << 1) | 1;
} else if (matchSpace(results->rawbuf[offset], } else if (matchSpace(results->rawbuf[offset],
COOLIX_ZERO_SPACE_TICKS * s_tick)) { kCoolixZeroSpaceTicks * s_tick)) {
if (flip) if (flip)
inverted <<= 1; inverted <<= 1;
else else
@@ -141,18 +405,17 @@ bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
} }
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], COOLIX_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kCoolixMinGapTicks * s_tick))
return false; return false;
// Compliance // Compliance
uint64_t orig = data; // Save a copy of the data. uint64_t orig = data; // Save a copy of the data.
if (strict) { if (strict) {
for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8) for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8)
if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF)) if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF)) return false;
return false;
} }
// Success // Success
+140
View File
@@ -0,0 +1,140 @@
// Coolix A/C
//
// Copyright 2018 David Conran
#ifndef IR_COOLIX_H_
#define IR_COOLIX_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// CCCCC OOOOO OOOOO LL IIIII XX XX
// CC C OO OO OO OO LL III XX XX
// CC OO OO OO OO LL III XXXX
// CC C OO OO OO OO LL III XX XX
// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX
// Supports:
// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon.
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/484
// Kudos:
// Hamper: For the breakdown and mapping of the bit values.
// Constants
// Modes
const uint8_t kCoolixCool = 0b00;
const uint8_t kCoolixDry = 0b01;
const uint8_t kCoolixAuto = 0b10;
const uint8_t kCoolixHeat = 0b11;
const uint8_t kCoolixFan = 4; // Synthetic.
const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC
const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000
// Fan Control
const uint8_t kCoolixFanMin = 0b100;
const uint8_t kCoolixFanMed = 0b010;
const uint8_t kCoolixFanMax = 0b001;
const uint8_t kCoolixFanAuto = 0b101;
const uint8_t kCoolixFanAuto0 = 0b000;
const uint8_t kCoolixFanZoneFollow = 0b110;
const uint8_t kCoolixFanFixed = 0b111;
const uint32_t kCoolixFanMask = 0b000000001110000000000000; // 0x00E000
// Temperature
const uint8_t kCoolixTempMin = 17; // Celsius
const uint8_t kCoolixTempMax = 30; // Celsius
const uint8_t kCoolixTempRange = kCoolixTempMax - kCoolixTempMin + 1;
const uint8_t kCoolixFanTempCode = 0b1110; // Part of Fan Mode.
const uint32_t kCoolixTempMask = 0b11110000;
const uint8_t kCoolixTempMap[kCoolixTempRange] = {
0b0000, // 17C
0b0001, // 18c
0b0011, // 19C
0b0010, // 20C
0b0110, // 21C
0b0111, // 22C
0b0101, // 23C
0b0100, // 24C
0b1100, // 25C
0b1101, // 26C
0b1001, // 27C
0b1000, // 28C
0b1010, // 29C
0b1011 // 30C
};
const uint8_t kCoolixSensorTempMin = 16; // Celsius
const uint8_t kCoolixSensorTempMax = 30; // Celsius
const uint8_t kCoolixSensorTempIgnoreCode = 0b1111;
const uint32_t kCoolixSensorTempMask = 0b000000000000111100000000; // 0xF00
// Fixed states/messages.
const uint8_t kCoolixPrefix = 0b1011; // 0xB
const uint8_t kCoolixUnknown = 0xFF;
const uint32_t kCoolixOff = 0b101100100111101111100000; // 0xB27BE0
const uint32_t kCoolixSwing = 0b101100100110101111100000; // 0xB26BE0
const uint32_t kCoolixSleep = 0b101100101110000000000011; // 0xB2E003
const uint32_t kCoolixTurbo = 0b101101011111010110100010; // 0xB5F5A2
const uint32_t kCoolixLed = 0b101101011111010110100101; // 0xB5F5A5
const uint32_t kCoolixClean = 0b101101011111010110101010; // 0xB5F5AA
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
const uint32_t kCoolixDefaultState = 0b101100101011111111001000; // 0xB2BFC8
// Classes
class IRCoolixAC {
public:
explicit IRCoolixAC(uint16_t pin);
void stateReset();
#if SEND_COOLIX
void send(const uint16_t repeat = kCoolixDefaultRepeat);
#endif // SEND_COOLIX
void begin();
void on();
void off();
void setPower(const bool state);
bool getPower();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setSensorTemp(const uint8_t desired);
uint8_t getSensorTemp();
void clearSensorTemp();
void setFan(const uint8_t fan);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
void setSwing();
bool getSwing();
void setSleep();
bool getSleep();
void setTurbo();
bool getTurbo();
void setLed();
bool getLed();
void setClean();
bool getClean();
bool getZoneFollow();
uint32_t getRaw();
void setRaw(const uint32_t new_code);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
private:
// The state of the IR remote in IR code form.
uint32_t remote_state;
IRsend _irsend;
void setTempRaw(const uint8_t code);
uint8_t getTempRaw();
void setSensorTempRaw(const uint8_t code);
void setZoneFollow(const bool state);
};
#endif // IR_COOLIX_H_
File diff suppressed because it is too large. Load diff
+186 -56
View File
@@ -1,5 +1,6 @@
// Copyright 2016 sillyfrog // Copyright 2016 sillyfrog
// Copyright 2017 sillyfrog, crankyoldgit // Copyright 2017 sillyfrog, crankyoldgit
// Copyright 2018-2019 crankyoldgit
#ifndef IR_DAIKIN_H_ #ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_ #define IR_DAIKIN_H_
@@ -8,8 +9,8 @@
#else #else
#include <string> #include <string>
#endif #endif
#include "IRremoteESP8266.h"
#include "IRrecv.h" #include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
// Option to disable the additional Daikin debug info to conserve memory // Option to disable the additional Daikin debug info to conserve memory
@@ -76,64 +77,107 @@
*/ */
// Constants // Constants
#define DAIKIN_COOL 0b011 const uint8_t kDaikinAuto = 0b000;
#define DAIKIN_HEAT 0b100 const uint8_t kDaikinDry = 0b010;
#define DAIKIN_FAN 0b110 const uint8_t kDaikinCool = 0b011;
#define DAIKIN_AUTO 0b000 const uint8_t kDaikinHeat = 0b100;
#define DAIKIN_DRY 0b010 const uint8_t kDaikinFan = 0b110;
#define DAIKIN_MIN_TEMP 10U // Celsius const uint8_t kDaikinMinTemp = 10; // Celsius
#define DAIKIN_MAX_TEMP 32U // Celsius const uint8_t kDaikinMaxTemp = 32; // Celsius
#define DAIKIN_FAN_MIN (uint8_t) 1U const uint8_t kDaikinFanMin = 1;
#define DAIKIN_FAN_MAX (uint8_t) 5U const uint8_t kDaikinFanMax = 5;
#define DAIKIN_FAN_AUTO (uint8_t) 0b1010 const uint8_t kDaikinFanAuto = 0b1010;
#define DAIKIN_FAN_QUIET (uint8_t) 0b1011 const uint8_t kDaikinFanQuiet = 0b1011;
const uint8_t kDaikinBytePower = 13;
#define DAIKIN_BYTE_POWER 13 const uint8_t kDaikinBitPower = 0b00000001;
#define DAIKIN_BIT_POWER 0b00000001 const uint8_t kDaikinBytePowerful = 21;
const uint8_t kDaikinBitPowerful = 0b00000001;
#define DAIKIN_BYTE_POWERFUL 21 const uint8_t kDaikinByteSilent = 21;
#define DAIKIN_BIT_POWERFUL 0b00000001 const uint8_t kDaikinBitSilent = 0b00100000;
#define DAIKIN_BYTE_SILENT 21 const uint8_t kDaikinByteSensor = 24;
#define DAIKIN_BIT_SILENT 0b00100000 const uint8_t kDaikinBitSensor = 0b00000010;
const uint8_t kDaikinByteEcono = 24;
#define DAIKIN_BYTE_SENSOR 24 const uint8_t kDaikinBitEcono = 0b00000100;
#define DAIKIN_BIT_SENSOR 0b00000010 const uint8_t kDaikinByteEye = 24;
#define DAIKIN_BYTE_ECONO 24 const uint8_t kDaikinBitEye = 0b10000000;
#define DAIKIN_BIT_ECONO 0b00000100 const uint8_t kDaikinByteMold = 25;
#define DAIKIN_BYTE_EYE 24 const uint8_t kDaikinBitMold = 0b00000010;
#define DAIKIN_BIT_EYE 0b10000000 const uint8_t kDaikinByteOffTimer = 13;
#define DAIKIN_BYTE_MOLD 25 const uint8_t kDaikinBitOffTimer = 0b00000100;
#define DAIKIN_BIT_MOLD 0b00000010 const uint8_t kDaikinByteOnTimer = 13;
const uint8_t kDaikinBitOnTimer = 0b00000010;
#define DAIKIN_BYTE_OFF_TIMER 13 const uint16_t kDaikinUnusedTime = 0x600;
#define DAIKIN_BIT_OFF_TIMER 0b00000100 const uint8_t kDaikinBeepQuiet = 1;
#define DAIKIN_BYTE_ON_TIMER 13 const uint8_t kDaikinBeepLoud = 2;
#define DAIKIN_BIT_ON_TIMER 0b00000010 const uint8_t kDaikinBeepOff = 3;
const uint8_t kDaikinLightBright = 1;
#define DAIKIN_CURBIT DAIKIN_COMMAND_LENGTH const uint8_t kDaikinLightDim = 2;
#define DAIKIN_CURINDEX (DAIKIN_COMMAND_LENGTH + 1) const uint8_t kDaikinLightOff = 3;
#define OFFSET_ERR 65432 const uint8_t kDaikinCurBit = kDaikinStateLength;
const uint8_t kDaikinCurIndex = kDaikinStateLength + 1;
#define DAIKIN_TOLERANCE 35 const uint8_t kDaikinTolerance = 35;
#define DAIKIN_MARK_EXCESS MARK_EXCESS const uint16_t kDaikinMarkExcess = kMarkExcess;
const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8
#define DAIKIN_HDR_MARK 3650U // DAIKIN_BIT_MARK * 8 const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4
#define DAIKIN_HDR_SPACE 1623U // DAIKIN_BIT_MARK * 4 const uint16_t kDaikinBitMark = 428;
#define DAIKIN_BIT_MARK 428U const uint16_t kDaikinZeroSpace = 428;
#define DAIKIN_ZERO_SPACE 428U const uint16_t kDaikinOneSpace = 1280;
#define DAIKIN_ONE_SPACE 1280U const uint16_t kDaikinGap = 29000;
#define DAIKIN_GAP 29000U
// Note bits in each octet swapped so can be sent as a single value // Note bits in each octet swapped so can be sent as a single value
#define DAIKIN_FIRST_HEADER64 \ const uint64_t kDaikinFirstHeader64 =
0b1101011100000000000000001100010100000000001001111101101000010001 0b1101011100000000000000001100010100000000001001111101101000010001;
// Another variant of the protocol for the Daikin ARC477A1 remote.
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 kDaikin2Sections = 2;
const uint16_t kDaikin2Section1Length = 20;
const uint16_t kDaikin2Section2Length = 19;
const uint8_t kDaikin2BitSleepTimer = 0b00100000;
const uint8_t kDaikin2BitPurify = 0b00010000;
const uint8_t kDaikin2BitEye = 0b00000010;
const uint8_t kDaikin2BitEyeAuto = 0b10000000;
const uint8_t kDaikin2BitMold = 0b00001000;
const uint8_t kDaikin2BitClean = 0b00100000;
const uint8_t kDaikin2BitFreshAir = 0b00000001;
const uint8_t kDaikin2BitFreshAirHigh = 0b10000000;
const uint8_t kDaikin2LightMask = 0b00110000;
const uint8_t kDaikin2BeepMask = 0b11000000;
const uint8_t kDaikin2SwingVHigh = 0x1;
const uint8_t kDaikin2SwingVLow = 0x6;
const uint8_t kDaikin2SwingVBreeze = 0xC;
const uint8_t kDaikin2SwingVCirculate = 0xD;
const uint8_t kDaikin2SwingVAuto = 0xE;
const uint8_t kDaikin2SwingHAuto = 0xBE;
const uint8_t kDaikin2SwingHSwing = 0xBF;
// Legacy defines.
#define DAIKIN_COOL kDaikinCool
#define DAIKIN_HEAT kDaikinHeat
#define DAIKIN_FAN kDaikinFan
#define DAIKIN_AUTO kDaikinAuto
#define DAIKIN_DRY kDaikinDry
#define DAIKIN_MIN_TEMP kDaikinMinTemp
#define DAIKIN_MAX_TEMP kDaikinMaxTemp
#define DAIKIN_FAN_MIN kDaikinFanMin
#define DAIKIN_FAN_MAX kDaikinFanMax
#define DAIKIN_FAN_AUTO kDaikinFanAuto
#define DAIKIN_FAN_QUIET kDaikinFanQuiet
class IRDaikinESP { class IRDaikinESP {
public: public:
explicit IRDaikinESP(uint16_t pin); explicit IRDaikinESP(uint16_t pin);
#if SEND_DAIKIN #if SEND_DAIKIN
void send(); void send(const uint16_t repeat = kDaikinDefaultRepeat);
#endif #endif
void begin(); void begin();
void on(); void on();
@@ -179,8 +223,8 @@ class IRDaikinESP {
#endif // DAIKIN_DEBUG #endif // DAIKIN_DEBUG
uint32_t getCommand(); uint32_t getCommand();
void setCommand(uint32_t value); void setCommand(uint32_t value);
static bool validChecksum(const uint8_t state[], static bool validChecksum(uint8_t state[],
const uint16_t length = DAIKIN_COMMAND_LENGTH); const uint16_t length = kDaikinStateLength);
#ifdef ARDUINO #ifdef ARDUINO
String toString(); String toString();
static String renderTime(uint16_t timemins); static String renderTime(uint16_t timemins);
@@ -191,9 +235,8 @@ class IRDaikinESP {
private: private:
// # of bytes per command // # of bytes per command
uint8_t daikin[DAIKIN_COMMAND_LENGTH]; uint8_t daikin[kDaikinStateLength];
void stateReset(); void stateReset();
static uint8_t calcBlockChecksum(const uint8_t *block, const uint16_t length);
void checksum(); void checksum();
void setBit(uint8_t byte, uint8_t bitmask); void setBit(uint8_t byte, uint8_t bitmask);
void clearBit(uint8_t byte, uint8_t bitmask); void clearBit(uint8_t byte, uint8_t bitmask);
@@ -201,4 +244,91 @@ class IRDaikinESP {
IRsend _irsend; IRsend _irsend;
}; };
// Class to emulate a Daikin ARC477A1 remote.
class IRDaikin2 {
public:
explicit IRDaikin2(uint16_t pin);
#if SEND_DAIKIN2
void send(const uint16_t repeat = kDaikin2DefaultRepeat);
#endif
void begin();
void on();
void off();
void setPower(const bool state);
bool getPower();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t fan);
uint8_t getFan();
uint8_t getMode();
void setMode(const uint8_t mode);
void setSwingVertical(const uint8_t position);
uint8_t getSwingVertical();
void setSwingHorizontal(const uint8_t position);
uint8_t getSwingHorizontal();
bool getQuiet();
void setQuiet(const bool on);
bool getPowerful();
void setPowerful(const bool on);
void setSensor(const bool on);
bool getSensor();
void setEcono(const bool on);
bool getEcono();
void setEye(const bool on);
bool getEye();
void setEyeAuto(const bool on);
bool getEyeAuto();
void setPurify(const bool on);
bool getPurify();
void setMold(const bool on);
bool getMold();
void enableOnTimer(const uint16_t starttime);
void disableOnTimer();
uint16_t getOnTime();
bool getOnTimerEnabled();
void enableSleepTimer(const uint16_t sleeptime);
void disableSleepTimer();
uint16_t getSleepTime();
bool getSleepTimerEnabled();
void enableOffTimer(const uint16_t endtime);
void disableOffTimer();
uint16_t getOffTime();
bool getOffTimerEnabled();
void setCurrentTime(const uint16_t time);
uint16_t getCurrentTime();
void setBeep(const uint8_t beep);
uint8_t getBeep();
void setLight(const uint8_t light);
uint8_t getLight();
void setClean(const bool on);
bool getClean();
void setFreshAir(const bool on);
bool getFreshAir();
void setFreshAirHigh(const bool on);
bool getFreshAirHigh();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[]);
uint32_t getCommand();
void setCommand(uint32_t value);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin2StateLength);
#ifdef ARDUINO
String toString();
static String renderTime(uint16_t timemins);
#else
std::string toString();
static std::string renderTime(uint16_t timemins);
#endif
private:
// # of bytes per command
uint8_t remote_state[kDaikin2StateLength];
void stateReset();
void checksum();
void clearOnTimerFlag();
void clearSleepTimerFlag();
IRsend _irsend;
};
#endif // IR_DAIKIN_H_ #endif // IR_DAIKIN_H_
+36 -40
View File
@@ -18,25 +18,25 @@
// Constants // Constants
// Ref: // Ref:
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
#define DENON_TICK 263U const uint16_t kDenonTick = 263;
#define DENON_HDR_MARK_TICKS 1U const uint16_t kDenonHdrMarkTicks = 1;
#define DENON_HDR_MARK (DENON_HDR_MARK_TICKS * DENON_TICK) const uint16_t kDenonHdrMark = kDenonHdrMarkTicks * kDenonTick;
#define DENON_HDR_SPACE_TICKS 3U const uint16_t kDenonHdrSpaceTicks = 3;
#define DENON_HDR_SPACE (DENON_HDR_SPACE_TICKS * DENON_TICK) const uint16_t kDenonHdrSpace = kDenonHdrSpaceTicks * kDenonTick;
#define DENON_BIT_MARK_TICKS 1U const uint16_t kDenonBitMarkTicks = 1;
#define DENON_BIT_MARK (DENON_BIT_MARK_TICKS * DENON_TICK) const uint16_t kDenonBitMark = kDenonBitMarkTicks * kDenonTick;
#define DENON_ONE_SPACE_TICKS 7U const uint16_t kDenonOneSpaceTicks = 7;
#define DENON_ONE_SPACE (DENON_ONE_SPACE_TICKS * DENON_TICK) const uint16_t kDenonOneSpace = kDenonOneSpaceTicks * kDenonTick;
#define DENON_ZERO_SPACE_TICKS 3U const uint16_t kDenonZeroSpaceTicks = 3;
#define DENON_ZERO_SPACE (DENON_ZERO_SPACE_TICKS * DENON_TICK) const uint16_t kDenonZeroSpace = kDenonZeroSpaceTicks * kDenonTick;
#define DENON_MIN_COMMAND_LENGTH_TICKS 510U const uint16_t kDenonMinCommandLengthTicks = 510;
#define DENON_MIN_COMMAND_LENGTH (DENON_MIN_COMMAND_LENGTH_TICKS * DENON_TICK) const uint16_t kDenonMinGapTicks =
#define DENON_MIN_GAP_TICKS (DENON_MIN_COMMAND_LENGTH_TICKS - \ kDenonMinCommandLengthTicks -
(DENON_HDR_MARK_TICKS + DENON_HDR_SPACE_TICKS + \ (kDenonHdrMarkTicks + kDenonHdrSpaceTicks +
DENON_BITS * (DENON_BIT_MARK_TICKS + DENON_ONE_SPACE_TICKS) + \ kDenonBits * (kDenonBitMarkTicks + kDenonOneSpaceTicks) +
DENON_BIT_MARK_TICKS)) kDenonBitMarkTicks);
#define DENON_MIN_GAP (DENON_MIN_GAP_TICKS * DENON_TICK) const uint32_t kDenonMinGap = kDenonMinGapTicks * kDenonTick;
#define DENON_MANUFACTURER 0x2A4CULL const uint64_t kDenonManufacturer = 0x2A4CULL;
#if SEND_DENON #if SEND_DENON
// Send a Denon message // Send a Denon message
@@ -55,9 +55,9 @@
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
// http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls // http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits >= PANASONIC_BITS) // Is this really Panasonic? if (nbits >= kPanasonicBits) // Is this really Panasonic?
sendPanasonic64(data, nbits, repeat); sendPanasonic64(data, nbits, repeat);
else if (nbits == DENON_LEGACY_BITS) else if (nbits == kDenonLegacyBits)
// Support legacy (broken) calls of sendDenon(). // Support legacy (broken) calls of sendDenon().
sendSharpRaw(data & (~0x2000ULL), nbits + 1, repeat); sendSharpRaw(data & (~0x2000ULL), nbits + 1, repeat);
else else
@@ -84,7 +84,7 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
switch (nbits) { switch (nbits) {
case DENON_BITS: case DENON_BITS:
case DENON_48_BITS: case DENON_48_BITS:
case DENON_LEGACY_BITS: case kDenonLegacyBits:
break; break;
default: default:
return false; return false;
@@ -99,40 +99,36 @@ bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) {
// manufacturer code. // manufacturer code.
if (!decodeSharp(results, nbits, true, false) && if (!decodeSharp(results, nbits, true, false) &&
!decodePanasonic(results, nbits, true, DENON_MANUFACTURER)) { !decodePanasonic(results, nbits, true, kDenonManufacturer)) {
// We couldn't decode it as expected, so try the old legacy method. // We couldn't decode it as expected, so try the old legacy method.
// NOTE: I don't think this following protocol actually exists. // NOTE: I don't think this following protocol actually exists.
// Looks like a partial version of the Sharp protocol. // Looks like a partial version of the Sharp protocol.
// Check we have enough data // Check we have enough data
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false;
return false; if (strict && nbits != kDenonLegacyBits) return false;
if (strict && nbits != DENON_LEGACY_BITS)
return false;
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], DENON_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kDenonHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDenonHdrMarkTicks;
DENON_HDR_MARK_TICKS; if (!matchSpace(results->rawbuf[offset], kDenonHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset], DENON_HDR_SPACE)) return false; uint32_t s_tick =
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / results->rawbuf[offset++] * kRawTick / kDenonHdrSpaceTicks;
DENON_HDR_SPACE_TICKS;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
DENON_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits,
DENON_ONE_SPACE_TICKS * s_tick, kDenonBitMarkTicks * m_tick, kDenonOneSpaceTicks * s_tick,
DENON_BIT_MARK_TICKS * m_tick, kDenonBitMarkTicks * m_tick, kDenonZeroSpaceTicks * s_tick);
DENON_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], DENON_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kDenonBitMarkTicks * m_tick))
return false; return false;
// Success // Success
+32 -35
View File
@@ -18,19 +18,19 @@
// Ref: // Ref:
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
// http://www.hifi-remote.com/wiki/index.php?title=Dish // http://www.hifi-remote.com/wiki/index.php?title=Dish
#define DISH_TICK 100U const uint16_t kDishTick = 100;
#define DISH_HDR_MARK_TICKS 4U const uint16_t kDishHdrMarkTicks = 4;
#define DISH_HDR_MARK (DISH_HDR_MARK_TICKS * DISH_TICK) const uint16_t kDishHdrMark = kDishHdrMarkTicks * kDishTick;
#define DISH_HDR_SPACE_TICKS 61U const uint16_t kDishHdrSpaceTicks = 61;
#define DISH_HDR_SPACE (DISH_HDR_SPACE_TICKS * DISH_TICK) const uint16_t kDishHdrSpace = kDishHdrSpaceTicks * kDishTick;
#define DISH_BIT_MARK_TICKS 4U const uint16_t kDishBitMarkTicks = 4;
#define DISH_BIT_MARK (DISH_BIT_MARK_TICKS * DISH_TICK) const uint16_t kDishBitMark = kDishBitMarkTicks * kDishTick;
#define DISH_ONE_SPACE_TICKS 17U const uint16_t kDishOneSpaceTicks = 17;
#define DISH_ONE_SPACE (DISH_ONE_SPACE_TICKS * DISH_TICK) const uint16_t kDishOneSpace = kDishOneSpaceTicks * kDishTick;
#define DISH_ZERO_SPACE_TICKS 28U const uint16_t kDishZeroSpaceTicks = 28;
#define DISH_ZERO_SPACE (DISH_ZERO_SPACE_TICKS * DISH_TICK) const uint16_t kDishZeroSpace = kDishZeroSpaceTicks * kDishTick;
#define DISH_RPT_SPACE_TICKS DISH_HDR_SPACE_TICKS const uint16_t kDishRptSpaceTicks = kDishHdrSpaceTicks;
#define DISH_RPT_SPACE (DISH_RPT_SPACE_TICKS * DISH_TICK) const uint16_t kDishRptSpace = kDishRptSpaceTicks * kDishTick;
#if SEND_DISH #if SEND_DISH
// Send an IR command to a DISH NETWORK device. // Send an IR command to a DISH NETWORK device.
@@ -57,14 +57,13 @@
void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) {
enableIROut(57600); // Set modulation freq. to 57.6kHz. enableIROut(57600); // Set modulation freq. to 57.6kHz.
// Header is only ever sent once. // Header is only ever sent once.
mark(DISH_HDR_MARK); mark(kDishHdrMark);
space(DISH_HDR_SPACE); space(kDishHdrSpace);
sendGeneric(0, 0, // No headers from here on in. sendGeneric(0, 0, // No headers from here on in.
DISH_BIT_MARK, DISH_ONE_SPACE, kDishBitMark, kDishOneSpace, kDishBitMark, kDishZeroSpace,
DISH_BIT_MARK, DISH_ZERO_SPACE, kDishBitMark, kDishRptSpace, data, nbits, 57600, true, repeat,
DISH_BIT_MARK, DISH_RPT_SPACE, 50);
data, nbits, 57600, true, repeat, 50);
} }
#endif #endif
@@ -73,7 +72,7 @@ void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // 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 DISH_BITS. // nbits: Nr. of bits to expect in the data portion. Typically kDishBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -89,34 +88,32 @@ void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) {
// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx // http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Not enough entries to be valid. return false; // Not enough entries to be valid.
if (strict && nbits != DISH_BITS) if (strict && nbits != kDishBits) return false; // Not strictly compliant.
return false; // Not strictly compliant.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!match(results->rawbuf[offset], DISH_HDR_MARK)) return false; if (!match(results->rawbuf[offset], kDishHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / DISH_HDR_MARK_TICKS; uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDishHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], DISH_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset], kDishHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / DISH_HDR_SPACE_TICKS; uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kDishHdrSpaceTicks;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
DISH_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits, kDishBitMarkTicks * m_tick,
DISH_ONE_SPACE_TICKS * s_tick, kDishOneSpaceTicks * s_tick, kDishBitMarkTicks * m_tick,
DISH_BIT_MARK_TICKS * m_tick, kDishZeroSpaceTicks * s_tick);
DISH_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], DISH_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kDishBitMarkTicks * m_tick))
return false; return false;
// Compliance // Compliance
@@ -124,7 +121,7 @@ bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) {
// The DISH protocol calls for a repeated message, so strictly speaking // The DISH protocol calls for a repeated message, so strictly speaking
// there should be a code following this. Only require it if we are set to // there should be a code following this. Only require it if we are set to
// strict matching. // strict matching.
if (!matchSpace(results->rawbuf[offset], DISH_RPT_SPACE_TICKS * s_tick)) if (!matchSpace(results->rawbuf[offset], kDishRptSpaceTicks * s_tick))
return false; return false;
} }
+112
View File
@@ -0,0 +1,112 @@
// Copyright 2018 David Conran
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA
// EE LL EE CC C TTT RR RR AAAAA
// EEEEE LL EEEEE CC TTT RRRRRR AA AA
// EE LL EE CC C TTT RR RR AAAAAAA
// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA
// Electra A/C added by crankyoldgit
//
// Equipment it seems compatible with:
// * <Add models (A/C & remotes) you've gotten it working with here>
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/527
// Constants
const uint16_t kElectraAcHdrMark = 9166;
const uint16_t kElectraAcBitMark = 646;
const uint16_t kElectraAcHdrSpace = 4470;
const uint16_t kElectraAcOneSpace = 1647;
const uint16_t kElectraAcZeroSpace = 547;
const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess.
#if SEND_ELECTRA_AC
// Send a Electra message
//
// Args:
// data: Contents of the message to be sent. (Guessing MSBF order)
// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits.
// repeat: Nr. of additional times the message is to be sent.
//
// Status: Alpha / Needs testing against a real device.
//
void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
for (uint16_t r = 0; r <= repeat; r++)
sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark,
kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace,
kElectraAcBitMark, kElectraAcMessageGap, data, nbytes,
38000, // Complete guess of the modulation frequency.
true, 0, 50);
}
#endif
#if DECODE_ELECTRA_AC
// Decode the supplied Electra A/C 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 kElectraAcBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: Alpha / Needs testing against a real device.
//
bool IRrecv::decodeElectraAC(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 != kElectraAcBits)
return false; // Not strictly a ELECTRA_AC message.
}
// The protocol sends the data normal + inverted, alternating on
// each byte. Hence twice the number of expected data bits.
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid ELECTRA_AC message.
uint16_t offset = kStartOffset;
// Message Header
if (!matchMark(results->rawbuf[offset++], kElectraAcHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kElectraAcHdrSpace)) return false;
// Data Section
match_result_t data_result;
uint16_t dataBitsSoFar = 0;
// 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, kElectraAcBitMark,
kElectraAcOneSpace, kElectraAcBitMark,
kElectraAcZeroSpace, kTolerance, 0, true);
if (data_result.success == false) return false; // Fail
results->state[i] = data_result.data;
}
// Message Footer
if (!matchMark(results->rawbuf[offset++], kElectraAcBitMark)) return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kElectraAcMessageGap))
return false;
// Compliance
if (strict && dataBitsSoFar != nbits) return false;
// Success
results->decode_type = ELECTRA_AC;
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_ELECTRA_AC
+121 -149
View File
@@ -7,7 +7,6 @@
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
// Fujitsu A/C support added by Jonny Graham & David Conran // Fujitsu A/C support added by Jonny Graham & David Conran
// Equipment it seems compatible with: // Equipment it seems compatible with:
@@ -17,12 +16,12 @@
// Ref: // Ref:
// These values are based on averages of measurements // These values are based on averages of measurements
#define FUJITSU_AC_HDR_MARK 3324U const uint16_t kFujitsuAcHdrMark = 3324;
#define FUJITSU_AC_HDR_SPACE 1574U const uint16_t kFujitsuAcHdrSpace = 1574;
#define FUJITSU_AC_BIT_MARK 448U const uint16_t kFujitsuAcBitMark = 448;
#define FUJITSU_AC_ONE_SPACE 1182U const uint16_t kFujitsuAcOneSpace = 1182;
#define FUJITSU_AC_ZERO_SPACE 390U const uint16_t kFujitsuAcZeroSpace = 390;
#define FUJITSU_AC_MIN_GAP 8100U const uint16_t kFujitsuAcMinGap = 8100;
#if SEND_FUJITSU_AC #if SEND_FUJITSU_AC
// Send a Fujitsu A/C message. // Send a Fujitsu A/C message.
@@ -30,22 +29,21 @@
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. Typically one of: // nbytes: Nr. of bytes of data in the array. Typically one of:
// FUJITSU_AC_STATE_LENGTH // kFujitsuAcStateLength
// FUJITSU_AC_STATE_LENGTH - 1 // kFujitsuAcStateLength - 1
// FUJITSU_AC_STATE_LENGTH_SHORT // kFujitsuAcStateLengthShort
// FUJITSU_AC_STATE_LENGTH_SHORT - 1 // kFujitsuAcStateLengthShort - 1
// repeat: Nr. of times the message is to be repeated. // repeat: Nr. of times the message is to be repeated.
// (Default = FUJITSU_AC_MIN_REPEAT). // (Default = kFujitsuAcMinRepeat).
// //
// Status: BETA / Appears to be working. // Status: BETA / Appears to be working.
// //
void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
sendGeneric(FUJITSU_AC_HDR_MARK, FUJITSU_AC_HDR_SPACE, sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark,
FUJITSU_AC_BIT_MARK, FUJITSU_AC_ONE_SPACE, kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace,
FUJITSU_AC_BIT_MARK, FUJITSU_AC_ZERO_SPACE, kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false,
FUJITSU_AC_BIT_MARK, FUJITSU_AC_MIN_GAP, repeat, 50);
data, nbytes, 38, false, repeat, 50);
} }
#endif // SEND_FUJITSU_AC #endif // SEND_FUJITSU_AC
@@ -62,35 +60,33 @@ void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) {
_model = model; _model = model;
switch (model) { switch (model) {
case ARDB1: case ARDB1:
_state_length = FUJITSU_AC_STATE_LENGTH - 1; _state_length = kFujitsuAcStateLength - 1;
_state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT - 1; _state_length_short = kFujitsuAcStateLengthShort - 1;
break; break;
default: default:
_state_length = FUJITSU_AC_STATE_LENGTH; _state_length = kFujitsuAcStateLength;
_state_length_short = FUJITSU_AC_STATE_LENGTH_SHORT; _state_length_short = kFujitsuAcStateLengthShort;
} }
} }
// Reset the state of the remote to a known good state/sequence. // Reset the state of the remote to a known good state/sequence.
void IRFujitsuAC::stateReset() { void IRFujitsuAC::stateReset() {
_temp = 24; _temp = 24;
_fanSpeed = FUJITSU_AC_FAN_HIGH; _fanSpeed = kFujitsuAcFanHigh;
_mode = FUJITSU_AC_MODE_COOL; _mode = kFujitsuAcModeCool;
_swingMode = FUJITSU_AC_SWING_BOTH; _swingMode = kFujitsuAcSwingBoth;
_cmd = FUJITSU_AC_CMD_TURN_ON; _cmd = kFujitsuAcCmdTurnOn;
buildState(); buildState();
} }
// Configure the pin for output. // Configure the pin for output.
void IRFujitsuAC::begin() { void IRFujitsuAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_FUJITSU_AC #if SEND_FUJITSU_AC
// Send the current desired state to the IR LED. // Send the current desired state to the IR LED.
void IRFujitsuAC::send() { void IRFujitsuAC::send(const uint16_t repeat) {
getRaw(); getRaw();
_irsend.sendFujitsuAC(remote_state, getStateLength()); _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat);
} }
#endif // SEND_FUJITSU_AC #endif // SEND_FUJITSU_AC
@@ -102,13 +98,13 @@ void IRFujitsuAC::buildState() {
remote_state[4] = 0x10; remote_state[4] = 0x10;
bool fullCmd = false; bool fullCmd = false;
switch (_cmd) { switch (_cmd) {
case FUJITSU_AC_CMD_TURN_OFF: case kFujitsuAcCmdTurnOff:
remote_state[5] = 0x02; remote_state[5] = 0x02;
break; break;
case FUJITSU_AC_CMD_STEP_HORIZ: case kFujitsuAcCmdStepHoriz:
remote_state[5] = 0x79; remote_state[5] = 0x79;
break; break;
case FUJITSU_AC_CMD_STEP_VERT: case kFujitsuAcCmdStepVert:
remote_state[5] = 0x6C; remote_state[5] = 0x6C;
break; break;
default: default:
@@ -124,12 +120,12 @@ void IRFujitsuAC::buildState() {
break; break;
} }
if (fullCmd) { // long codes if (fullCmd) { // long codes
uint8_t tempByte = _temp - FUJITSU_AC_MIN_TEMP; uint8_t tempByte = _temp - kFujitsuAcMinTemp;
// Nr. of bytes in the message after this byte. // Nr. of bytes in the message after this byte.
remote_state[6] = _state_length - 7; remote_state[6] = _state_length - 7;
remote_state[7] = 0x30; remote_state[7] = 0x30;
remote_state[8] = (_cmd == FUJITSU_AC_CMD_TURN_ON) | (tempByte << 4); remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4);
remote_state[9] = _mode | 0 << 4; // timer off remote_state[9] = _mode | 0 << 4; // timer off
remote_state[10] = _fanSpeed | _swingMode << 4; remote_state[10] = _fanSpeed | _swingMode << 4;
remote_state[11] = 0; // timerOff values remote_state[11] = 0; // timerOff values
@@ -154,12 +150,10 @@ void IRFujitsuAC::buildState() {
} else { // short codes } else { // short codes
if (_model == ARRAH2E) if (_model == ARRAH2E)
// The last byte is the inverse of penultimate byte // The last byte is the inverse of penultimate byte
remote_state[_state_length_short - 1] = ~remote_state[_state_length_short remote_state[_state_length_short - 1] =
- 2]; ~remote_state[_state_length_short - 2];
// Zero the rest of the state. // Zero the rest of the state.
for (uint8_t i = _state_length_short; for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++)
i < FUJITSU_AC_STATE_LENGTH;
i++)
remote_state[i] = 0; remote_state[i] = 0;
} }
} }
@@ -181,8 +175,8 @@ uint8_t* IRFujitsuAC::getRaw() {
void IRFujitsuAC::buildFromState(const uint16_t length) { void IRFujitsuAC::buildFromState(const uint16_t length) {
switch (length) { switch (length) {
case FUJITSU_AC_STATE_LENGTH - 1: case kFujitsuAcStateLength - 1:
case FUJITSU_AC_STATE_LENGTH_SHORT - 1: case kFujitsuAcStateLengthShort - 1:
setModel(ARDB1); setModel(ARDB1);
break; break;
default: default:
@@ -196,26 +190,26 @@ void IRFujitsuAC::buildFromState(const uint16_t length) {
setModel(ARRAH2E); setModel(ARRAH2E);
break; break;
} }
setTemp((remote_state[8] >> 4) + FUJITSU_AC_MIN_TEMP); setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp);
if (remote_state[8] & 0x1) if (remote_state[8] & 0x1)
setCmd(FUJITSU_AC_CMD_TURN_ON); setCmd(kFujitsuAcCmdTurnOn);
else else
setCmd(FUJITSU_AC_CMD_STAY_ON); setCmd(kFujitsuAcCmdStayOn);
setMode(remote_state[9] & 0b111); setMode(remote_state[9] & 0b111);
setFanSpeed(remote_state[10] & 0b111); setFanSpeed(remote_state[10] & 0b111);
setSwing(remote_state[10] >> 4); setSwing(remote_state[10] >> 4);
switch (remote_state[5]) { switch (remote_state[5]) {
case FUJITSU_AC_CMD_TURN_OFF: case kFujitsuAcCmdTurnOff:
case FUJITSU_AC_CMD_STEP_HORIZ: case kFujitsuAcCmdStepHoriz:
case FUJITSU_AC_CMD_STEP_VERT: case kFujitsuAcCmdStepVert:
setCmd(remote_state[5]); setCmd(remote_state[5]);
break; break;
} }
} }
bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
if (length > FUJITSU_AC_STATE_LENGTH) return false; if (length > kFujitsuAcStateLength) return false;
for (uint16_t i = 0; i < FUJITSU_AC_STATE_LENGTH; i++) { for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) {
if (i < length) if (i < length)
remote_state[i] = newState[i]; remote_state[i] = newState[i];
else else
@@ -226,115 +220,99 @@ bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) {
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRFujitsuAC::off() { void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; }
_cmd = FUJITSU_AC_CMD_TURN_OFF;
}
void IRFujitsuAC::stepHoriz() { void IRFujitsuAC::stepHoriz() {
switch (_model) { switch (_model) {
case ARDB1: break; // This remote doesn't have a horizontal option. case ARDB1:
break; // This remote doesn't have a horizontal option.
default: default:
_cmd = FUJITSU_AC_CMD_STEP_HORIZ; _cmd = kFujitsuAcCmdStepHoriz;
} }
} }
void IRFujitsuAC::stepVert() { void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; }
_cmd = FUJITSU_AC_CMD_STEP_VERT;
}
// Set the requested command of the A/C. // Set the requested command of the A/C.
void IRFujitsuAC::setCmd(uint8_t cmd) { void IRFujitsuAC::setCmd(uint8_t cmd) {
switch (cmd) { switch (cmd) {
case FUJITSU_AC_CMD_TURN_OFF: case kFujitsuAcCmdTurnOff:
case FUJITSU_AC_CMD_TURN_ON: case kFujitsuAcCmdTurnOn:
case FUJITSU_AC_CMD_STAY_ON: case kFujitsuAcCmdStayOn:
case FUJITSU_AC_CMD_STEP_VERT: case kFujitsuAcCmdStepVert:
_cmd = cmd; _cmd = cmd;
break; break;
case FUJITSU_AC_CMD_STEP_HORIZ: case kFujitsuAcCmdStepHoriz:
if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal.
_cmd = cmd; _cmd = cmd;
// FALLTHRU
default: default:
_cmd = FUJITSU_AC_CMD_STAY_ON; _cmd = kFujitsuAcCmdStayOn;
break; break;
} }
} }
uint8_t IRFujitsuAC::getCmd() { uint8_t IRFujitsuAC::getCmd() { return _cmd; }
return _cmd;
}
bool IRFujitsuAC::getPower() { bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; }
return _cmd != FUJITSU_AC_CMD_TURN_OFF;
}
// Set the temp. in deg C // Set the temp. in deg C
void IRFujitsuAC::setTemp(uint8_t temp) { void IRFujitsuAC::setTemp(uint8_t temp) {
temp = std::max((uint8_t) FUJITSU_AC_MIN_TEMP, temp); temp = std::max((uint8_t)kFujitsuAcMinTemp, temp);
temp = std::min((uint8_t) FUJITSU_AC_MAX_TEMP, temp); temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp);
_temp = temp; _temp = temp;
} }
uint8_t IRFujitsuAC::getTemp() { uint8_t IRFujitsuAC::getTemp() { return _temp; }
return _temp;
}
// Set the speed of the fan // Set the speed of the fan
void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) { void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) {
if (fanSpeed > FUJITSU_AC_FAN_QUIET) if (fanSpeed > kFujitsuAcFanQuiet)
fanSpeed = FUJITSU_AC_FAN_HIGH; // Set the fan to maximum if out of range. fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range.
_fanSpeed = fanSpeed; _fanSpeed = fanSpeed;
} }
uint8_t IRFujitsuAC::getFanSpeed() { uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; }
return _fanSpeed;
}
// Set the requested climate operation mode of the a/c unit. // Set the requested climate operation mode of the a/c unit.
void IRFujitsuAC::setMode(uint8_t mode) { void IRFujitsuAC::setMode(uint8_t mode) {
if (mode > FUJITSU_AC_MODE_HEAT) if (mode > kFujitsuAcModeHeat)
mode = FUJITSU_AC_MODE_HEAT; // Set the mode to maximum if out of range. mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range.
_mode = mode; _mode = mode;
} }
uint8_t IRFujitsuAC::getMode() { uint8_t IRFujitsuAC::getMode() { return _mode; }
return _mode;
}
// Set the requested swing operation mode of the a/c unit. // Set the requested swing operation mode of the a/c unit.
void IRFujitsuAC::setSwing(uint8_t swingMode) { void IRFujitsuAC::setSwing(uint8_t swingMode) {
switch (_model) { switch (_model) {
case ARDB1: case ARDB1:
// Set the mode to max if out of range // Set the mode to max if out of range
if (swingMode > FUJITSU_AC_SWING_VERT) if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert;
swingMode = FUJITSU_AC_SWING_VERT;
break; break;
case ARRAH2E: case ARRAH2E:
default: default:
// Set the mode to max if out of range // Set the mode to max if out of range
if (swingMode > FUJITSU_AC_SWING_BOTH) if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth;
swingMode = FUJITSU_AC_SWING_BOTH;
} }
_swingMode = swingMode; _swingMode = swingMode;
} }
uint8_t IRFujitsuAC::getSwing() { uint8_t IRFujitsuAC::getSwing() { return _swingMode; }
return _swingMode;
}
bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) {
uint8_t sum = 0; uint8_t sum = 0;
uint8_t sum_complement = 0; uint8_t sum_complement = 0;
uint8_t checksum = 0; uint8_t checksum = state[length - 1];
switch (length) { switch (length) {
case FUJITSU_AC_STATE_LENGTH_SHORT: // ARRAH2E case kFujitsuAcStateLengthShort: // ARRAH2E
return state[length - 1] == (uint8_t)~state[length - 2]; return state[length - 1] == (uint8_t)~state[length - 2];
case FUJITSU_AC_STATE_LENGTH - 1: // ARDB1 case kFujitsuAcStateLength - 1: // ARDB1
sum = sumBytes(state, length - 1); sum = sumBytes(state, length - 1);
sum_complement = 0x9B; sum_complement = 0x9B;
checksum = state[length - 1];
break; break;
case FUJITSU_AC_STATE_LENGTH: // ARRAH2E case kFujitsuAcStateLength: // ARRAH2E
sum = sumBytes(state + FUJITSU_AC_STATE_LENGTH_SHORT, sum = sumBytes(state + kFujitsuAcStateLengthShort,
length - 1 - FUJITSU_AC_STATE_LENGTH_SHORT); length - 1 - kFujitsuAcStateLengthShort);
break;
default: // Includes ARDB1 short. default: // Includes ARDB1 short.
return true; // Assume the checksum is valid for other lengths. return true; // Assume the checksum is valid for other lengths.
} }
@@ -356,19 +334,19 @@ std::string IRFujitsuAC::toString() {
result += "Off"; result += "Off";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case FUJITSU_AC_MODE_AUTO: case kFujitsuAcModeAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case FUJITSU_AC_MODE_COOL: case kFujitsuAcModeCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case FUJITSU_AC_MODE_HEAT: case kFujitsuAcModeHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case FUJITSU_AC_MODE_DRY: case kFujitsuAcModeDry:
result += " (DRY)"; result += " (DRY)";
break; break;
case FUJITSU_AC_MODE_FAN: case kFujitsuAcModeFan:
result += " (FAN)"; result += " (FAN)";
break; break;
default: default:
@@ -377,34 +355,34 @@ std::string IRFujitsuAC::toString() {
result += ", Temp: " + uint64ToString(getTemp()) + "C"; result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFanSpeed()); result += ", Fan: " + uint64ToString(getFanSpeed());
switch (getFanSpeed()) { switch (getFanSpeed()) {
case FUJITSU_AC_FAN_AUTO: case kFujitsuAcFanAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case FUJITSU_AC_FAN_HIGH: case kFujitsuAcFanHigh:
result += " (HIGH)"; result += " (HIGH)";
break; break;
case FUJITSU_AC_FAN_MED: case kFujitsuAcFanMed:
result += " (MED)"; result += " (MED)";
break; break;
case FUJITSU_AC_FAN_LOW: case kFujitsuAcFanLow:
result += " (LOW)"; result += " (LOW)";
break; break;
case FUJITSU_AC_FAN_QUIET: case kFujitsuAcFanQuiet:
result += " (QUIET)"; result += " (QUIET)";
break; break;
} }
result += ", Swing: "; result += ", Swing: ";
switch (getSwing()) { switch (getSwing()) {
case FUJITSU_AC_SWING_OFF: case kFujitsuAcSwingOff:
result += "Off"; result += "Off";
break; break;
case FUJITSU_AC_SWING_VERT: case kFujitsuAcSwingVert:
result += "Vert"; result += "Vert";
break; break;
case FUJITSU_AC_SWING_HORIZ: case kFujitsuAcSwingHoriz:
result += "Horiz"; result += "Horiz";
break; break;
case FUJITSU_AC_SWING_BOTH: case kFujitsuAcSwingBoth:
result += "Vert + Horiz"; result += "Vert + Horiz";
break; break;
default: default:
@@ -412,10 +390,10 @@ std::string IRFujitsuAC::toString() {
} }
result += ", Command: "; result += ", Command: ";
switch (getCmd()) { switch (getCmd()) {
case FUJITSU_AC_CMD_STEP_HORIZ: case kFujitsuAcCmdStepHoriz:
result += "Step vane horizontally"; result += "Step vane horizontally";
break; break;
case FUJITSU_AC_CMD_STEP_VERT: case kFujitsuAcCmdStepVert:
result += "Step vane vertically"; result += "Step vane vertically";
break; break;
default: default:
@@ -429,7 +407,7 @@ std::string IRFujitsuAC::toString() {
// Places successful decode information in the results pointer. // Places successful decode information in the results pointer.
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically FUJITSU_AC_BITS. // nbits: The number of data bits to expect. Typically kFujitsuAcBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -440,21 +418,20 @@ std::string IRFujitsuAC::toString() {
// //
bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits,
bool strict) { bool strict) {
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0; uint16_t dataBitsSoFar = 0;
// Have we got enough data to successfully decode? // Have we got enough data to successfully decode?
if (results->rawlen < (2 * FUJITSU_AC_MIN_BITS) + HEADER + FOOTER - 1) if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1)
return false; // Can't possibly be a valid message. return false; // Can't possibly be a valid message.
// Compliance // Compliance
if (strict) { if (strict) {
switch (nbits) { switch (nbits) {
case FUJITSU_AC_BITS: case kFujitsuAcBits:
case FUJITSU_AC_BITS - 8: case kFujitsuAcBits - 8:
case FUJITSU_AC_MIN_BITS: case kFujitsuAcMinBits:
case FUJITSU_AC_MIN_BITS + 8: case kFujitsuAcMinBits + 8:
break; break;
default: default:
return false; // Must be called with the correct nr. of bits. return false; // Must be called with the correct nr. of bits.
@@ -462,22 +439,17 @@ bool IRrecv::decodeFujitsuAC(decode_results *results, uint16_t nbits,
} }
// Header // Header
if (!matchMark(results->rawbuf[offset++], FUJITSU_AC_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false;
return false; if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset++], FUJITSU_AC_HDR_SPACE))
return false;
// Data (Fixed signature) // Data (Fixed signature)
match_result_t data_result = matchData(&(results->rawbuf[offset]), match_result_t data_result =
FUJITSU_AC_MIN_BITS - 8, matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8,
FUJITSU_AC_BIT_MARK, kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark,
FUJITSU_AC_ONE_SPACE, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
FUJITSU_AC_BIT_MARK,
FUJITSU_AC_ZERO_SPACE);
if (data_result.success == false) return false; // Fail if (data_result.success == false) return false; // Fail
if (reverseBits(data_result.data, FUJITSU_AC_MIN_BITS - 8) != 0x1010006314) if (data_result.data != 0x1010006314) return false; // Signature failed.
return false; // Signature failed. dataBitsSoFar += kFujitsuAcMinBits - 8;
dataBitsSoFar += FUJITSU_AC_MIN_BITS - 8;
offset += data_result.used; offset += data_result.used;
results->state[0] = 0x14; results->state[0] = 0x14;
results->state[1] = 0x63; results->state[1] = 0x63;
@@ -487,23 +459,23 @@ bool IRrecv::decodeFujitsuAC(decode_results *results, uint16_t nbits,
// Keep reading bytes until we either run out of message or state to fill. // Keep reading bytes until we either run out of message or state to fill.
for (uint16_t i = 5; for (uint16_t i = 5;
offset <= results->rawlen - 16 && i < FUJITSU_AC_STATE_LENGTH; offset <= results->rawlen - 16 && i < kFujitsuAcStateLength;
i++, dataBitsSoFar += 8, offset += data_result.used) { i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8, data_result = matchData(
FUJITSU_AC_BIT_MARK, &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace,
FUJITSU_AC_ONE_SPACE, kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false);
FUJITSU_AC_BIT_MARK,
FUJITSU_AC_ZERO_SPACE);
if (data_result.success == false) break; // Fail if (data_result.success == false) break; // Fail
results->state[i] = (uint8_t) reverseBits(data_result.data, 8); results->state[i] = data_result.data;
} }
// Footer // Footer
if (offset > results->rawlen || if (offset > results->rawlen ||
!matchMark(results->rawbuf[offset++], FUJITSU_AC_BIT_MARK)) return false; !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark))
return false;
// The space is optional if we are out of capture. // The space is optional if we are out of capture.
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], FUJITSU_AC_MIN_GAP)) return false; !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap))
return false;
// Compliance // Compliance
if (strict) { if (strict) {
@@ -515,23 +487,23 @@ bool IRrecv::decodeFujitsuAC(decode_results *results, uint16_t nbits,
// Compliance // Compliance
switch (dataBitsSoFar) { switch (dataBitsSoFar) {
case FUJITSU_AC_MIN_BITS: case kFujitsuAcMinBits:
// Check if this values indicate that this should have been a long state // Check if this values indicate that this should have been a long state
// message. // message.
if (results->state[5] == 0xFC) return false; if (results->state[5] == 0xFC) return false;
return true; // Success return true; // Success
case FUJITSU_AC_MIN_BITS + 8: case kFujitsuAcMinBits + 8:
// Check if this values indicate that this should have been a long state // Check if this values indicate that this should have been a long state
// message. // message.
if (results->state[5] == 0xFE) return false; if (results->state[5] == 0xFE) return false;
// The last byte needs to be the inverse of the penultimate byte. // The last byte needs to be the inverse of the penultimate byte.
if (results->state[5] != (uint8_t)~results->state[6]) return false; if (results->state[5] != (uint8_t)~results->state[6]) return false;
return true; // Success return true; // Success
case FUJITSU_AC_BITS - 8: case kFujitsuAcBits - 8:
// Long messages of this size require this byte be correct. // Long messages of this size require this byte be correct.
if (results->state[5] != 0xFC) return false; if (results->state[5] != 0xFC) return false;
break; break;
case FUJITSU_AC_BITS: case kFujitsuAcBits:
// Long messages of this size require this byte be correct. // Long messages of this size require this byte be correct.
if (results->state[5] != 0xFE) return false; if (results->state[5] != 0xFE) return false;
break; break;
+47 -26
View File
@@ -1,4 +1,5 @@
// Copyright 2017 Jonny Graham // Copyright 2017 Jonny Graham
// Copyright 2018 David Conran
#ifndef IR_FUJITSU_H_ #ifndef IR_FUJITSU_H_
#define IR_FUJITSU_H_ #define IR_FUJITSU_H_
@@ -9,41 +10,61 @@
#else #else
#include <string> #include <string>
#endif #endif
#include "IRremoteESP8266.h"
#include "IRrecv.h" #include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
// FUJITSU A/C support added by Jonny Graham // FUJITSU A/C support added by Jonny Graham
// Constants // Constants
const uint8_t kFujitsuAcModeAuto = 0x00;
const uint8_t kFujitsuAcModeCool = 0x01;
const uint8_t kFujitsuAcModeDry = 0x02;
const uint8_t kFujitsuAcModeFan = 0x03;
const uint8_t kFujitsuAcModeHeat = 0x04;
#define FUJITSU_AC_MODE_AUTO 0x00U const uint8_t kFujitsuAcCmdStayOn = 0x00;
#define FUJITSU_AC_MODE_COOL 0x01U const uint8_t kFujitsuAcCmdTurnOn = 0x01;
#define FUJITSU_AC_MODE_DRY 0x02U const uint8_t kFujitsuAcCmdTurnOff = 0x02;
#define FUJITSU_AC_MODE_FAN 0x03U const uint8_t kFujitsuAcCmdStepHoriz = 0x79;
#define FUJITSU_AC_MODE_HEAT 0x04U const uint8_t kFujitsuAcCmdStepVert = 0x6C;
#define FUJITSU_AC_CMD_STAY_ON 0x00U const uint8_t kFujitsuAcFanAuto = 0x00;
#define FUJITSU_AC_CMD_TURN_ON 0x01U const uint8_t kFujitsuAcFanHigh = 0x01;
#define FUJITSU_AC_CMD_TURN_OFF 0x02U const uint8_t kFujitsuAcFanMed = 0x02;
#define FUJITSU_AC_CMD_STEP_HORIZ 0x79U const uint8_t kFujitsuAcFanLow = 0x03;
#define FUJITSU_AC_CMD_STEP_VERT 0x6CU const uint8_t kFujitsuAcFanQuiet = 0x04;
#define FUJITSU_AC_FAN_AUTO 0x00U const uint8_t kFujitsuAcMinTemp = 16; // 16C
#define FUJITSU_AC_FAN_HIGH 0x01U const uint8_t kFujitsuAcMaxTemp = 30; // 30C
#define FUJITSU_AC_FAN_MED 0x02U
#define FUJITSU_AC_FAN_LOW 0x03U
#define FUJITSU_AC_FAN_QUIET 0x04U
#define FUJITSU_AC_MIN_TEMP 16U // 16C const uint8_t kFujitsuAcSwingOff = 0x00;
#define FUJITSU_AC_MAX_TEMP 30U // 30C const uint8_t kFujitsuAcSwingVert = 0x01;
const uint8_t kFujitsuAcSwingHoriz = 0x02;
#define FUJITSU_AC_SWING_OFF 0x00U const uint8_t kFujitsuAcSwingBoth = 0x03;
#define FUJITSU_AC_SWING_VERT 0x01U
#define FUJITSU_AC_SWING_HORIZ 0x02U
#define FUJITSU_AC_SWING_BOTH 0x03U
// Legacy defines.
#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto
#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool
#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry
#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan
#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat
#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn
#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn
#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff
#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz
#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert
#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto
#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh
#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed
#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow
#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet
#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp
#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp
#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff
#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert
#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz
#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth
enum fujitsu_ac_remote_model_t { enum fujitsu_ac_remote_model_t {
ARRAH2E = 1, ARRAH2E = 1,
@@ -57,7 +78,7 @@ class IRFujitsuAC {
void setModel(fujitsu_ac_remote_model_t model); void setModel(fujitsu_ac_remote_model_t model);
void stateReset(); void stateReset();
#if SEND_FUJITSU_AC #if SEND_FUJITSU_AC
void send(); void send(const uint16_t repeat = kFujitsuAcMinRepeat);
#endif // SEND_FUJITSU_AC #endif // SEND_FUJITSU_AC
void begin(); void begin();
void off(); void off();
@@ -85,7 +106,7 @@ class IRFujitsuAC {
#endif #endif
private: private:
uint8_t remote_state[FUJITSU_AC_STATE_LENGTH]; uint8_t remote_state[kFujitsuAcStateLength];
IRsend _irsend; IRsend _irsend;
uint8_t _temp; uint8_t _temp;
uint8_t _fanSpeed; uint8_t _fanSpeed;
+34 -37
View File
@@ -18,17 +18,17 @@
// https://github.com/markszabo/IRremoteESP8266/issues/447 // https://github.com/markszabo/IRremoteESP8266/issues/447
// Constants // Constants
#define GICABLE_HDR_MARK 9000U const uint16_t kGicableHdrMark = 9000;
#define GICABLE_HDR_SPACE 4400U const uint16_t kGicableHdrSpace = 4400;
#define GICABLE_BIT_MARK 550U const uint16_t kGicableBitMark = 550;
#define GICABLE_ONE_SPACE 4400U const uint16_t kGicableOneSpace = 4400;
#define GICABLE_ZERO_SPACE 2200U const uint16_t kGicableZeroSpace = 2200;
#define GICABLE_RPT_SPACE 2200U const uint16_t kGicableRptSpace = 2200;
#define GICABLE_MIN_COMMAND_LENGTH 99600U const uint32_t kGicableMinCommandLength = 99600;
#define GICABLE_MIN_GAP (GICABLE_MIN_COMMAND_LENGTH - \ const uint32_t kGicableMinGap =
(GICABLE_HDR_MARK + GICABLE_HDR_SPACE + \ kGicableMinCommandLength -
GICABLE_BITS * (GICABLE_BIT_MARK + GICABLE_ONE_SPACE) + GICABLE_BIT_MARK)) (kGicableHdrMark + kGicableHdrSpace +
kGicableBits * (kGicableBitMark + kGicableOneSpace) + kGicableBitMark);
#if SEND_GICABLE #if SEND_GICABLE
// Send a raw G.I. Cable formatted message. // Send a raw G.I. Cable formatted message.
@@ -36,25 +36,24 @@
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The number of bits of the message to be sent. // nbits: The number of bits of the message to be sent.
// Typically GICABLE_BITS. // Typically kGicableBits.
// repeat: The number of times the command is to be repeated. // repeat: The number of times the command is to be repeated.
// //
// Status: Alpha / Untested. // Status: Alpha / Untested.
// //
// Ref: // Ref:
void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(GICABLE_HDR_MARK, GICABLE_HDR_SPACE, sendGeneric(kGicableHdrMark, kGicableHdrSpace, kGicableBitMark,
GICABLE_BIT_MARK, GICABLE_ONE_SPACE, kGicableOneSpace, kGicableBitMark, kGicableZeroSpace,
GICABLE_BIT_MARK, GICABLE_ZERO_SPACE, kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, data,
GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH, nbits, 39, true, 0, // Repeats are handled later.
data, nbits, 39, true, 0, // Repeats are handled later.
50); 50);
// Message repeat sequence. // Message repeat sequence.
if (repeat) if (repeat)
sendGeneric(GICABLE_HDR_MARK, GICABLE_RPT_SPACE, sendGeneric(kGicableHdrMark, kGicableRptSpace, 0, 0, 0,
0, 0, 0, 0, // No actual data sent. 0, // No actual data sent.
GICABLE_BIT_MARK, GICABLE_MIN_GAP, GICABLE_MIN_COMMAND_LENGTH, kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, 0,
0, 0, // No data to be sent. 0, // No data to be sent.
39, true, repeat - 1, 50); 39, true, repeat - 1, 50);
} }
#endif // SEND_GICABLE #endif // SEND_GICABLE
@@ -64,7 +63,7 @@ void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically GICABLE_BITS. // nbits: The number of data bits to expect. Typically kGicableBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -72,40 +71,38 @@ void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Status: Alpha / Not tested against a real device. // Status: Alpha / Not tested against a real device.
bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits, bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * (nbits + HEADER + FOOTER) - 1) if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1)
return false; // Can't possibly be a valid GICABLE message. return false; // Can't possibly be a valid GICABLE message.
if (strict && nbits != GICABLE_BITS) if (strict && nbits != kGicableBits)
return false; // Not strictly an GICABLE message. return false; // Not strictly an GICABLE message.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], GICABLE_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset++], kGicableHdrSpace)) return false;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
GICABLE_BIT_MARK, matchData(&(results->rawbuf[offset]), nbits, kGicableBitMark,
GICABLE_ONE_SPACE, kGicableOneSpace, kGicableBitMark, kGicableZeroSpace);
GICABLE_BIT_MARK,
GICABLE_ZERO_SPACE);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], GICABLE_MIN_GAP)) !matchAtLeast(results->rawbuf[offset++], kGicableMinGap))
return false; return false;
// Compliance // Compliance
if (strict) { if (strict) {
// We expect a repeat frame. // We expect a repeat frame.
if (!matchMark(results->rawbuf[offset++], GICABLE_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], GICABLE_RPT_SPACE)) return false; if (!matchSpace(results->rawbuf[offset++], kGicableRptSpace)) return false;
if (!matchMark(results->rawbuf[offset++], GICABLE_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false;
} }
// Success // Success
+15 -16
View File
@@ -14,12 +14,12 @@
// (http://www.hishamkhalifa.com) // (http://www.hishamkhalifa.com)
// Constants // Constants
#define GLOBALCACHE_MAX_REPEAT 50U const uint16_t kGlobalCacheMaxRepeat = 50;
#define GLOBALCACHE_MIN_USEC 80U const uint32_t kGlobalCacheMinUsec = 80;
#define GLOBALCACHE_FREQ_INDEX 0U const uint8_t kGlobalCacheFreqIndex = 0;
#define GLOBALCACHE_RPT_INDEX GLOBALCACHE_FREQ_INDEX + 1U const uint8_t kGlobalCacheRptIndex = kGlobalCacheFreqIndex + 1;
#define GLOBALCACHE_RPT_START_INDEX GLOBALCACHE_RPT_INDEX + 1U const uint8_t kGlobalCacheRptStartIndex = kGlobalCacheRptIndex + 1;
#define GLOBALCACHE_START_INDEX GLOBALCACHE_RPT_START_INDEX + 1U const uint8_t kGlobalCacheStartIndex = kGlobalCacheRptStartIndex + 1;
#if SEND_GLOBALCACHE #if SEND_GLOBALCACHE
// Send a shortened GlobalCache (GC) IRdb/control tower formatted message. // Send a shortened GlobalCache (GC) IRdb/control tower formatted message.
@@ -40,24 +40,23 @@
// Ref: // Ref:
// https://irdb.globalcache.com/Home/Database // https://irdb.globalcache.com/Home/Database
void IRsend::sendGC(uint16_t buf[], uint16_t len) { void IRsend::sendGC(uint16_t buf[], uint16_t len) {
uint16_t hz = buf[GLOBALCACHE_FREQ_INDEX]; // GC frequency is in Hz. uint16_t hz = buf[kGlobalCacheFreqIndex]; // GC frequency is in Hz.
enableIROut(hz); enableIROut(hz);
uint32_t periodic_time = calcUSecPeriod(hz, false); uint32_t periodic_time = calcUSecPeriod(hz, false);
uint8_t emits = std::min(buf[GLOBALCACHE_RPT_INDEX], uint8_t emits =
(uint16_t) GLOBALCACHE_MAX_REPEAT); std::min(buf[kGlobalCacheRptIndex], (uint16_t)kGlobalCacheMaxRepeat);
// Repeat // Repeat
for (uint8_t repeat = 0; repeat < emits; repeat++) { for (uint8_t repeat = 0; repeat < emits; repeat++) {
// First time through, start at the beginning (GLOBALCACHE_START_INDEX), // First time through, start at the beginning (kGlobalCacheStartIndex),
// otherwise for repeats, we start a specified offset from that. // otherwise for repeats, we start a specified offset from that.
uint16_t offset = GLOBALCACHE_START_INDEX; uint16_t offset = kGlobalCacheStartIndex;
if (repeat) if (repeat) offset += buf[kGlobalCacheRptStartIndex] - 1;
offset += buf[GLOBALCACHE_RPT_START_INDEX] - 1;
// Data // Data
for (; offset < len; offset++) { for (; offset < len; offset++) {
// Convert periodic units to microseconds. // Convert periodic units to microseconds.
// Minimum is GLOBALCACHE_MIN_USEC for actual GC units. // Minimum is kGlobalCacheMinUsec for actual GC units.
uint32_t microseconds = std::max(buf[offset] * periodic_time, uint32_t microseconds =
GLOBALCACHE_MIN_USEC); std::max(buf[offset] * periodic_time, kGlobalCacheMinUsec);
// These codes start at an odd index (not even as with sendRaw). // These codes start at an odd index (not even as with sendRaw).
if (offset & 1) // Odd bit. if (offset & 1) // Odd bit.
mark(microseconds); mark(microseconds);
+118 -138
View File
@@ -12,8 +12,8 @@
#ifndef ARDUINO #ifndef ARDUINO
#include <string> #include <string>
#endif #endif
#include "IRremoteESP8266.h"
#include "IRrecv.h" #include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
#include "ir_Kelvinator.h" #include "ir_Kelvinator.h"
@@ -26,21 +26,21 @@
// Constants // Constants
// Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h // Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h
#define GREE_HDR_MARK 9000U const uint16_t kGreeHdrMark = 9000;
#define GREE_HDR_SPACE 4000U const uint16_t kGreeHdrSpace = 4000;
#define GREE_BIT_MARK 620U const uint16_t kGreeBitMark = 620;
#define GREE_ONE_SPACE 1600U const uint16_t kGreeOneSpace = 1600;
#define GREE_ZERO_SPACE 540U const uint16_t kGreeZeroSpace = 540;
#define GREE_MSG_SPACE 19000U const uint16_t kGreeMsgSpace = 19000;
#define GREE_BLOCK_FOOTER 0b010U const uint8_t kGreeBlockFooter = 0b010;
#define GREE_BLOCK_FOOTER_BITS 3U const uint8_t kGreeBlockFooterBits = 3;
#if SEND_GREE #if SEND_GREE
// Send a Gree Heat Pump message. // Send a Gree Heat Pump message.
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=GREE_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kGreeStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
@@ -48,29 +48,24 @@
// Ref: // Ref:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
if (nbytes < GREE_STATE_LENGTH) if (nbytes < kGreeStateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
// Block #1 // Block #1
sendGeneric(GREE_HDR_MARK, GREE_HDR_SPACE, sendGeneric(kGreeHdrMark, kGreeHdrSpace, kGreeBitMark, kGreeOneSpace,
GREE_BIT_MARK, GREE_ONE_SPACE, kGreeBitMark, kGreeZeroSpace, 0, 0, // No Footer.
GREE_BIT_MARK, GREE_ZERO_SPACE,
0, 0, // No Footer.
data, 4, 38, false, 0, 50); data, 4, 38, false, 0, 50);
// Footer #1 // Footer #1
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
GREE_BIT_MARK, GREE_ONE_SPACE, kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
GREE_BIT_MARK, GREE_ZERO_SPACE, kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false);
GREE_BIT_MARK, GREE_MSG_SPACE,
0b010, 3, 38, true, 0, false);
// Block #2 // Block #2
sendGeneric(0, 0, // No Header for Block #2 sendGeneric(0, 0, // No Header for Block #2
GREE_BIT_MARK, GREE_ONE_SPACE, kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
GREE_BIT_MARK, GREE_ZERO_SPACE, kGreeBitMark, kGreeMsgSpace, data + 4, nbytes - 4, 38, false, 0,
GREE_BIT_MARK, GREE_MSG_SPACE, 50);
data + 4, nbytes - 4, 38, false, 0, 50);
} }
} }
@@ -78,7 +73,7 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
// //
// Args: // Args:
// data: The raw message to be sent. // data: The raw message to be sent.
// nbits: Nr. of bits of data in the message. (Default is GREE_BITS) // nbits: Nr. of bits of data in the message. (Default is kGreeBits)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
@@ -86,43 +81,40 @@ void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) {
// Ref: // Ref:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp // https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp
void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits != GREE_BITS) if (nbits != kGreeBits)
return; // Wrong nr. of bits to send a proper message. return; // Wrong nr. of bits to send a proper message.
// Set IR carrier frequency // Set IR carrier frequency
enableIROut(38); enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
// Header // Header
mark(GREE_HDR_MARK); mark(kGreeHdrMark);
space(GREE_HDR_SPACE); space(kGreeHdrSpace);
// Data // Data
for (int16_t i = 8; i <= nbits; i += 8) { for (int16_t i = 8; i <= nbits; i += 8) {
sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE, sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
(data >> (nbits - i)) & 0xFF, 8, false); (data >> (nbits - i)) & 0xFF, 8, false);
if (i == nbits / 2) { if (i == nbits / 2) {
// Send the mid-message Footer. // Send the mid-message Footer.
sendData(GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE, sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
0b010, 3); 0b010, 3);
mark(GREE_BIT_MARK); mark(kGreeBitMark);
space(GREE_MSG_SPACE); space(kGreeMsgSpace);
} }
} }
// Footer // Footer
mark(GREE_BIT_MARK); mark(kGreeBitMark);
space(GREE_MSG_SPACE); space(kGreeMsgSpace);
} }
} }
#endif // SEND_GREE #endif // SEND_GREE
IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
void IRGreeAC::stateReset() { void IRGreeAC::stateReset() {
// This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C. // This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C.
for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++) for (uint8_t i = 0; i < kGreeStateLength; i++) remote_state[i] = 0x0;
remote_state[i] = 0x0;
remote_state[1] = 0x09; remote_state[1] = 0x09;
remote_state[2] = 0x20; remote_state[2] = 0x20;
remote_state[3] = 0x50; remote_state[3] = 0x50;
@@ -134,14 +126,12 @@ void IRGreeAC::fixup() {
checksum(); // Calculate the checksums checksum(); // Calculate the checksums
} }
void IRGreeAC::begin() { void IRGreeAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_GREE #if SEND_GREE
void IRGreeAC::send() { void IRGreeAC::send(const uint16_t repeat) {
fixup(); // Ensure correct settings before sending. fixup(); // Ensure correct settings before sending.
_irsend.sendGree(remote_state); _irsend.sendGree(remote_state, kGreeStateLength, repeat);
} }
#endif // SEND_GREE #endif // SEND_GREE
@@ -151,7 +141,7 @@ uint8_t* IRGreeAC::getRaw() {
} }
void IRGreeAC::setRaw(uint8_t new_code[]) { void IRGreeAC::setRaw(uint8_t new_code[]) {
for (uint8_t i = 0; i < GREE_STATE_LENGTH; i++) { for (uint8_t i = 0; i < kGreeStateLength; i++) {
remote_state[i] = new_code[i]; remote_state[i] = new_code[i];
} }
} }
@@ -170,21 +160,21 @@ void IRGreeAC::checksum(const uint16_t length) {
// A boolean. // A boolean.
bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) {
// Top 4 bits of the last byte in the state is the state's checksum. // Top 4 bits of the last byte in the state is the state's checksum.
if (state[length - 1] >> 4 == IRKelvinatorAC::calcBlockChecksum(state, if (state[length - 1] >> 4 ==
length)) IRKelvinatorAC::calcBlockChecksum(state, length))
return true; return true;
else else
return false; return false;
} }
void IRGreeAC::on() { void IRGreeAC::on() {
remote_state[0] |= GREE_POWER1_MASK; remote_state[0] |= kGreePower1Mask;
remote_state[2] |= GREE_POWER2_MASK; remote_state[2] |= kGreePower2Mask;
} }
void IRGreeAC::off() { void IRGreeAC::off() {
remote_state[0] &= ~GREE_POWER1_MASK; remote_state[0] &= ~kGreePower1Mask;
remote_state[2] &= ~GREE_POWER2_MASK; remote_state[2] &= ~kGreePower2Mask;
} }
void IRGreeAC::setPower(const bool state) { void IRGreeAC::setPower(const bool state) {
@@ -195,136 +185,124 @@ void IRGreeAC::setPower(const bool state) {
} }
bool IRGreeAC::getPower() { bool IRGreeAC::getPower() {
return (remote_state[0] & GREE_POWER1_MASK) && return (remote_state[0] & kGreePower1Mask) &&
(remote_state[2] & GREE_POWER2_MASK); (remote_state[2] & kGreePower2Mask);
} }
// Set the temp. in deg C // Set the temp. in deg C
void IRGreeAC::setTemp(const uint8_t temp) { void IRGreeAC::setTemp(const uint8_t temp) {
uint8_t new_temp = std::max((uint8_t) GREE_MIN_TEMP, temp); uint8_t new_temp = std::max((uint8_t)kGreeMinTemp, temp);
new_temp = std::min((uint8_t) GREE_MAX_TEMP, new_temp); new_temp = std::min((uint8_t)kGreeMaxTemp, new_temp);
if (getMode() == GREE_AUTO) new_temp = 25; if (getMode() == kGreeAuto) new_temp = 25;
remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - GREE_MIN_TEMP); remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - kGreeMinTemp);
} }
// Return the set temp. in deg C // Return the set temp. in deg C
uint8_t IRGreeAC::getTemp() { uint8_t IRGreeAC::getTemp() {
return ((remote_state[1] & 0xFU) + GREE_MIN_TEMP); return ((remote_state[1] & 0xFU) + kGreeMinTemp);
} }
// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed // Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed
void IRGreeAC::setFan(const uint8_t speed) { void IRGreeAC::setFan(const uint8_t speed) {
uint8_t fan = std::min((uint8_t) GREE_FAN_MAX, speed); // Bounds check uint8_t fan = std::min((uint8_t)kGreeFanMax, speed); // Bounds check
if (getMode() == GREE_DRY) fan = 1; // DRY mode is always locked to fan 1. if (getMode() == kGreeDry) fan = 1; // DRY mode is always locked to fan 1.
// Set the basic fan values. // Set the basic fan values.
remote_state[0] &= ~GREE_FAN_MASK; remote_state[0] &= ~kGreeFanMask;
remote_state[0] |= (fan << 4); remote_state[0] |= (fan << 4);
} }
uint8_t IRGreeAC::getFan() { uint8_t IRGreeAC::getFan() { return ((remote_state[0] & kGreeFanMask) >> 4); }
return ((remote_state[0] & GREE_FAN_MASK) >> 4);
}
void IRGreeAC::setMode(const uint8_t new_mode) { void IRGreeAC::setMode(const uint8_t new_mode) {
uint8_t mode = new_mode; uint8_t mode = new_mode;
switch (mode) { switch (mode) {
case GREE_AUTO: case kGreeAuto:
// AUTO is locked to 25C // AUTO is locked to 25C
setTemp(25); setTemp(25);
break; break;
case GREE_DRY: case kGreeDry:
// DRY always sets the fan to 1. // DRY always sets the fan to 1.
setFan(1); setFan(1);
break; break;
case GREE_COOL: case kGreeCool:
case GREE_FAN: case kGreeFan:
case GREE_HEAT: case kGreeHeat:
break; break;
default: default:
// If we get an unexpected mode, default to AUTO. // If we get an unexpected mode, default to AUTO.
mode = GREE_AUTO; mode = kGreeAuto;
} }
remote_state[0] &= ~GREE_MODE_MASK; remote_state[0] &= ~kGreeModeMask;
remote_state[0] |= mode; remote_state[0] |= mode;
} }
uint8_t IRGreeAC::getMode() { uint8_t IRGreeAC::getMode() { return (remote_state[0] & kGreeModeMask); }
return (remote_state[0] & GREE_MODE_MASK);
}
void IRGreeAC::setLight(const bool state) { void IRGreeAC::setLight(const bool state) {
remote_state[2] &= ~GREE_LIGHT_MASK; remote_state[2] &= ~kGreeLightMask;
remote_state[2] |= (state << 5); remote_state[2] |= (state << 5);
} }
bool IRGreeAC::getLight() { bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; }
return remote_state[2] & GREE_LIGHT_MASK;
}
void IRGreeAC::setXFan(const bool state) { void IRGreeAC::setXFan(const bool state) {
remote_state[2] &= ~GREE_XFAN_MASK; remote_state[2] &= ~kGreeXfanMask;
remote_state[2] |= (state << 7); remote_state[2] |= (state << 7);
} }
bool IRGreeAC::getXFan() { bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; }
return remote_state[2] & GREE_XFAN_MASK;
}
void IRGreeAC::setSleep(const bool state) { void IRGreeAC::setSleep(const bool state) {
remote_state[0] &= ~GREE_SLEEP_MASK; remote_state[0] &= ~kGreeSleepMask;
remote_state[0] |= (state << 7); remote_state[0] |= (state << 7);
} }
bool IRGreeAC::getSleep() { bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; }
return remote_state[0] & GREE_SLEEP_MASK;
}
void IRGreeAC::setTurbo(const bool state) { void IRGreeAC::setTurbo(const bool state) {
remote_state[2] &= ~GREE_TURBO_MASK; remote_state[2] &= ~kGreeTurboMask;
remote_state[2] |= (state << 4); remote_state[2] |= (state << 4);
} }
bool IRGreeAC::getTurbo() { bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; }
return remote_state[2] & GREE_TURBO_MASK;
}
void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
remote_state[0] &= ~GREE_SWING_AUTO_MASK; remote_state[0] &= ~kGreeSwingAutoMask;
remote_state[0] |= (automatic << 6); remote_state[0] |= (automatic << 6);
uint8_t new_position = position; uint8_t new_position = position;
if (!automatic) { if (!automatic) {
switch (position) { switch (position) {
case GREE_SWING_UP: case kGreeSwingUp:
case GREE_SWING_MIDDLE_UP: case kGreeSwingMiddleUp:
case GREE_SWING_MIDDLE: case kGreeSwingMiddle:
case GREE_SWING_MIDDLE_DOWN: case kGreeSwingMiddleDown:
case GREE_SWING_DOWN: case kGreeSwingDown:
break; break;
default: default:
new_position = GREE_SWING_LAST_POS; new_position = kGreeSwingLastPos;
} }
} else { } else {
switch (position) { switch (position) {
case GREE_SWING_AUTO: case kGreeSwingAuto:
case GREE_SWING_DOWN_AUTO: case kGreeSwingDownAuto:
case GREE_SWING_MIDDLE_AUTO: case kGreeSwingMiddleAuto:
case GREE_SWING_UP_AUTO: case kGreeSwingUpAuto:
break; break;
default: default:
new_position = GREE_SWING_AUTO; new_position = kGreeSwingAuto;
} }
} }
remote_state[4] &= ~GREE_SWING_POS_MASK; remote_state[4] &= ~kGreeSwingPosMask;
remote_state[4] |= new_position; remote_state[4] |= new_position;
} }
bool IRGreeAC::getSwingVerticalAuto() { bool IRGreeAC::getSwingVerticalAuto() {
return remote_state[0] & GREE_SWING_AUTO_MASK; return remote_state[0] & kGreeSwingAutoMask;
} }
uint8_t IRGreeAC::getSwingVerticalPosition() { uint8_t IRGreeAC::getSwingVerticalPosition() {
return remote_state[4] & GREE_SWING_POS_MASK; return remote_state[4] & kGreeSwingPosMask;
} }
// Convert the internal state into a human readable string. // Convert the internal state into a human readable string.
@@ -342,19 +320,19 @@ std::string IRGreeAC::toString() {
result += "Off"; result += "Off";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case GREE_AUTO: case kGreeAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case GREE_COOL: case kGreeCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case GREE_HEAT: case kGreeHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case GREE_DRY: case kGreeDry:
result += " (DRY)"; result += " (DRY)";
break; break;
case GREE_FAN: case kGreeFan:
result += " (FAN)"; result += " (FAN)";
break; break;
default: default:
@@ -366,7 +344,7 @@ std::string IRGreeAC::toString() {
case 0: case 0:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case GREE_FAN_MAX: case kGreeFanMax:
result += " (MAX)"; result += " (MAX)";
break; break;
} }
@@ -395,13 +373,13 @@ std::string IRGreeAC::toString() {
result += "Auto"; result += "Auto";
else else
result += "Manual"; result += "Manual";
result += ", Swing Vertical Pos: " + result +=
uint64ToString(getSwingVerticalPosition()); ", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition());
switch (getSwingVerticalPosition()) { switch (getSwingVerticalPosition()) {
case GREE_SWING_LAST_POS: case kGreeSwingLastPos:
result += " (Last Pos)"; result += " (Last Pos)";
break; break;
case GREE_SWING_AUTO: case kGreeSwingAuto:
result += " (Auto)"; result += " (Auto)";
break; break;
} }
@@ -413,21 +391,21 @@ std::string IRGreeAC::toString() {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically GREE_BITS. // nbits: The number of data bits to expect. Typically kGreeBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
// //
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) { bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * (nbits + GREE_BLOCK_FOOTER_BITS) + if (results->rawlen <
(HEADER + FOOTER + 1)) 2 * (nbits + kGreeBlockFooterBits) + (kHeader + kFooter + 1))
return false; // Can't possibly be a valid Gree message. return false; // Can't possibly be a valid Gree message.
if (strict && nbits != GREE_BITS) if (strict && nbits != kGreeBits)
return false; // Not strictly a Gree message. return false; // Not strictly a Gree message.
uint32_t data; uint32_t data;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// There are two blocks back-to-back in a full Gree IR message // There are two blocks back-to-back in a full Gree IR message
// sequence. // sequence.
@@ -435,54 +413,56 @@ bool IRrecv::decodeGree(decode_results *results, uint16_t nbits, bool strict) {
match_result_t data_result; match_result_t data_result;
// Header // Header
if (!matchMark(results->rawbuf[offset++], GREE_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGreeHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], GREE_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset++], kGreeHdrSpace)) return false;
// Data Block #1 (32 bits) // Data Block #1 (32 bits)
data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK, data_result =
GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE); matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace,
kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Record Data Block #1 in the state. // Record Data Block #1 in the state.
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) for (uint16_t i = 0; i < 4; i++, data >>= 8)
results->state[i] = reverseBits(data & 0xFF, 8); results->state[state_pos + i] = data & 0xFF;
state_pos += 4; state_pos += 4;
// Block #1 footer (3 bits, B010) // Block #1 footer (3 bits, B010)
data_result = matchData(&(results->rawbuf[offset]), GREE_BLOCK_FOOTER_BITS, data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits,
GREE_BIT_MARK, GREE_ONE_SPACE, GREE_BIT_MARK, kGreeBitMark, kGreeOneSpace, kGreeBitMark,
GREE_ZERO_SPACE); kGreeZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
if (data_result.data != GREE_BLOCK_FOOTER) return false; if (data_result.data != kGreeBlockFooter) return false;
offset += data_result.used; offset += data_result.used;
// Inter-block gap. // Inter-block gap.
if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false;
if (!matchSpace(results->rawbuf[offset++], GREE_MSG_SPACE)) return false; if (!matchSpace(results->rawbuf[offset++], kGreeMsgSpace)) return false;
// Data Block #2 (32 bits) // Data Block #2 (32 bits)
data_result = matchData(&(results->rawbuf[offset]), 32, GREE_BIT_MARK, data_result =
GREE_ONE_SPACE, GREE_BIT_MARK, GREE_ZERO_SPACE); matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace,
kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Record Data Block #2 in the state. // Record Data Block #2 in the state.
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) for (uint16_t i = 0; i < 4; i++, data >>= 8)
results->state[i] = reverseBits(data & 0xFF, 8); results->state[state_pos + i] = data & 0xFF;
state_pos += 4; state_pos += 4;
// Footer. // Footer.
if (!matchMark(results->rawbuf[offset++], GREE_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false;
if (offset <= results->rawlen && if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], GREE_MSG_SPACE)) !matchAtLeast(results->rawbuf[offset], kGreeMsgSpace))
return false; return false;
// Compliance // Compliance
if (strict) { if (strict) {
// Correct size/length) // Correct size/length)
if (state_pos != GREE_STATE_LENGTH) return false; if (state_pos != kGreeStateLength) return false;
// Verify the message's checksum is correct. // Verify the message's checksum is correct.
if (!IRGreeAC::validChecksum(results->state)) return false; if (!IRGreeAC::validChecksum(results->state)) return false;
} }
+56 -32
View File
@@ -22,36 +22,60 @@
// GGGGGG RR RR EEEEEEE EEEEEEE // GGGGGG RR RR EEEEEEE EEEEEEE
// Constants // Constants
#define GREE_AUTO 0U const uint8_t kGreeAuto = 0;
#define GREE_COOL 1U const uint8_t kGreeCool = 1;
#define GREE_DRY 2U const uint8_t kGreeDry = 2;
#define GREE_FAN 3U const uint8_t kGreeFan = 3;
#define GREE_HEAT 4U const uint8_t kGreeHeat = 4;
#define GREE_POWER1_MASK 0b00001000U // Byte 0
#define GREE_POWER2_MASK 0b01000000U const uint8_t kGreeModeMask = 0b00000111;
#define GREE_MIN_TEMP 16U // Celsius const uint8_t kGreePower1Mask = 0b00001000;
#define GREE_MAX_TEMP 30U // Celsius const uint8_t kGreeFanMask = 0b00110000;
#define GREE_FAN_MAX 3U const uint8_t kGreeSwingAutoMask = 0b01000000;
#define GREE_FAN_MASK 0b00110000U const uint8_t kGreeSleepMask = 0b10000000;
#define GREE_MODE_MASK 0b00000111U // Byte 2
#define GREE_TURBO_MASK 0b00010000U const uint8_t kGreeTurboMask = 0b00010000;
#define GREE_LIGHT_MASK 0b00100000U const uint8_t kGreeLightMask = 0b00100000;
#define GREE_XFAN_MASK 0b10000000U const uint8_t kGreePower2Mask = 0b01000000;
#define GREE_SLEEP_MASK 0b10000000U const uint8_t kGreeXfanMask = 0b10000000;
// Byte 4
const uint8_t kGreeSwingPosMask = 0b00001111;
#define GREE_SWING_AUTO_MASK 0b01000000U const uint8_t kGreeMinTemp = 16; // Celsius
#define GREE_SWING_POS_MASK 0b00001111U const uint8_t kGreeMaxTemp = 30; // Celsius
#define GREE_SWING_LAST_POS 0b00000000U const uint8_t kGreeFanMax = 3;
#define GREE_SWING_AUTO 0b00000001U
#define GREE_SWING_UP 0b00000010U const uint8_t kGreeSwingLastPos = 0b00000000;
#define GREE_SWING_MIDDLE_UP 0b00000011U const uint8_t kGreeSwingAuto = 0b00000001;
#define GREE_SWING_MIDDLE 0b00000100U const uint8_t kGreeSwingUp = 0b00000010;
#define GREE_SWING_MIDDLE_DOWN 0b00000101U const uint8_t kGreeSwingMiddleUp = 0b00000011;
#define GREE_SWING_DOWN 0b00000110U const uint8_t kGreeSwingMiddle = 0b00000100;
#define GREE_SWING_DOWN_AUTO 0b00000111U const uint8_t kGreeSwingMiddleDown = 0b00000101;
#define GREE_SWING_MIDDLE_AUTO 0b00001001U const uint8_t kGreeSwingDown = 0b00000110;
#define GREE_SWING_UP_AUTO 0b00001011U const uint8_t kGreeSwingDownAuto = 0b00000111;
const uint8_t kGreeSwingMiddleAuto = 0b00001001;
const uint8_t kGreeSwingUpAuto = 0b00001011;
// Legacy defines.
#define GREE_AUTO kGreeAuto
#define GREE_COOL kGreeCool
#define GREE_DRY kGreeDry
#define GREE_FAN kGreeFan
#define GREE_HEAT kGreeHeat
#define GREE_MIN_TEMP kGreeMinTemp
#define GREE_MAX_TEMP kGreeMaxTemp
#define GREE_FAN_MAX kGreeFanMax
#define GREE_SWING_LAST_POS kGreeSwingLastPos
#define GREE_SWING_AUTO kGreeSwingAuto
#define GREE_SWING_UP kGreeSwingUp
#define GREE_SWING_MIDDLE_UP kGreeSwingMiddleUp
#define GREE_SWING_MIDDLE kGreeSwingMiddle
#define GREE_SWING_MIDDLE_DOWN kGreeSwingMiddleDown
#define GREE_SWING_DOWN kGreeSwingDown
#define GREE_SWING_DOWN_AUTO kGreeSwingDownAuto
#define GREE_SWING_MIDDLE_AUTO kGreeSwingMiddleAuto
#define GREE_SWING_UP_AUTO kGreeSwingUpAuto
// Classes // Classes
class IRGreeAC { class IRGreeAC {
@@ -60,7 +84,7 @@ class IRGreeAC {
void stateReset(); void stateReset();
#if SEND_GREE #if SEND_GREE
void send(); void send(const uint16_t repeat = kGreeDefaultRepeat);
#endif // SEND_GREE #endif // SEND_GREE
void begin(); void begin();
void on(); void on();
@@ -88,7 +112,7 @@ class IRGreeAC {
uint8_t* getRaw(); uint8_t* getRaw();
void setRaw(uint8_t new_code[]); void setRaw(uint8_t new_code[]);
static bool validChecksum(const uint8_t state[], static bool validChecksum(const uint8_t state[],
const uint16_t length = GREE_STATE_LENGTH); const uint16_t length = kGreeStateLength);
#ifdef ARDUINO #ifdef ARDUINO
String toString(); String toString();
#else #else
@@ -97,8 +121,8 @@ class IRGreeAC {
private: private:
// The state of the IR remote in IR code form. // The state of the IR remote in IR code form.
uint8_t remote_state[GREE_STATE_LENGTH]; uint8_t remote_state[kGreeStateLength];
void checksum(const uint16_t length = GREE_STATE_LENGTH); void checksum(const uint16_t length = kGreeStateLength);
void fixup(); void fixup();
IRsend _irsend; IRsend _irsend;
}; };
+511 -143
View File
@@ -1,5 +1,7 @@
// Copyright 2018 crankyoldgit // Copyright 2018 crankyoldgit
// The specifics of reverse engineering the protocol details by kuzin2006 // The specifics of reverse engineering the protocols details:
// * HSU07-HEA03 by kuzin2006.
// * YR-W02/HSU-09HMC203 by non7top.
#include "ir_Haier.h" #include "ir_Haier.h"
#ifndef UNIT_TEST #ifndef UNIT_TEST
@@ -18,66 +20,80 @@
// Supported devices: // Supported devices:
// * Haier HSU07-HEA03 Remote control. // * Haier HSU07-HEA03 Remote control.
// * Haier YR-W02 Remote control
// * Haier HSU-09HMC203 A/C unit.
// Ref: // Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/404 // https://github.com/markszabo/IRremoteESP8266/issues/404
// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
// https://github.com/markszabo/IRremoteESP8266/issues/485
// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
// Constants // Constants
#define HAIER_AC_HDR 3000U const uint16_t kHaierAcHdr = 3000;
#define HAIER_AC_HDR_GAP 4300U const uint16_t kHaierAcHdrGap = 4300;
const uint16_t kHaierAcBitMark = 520;
const uint16_t kHaierAcOneSpace = 1650;
const uint16_t kHaierAcZeroSpace = 650;
const uint32_t kHaierAcMinGap = 150000; // Completely made up value.
#define HAIER_AC_BIT_MARK 520U #if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
#define HAIER_AC_ONE_SPACE 1650U // Send a Haier A/C message. (HSU07-HEA03 remote)
#define HAIER_AC_ZERO_SPACE 650U
#define HAIER_AC_MIN_GAP 150000U // Completely made up value.
#if SEND_HAIER_AC
// Send a Haier A/C message.
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HAIER_AC_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: Beta / Probably working. // Status: Beta / Probably working.
// //
void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < HAIER_AC_STATE_LENGTH) if (nbytes < kHaierACStateLength) return;
return;
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
enableIROut(38000); enableIROut(38000);
mark(HAIER_AC_HDR); mark(kHaierAcHdr);
space(HAIER_AC_HDR); space(kHaierAcHdr);
sendGeneric(HAIER_AC_HDR, HAIER_AC_HDR_GAP, sendGeneric(kHaierAcHdr, kHaierAcHdrGap, kHaierAcBitMark, kHaierAcOneSpace,
HAIER_AC_BIT_MARK, HAIER_AC_ONE_SPACE, kHaierAcBitMark, kHaierAcZeroSpace, kHaierAcBitMark,
HAIER_AC_BIT_MARK, HAIER_AC_ZERO_SPACE, kHaierAcMinGap, data, nbytes, 38, true,
HAIER_AC_BIT_MARK, HAIER_AC_MIN_GAP, 0, // Repeats handled elsewhere
data, nbytes, 38, true, 0, // Repeats handled elsewhere
50); 50);
} }
} }
#endif // SEND_HAIER_AC #endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02)
IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { #if SEND_HAIER_AC_YRW02
stateReset(); // Send a Haier YR-W02 remote A/C message.
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=kHaierACYRW02StateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: Alpha / Untested on a real device.
//
void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat);
} }
#endif // SEND_HAIER_AC_YRW02
void IRHaierAC::begin() { // Class for emulating a Haier HSU07-HEA03 remote
_irsend.begin(); IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); }
}
void IRHaierAC::begin() { _irsend.begin(); }
#if SEND_HAIER_AC #if SEND_HAIER_AC
void IRHaierAC::send() { void IRHaierAC::send(const uint16_t repeat) {
checksum(); checksum();
_irsend.sendHaierAC(remote_state); _irsend.sendHaierAC(remote_state, kHaierACStateLength, repeat);
} }
#endif // SEND_HAIER_AC #endif // SEND_HAIER_AC
void IRHaierAC::checksum() { void IRHaierAC::checksum() {
remote_state[8] = sumBytes(remote_state, HAIER_AC_STATE_LENGTH - 1); remote_state[8] = sumBytes(remote_state, kHaierACStateLength - 1);
} }
bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) {
@@ -86,15 +102,14 @@ bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) {
} }
void IRHaierAC::stateReset() { void IRHaierAC::stateReset() {
for (uint8_t i = 1; i < HAIER_AC_STATE_LENGTH; i++) for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0;
remote_state[i] = 0x0; remote_state[0] = kHaierAcPrefix;
remote_state[0] = HAIER_AC_PREFIX;
remote_state[2] = 0b00100000; remote_state[2] = 0b00100000;
setTemp(HAIER_AC_DEF_TEMP); setTemp(kHaierAcDefTemp);
setFan(HAIER_AC_FAN_AUTO); setFan(kHaierAcFanAuto);
setMode(HAIER_AC_AUTO); setMode(kHaierAcAuto);
setCommand(HAIER_AC_CMD_ON); setCommand(kHaierAcCmdOn);
} }
uint8_t* IRHaierAC::getRaw() { uint8_t* IRHaierAC::getRaw() {
@@ -103,7 +118,7 @@ uint8_t* IRHaierAC::getRaw() {
} }
void IRHaierAC::setRaw(uint8_t new_code[]) { void IRHaierAC::setRaw(uint8_t new_code[]) {
for (uint8_t i = 0; i < HAIER_AC_STATE_LENGTH; i++) { for (uint8_t i = 0; i < kHaierACStateLength; i++) {
remote_state[i] = new_code[i]; remote_state[i] = new_code[i];
} }
} }
@@ -111,42 +126,40 @@ void IRHaierAC::setRaw(uint8_t new_code[]) {
void IRHaierAC::setCommand(uint8_t state) { void IRHaierAC::setCommand(uint8_t state) {
remote_state[1] &= 0b11110000; remote_state[1] &= 0b11110000;
switch (state) { switch (state) {
case HAIER_AC_CMD_OFF: case kHaierAcCmdOff:
case HAIER_AC_CMD_ON: case kHaierAcCmdOn:
case HAIER_AC_CMD_MODE: case kHaierAcCmdMode:
case HAIER_AC_CMD_FAN: case kHaierAcCmdFan:
case HAIER_AC_CMD_TEMP_UP: case kHaierAcCmdTempUp:
case HAIER_AC_CMD_TEMP_DOWN: case kHaierAcCmdTempDown:
case HAIER_AC_CMD_SLEEP: case kHaierAcCmdSleep:
case HAIER_AC_CMD_TIMER_SET: case kHaierAcCmdTimerSet:
case HAIER_AC_CMD_TIMER_CANCEL: case kHaierAcCmdTimerCancel:
case HAIER_AC_CMD_HEALTH: case kHaierAcCmdHealth:
case HAIER_AC_CMD_SWING: case kHaierAcCmdSwing:
remote_state[1] |= (state & 0b00001111); remote_state[1] |= (state & 0b00001111);
} }
} }
uint8_t IRHaierAC::getCommand() { uint8_t IRHaierAC::getCommand() { return remote_state[1] & (0b00001111); }
return remote_state[1] & (0b00001111);
}
void IRHaierAC::setFan(uint8_t speed) { void IRHaierAC::setFan(uint8_t speed) {
uint8_t new_speed = HAIER_AC_FAN_AUTO; uint8_t new_speed = kHaierAcFanAuto;
switch (speed) { switch (speed) {
case HAIER_AC_FAN_LOW: case kHaierAcFanLow:
new_speed = 3; new_speed = 3;
break; break;
case HAIER_AC_FAN_MED: case kHaierAcFanMed:
new_speed = 1; new_speed = 1;
break; break;
case HAIER_AC_FAN_HIGH: case kHaierAcFanHigh:
new_speed = 2; new_speed = 2;
break; break;
default: default:
new_speed = HAIER_AC_FAN_AUTO; // Default to auto for anything else. new_speed = kHaierAcFanAuto; // Default to auto for anything else.
} }
if (speed != getFan()) setCommand(HAIER_AC_CMD_FAN); if (speed != getFan()) setCommand(kHaierAcCmdFan);
remote_state[5] &= 0b11111100; remote_state[5] &= 0b11111100;
remote_state[5] |= new_speed; remote_state[5] |= new_speed;
} }
@@ -154,70 +167,64 @@ void IRHaierAC::setFan(uint8_t speed) {
uint8_t IRHaierAC::getFan() { uint8_t IRHaierAC::getFan() {
switch (remote_state[5] & 0b00000011) { switch (remote_state[5] & 0b00000011) {
case 1: case 1:
return HAIER_AC_FAN_MED; return kHaierAcFanMed;
case 2: case 2:
return HAIER_AC_FAN_HIGH; return kHaierAcFanHigh;
case 3: case 3:
return HAIER_AC_FAN_LOW; return kHaierAcFanLow;
default: default:
return HAIER_AC_FAN_AUTO; return kHaierAcFanAuto;
} }
} }
void IRHaierAC::setMode(uint8_t mode) { void IRHaierAC::setMode(uint8_t mode) {
uint8_t new_mode = mode; uint8_t new_mode = mode;
setCommand(HAIER_AC_CMD_MODE); setCommand(kHaierAcCmdMode);
if (mode > HAIER_AC_FAN) // If out of range, default to auto mode. if (mode > kHaierAcFan) // If out of range, default to auto mode.
new_mode = HAIER_AC_AUTO; new_mode = kHaierAcAuto;
remote_state[7] &= 0b00011111; remote_state[7] &= 0b00011111;
remote_state[7] |= (new_mode << 5); remote_state[7] |= (new_mode << 5);
} }
uint8_t IRHaierAC::getMode() { uint8_t IRHaierAC::getMode() { return (remote_state[7] & 0b11100000) >> 5; }
return (remote_state[7] & 0b11100000) >> 5;
}
void IRHaierAC::setTemp(const uint8_t celcius ) { void IRHaierAC::setTemp(const uint8_t celsius) {
uint8_t temp = celcius; uint8_t temp = celsius;
if (temp < HAIER_AC_MIN_TEMP) if (temp < kHaierAcMinTemp)
temp = HAIER_AC_MIN_TEMP; temp = kHaierAcMinTemp;
else if (temp > HAIER_AC_MAX_TEMP) else if (temp > kHaierAcMaxTemp)
temp = HAIER_AC_MAX_TEMP; temp = kHaierAcMaxTemp;
uint8_t old_temp = getTemp(); uint8_t old_temp = getTemp();
if (old_temp == temp) return; if (old_temp == temp) return;
if (old_temp > temp) if (old_temp > temp)
setCommand(HAIER_AC_CMD_TEMP_DOWN); setCommand(kHaierAcCmdTempDown);
else else
setCommand(HAIER_AC_CMD_TEMP_UP); setCommand(kHaierAcCmdTempUp);
remote_state[1] &= 0b00001111; // Clear the previous temp. remote_state[1] &= 0b00001111; // Clear the previous temp.
remote_state[1] |= ((temp - HAIER_AC_MIN_TEMP) << 4); remote_state[1] |= ((temp - kHaierAcMinTemp) << 4);
} }
uint8_t IRHaierAC::getTemp() { uint8_t IRHaierAC::getTemp() {
return ((remote_state[1] & 0b11110000) >> 4) + HAIER_AC_MIN_TEMP; return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp;
} }
void IRHaierAC::setHealth(bool state) { void IRHaierAC::setHealth(bool state) {
setCommand(HAIER_AC_CMD_HEALTH); setCommand(kHaierAcCmdHealth);
remote_state[4] &= 0b11011111; remote_state[4] &= 0b11011111;
remote_state[4] |= (state << 5); remote_state[4] |= (state << 5);
} }
bool IRHaierAC::getHealth(void) { bool IRHaierAC::getHealth(void) { return remote_state[4] & (1 << 5); }
return remote_state[4] & (1 << 5);
}
void IRHaierAC::setSleep(bool state) { void IRHaierAC::setSleep(bool state) {
setCommand(HAIER_AC_CMD_SLEEP); setCommand(kHaierAcCmdSleep);
remote_state[7] &= 0b10111111; remote_state[7] &= 0b10111111;
remote_state[7] |= (state << 6); remote_state[7] |= (state << 6);
} }
bool IRHaierAC::getSleep(void) { bool IRHaierAC::getSleep(void) { return remote_state[7] & 0b01000000; }
return remote_state[7] & 0b01000000;
}
uint16_t IRHaierAC::getTime(const uint8_t ptr[]) { uint16_t IRHaierAC::getTime(const uint8_t ptr[]) {
return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111); return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111);
@@ -237,14 +244,11 @@ int16_t IRHaierAC::getOffTimer() {
return -1; return -1;
} }
uint16_t IRHaierAC::getCurrTime() { uint16_t IRHaierAC::getCurrTime() { return getTime(remote_state + 2); }
return getTime(remote_state + 2);
}
void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) { void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) {
uint16_t mins = nr_mins; uint16_t mins = nr_mins;
if (nr_mins > HAIER_AC_MAX_TIME) if (nr_mins > kHaierAcMaxTime) mins = kHaierAcMaxTime;
mins = HAIER_AC_MAX_TIME;
// Hours // Hours
ptr[0] &= 0b11100000; ptr[0] &= 0b11100000;
@@ -255,19 +259,19 @@ void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) {
} }
void IRHaierAC::setOnTimer(const uint16_t nr_mins) { void IRHaierAC::setOnTimer(const uint16_t nr_mins) {
setCommand(HAIER_AC_CMD_TIMER_SET); setCommand(kHaierAcCmdTimerSet);
remote_state[3] |= 0b10000000; remote_state[3] |= 0b10000000;
setTime(remote_state + 6, nr_mins); setTime(remote_state + 6, nr_mins);
} }
void IRHaierAC::setOffTimer(const uint16_t nr_mins) { void IRHaierAC::setOffTimer(const uint16_t nr_mins) {
setCommand(HAIER_AC_CMD_TIMER_SET); setCommand(kHaierAcCmdTimerSet);
remote_state[3] |= 0b01000000; remote_state[3] |= 0b01000000;
setTime(remote_state + 4, nr_mins); setTime(remote_state + 4, nr_mins);
} }
void IRHaierAC::cancelTimers() { void IRHaierAC::cancelTimers() {
setCommand(HAIER_AC_CMD_TIMER_CANCEL); setCommand(kHaierAcCmdTimerCancel);
remote_state[3] &= 0b00111111; remote_state[3] &= 0b00111111;
} }
@@ -275,18 +279,16 @@ void IRHaierAC::setCurrTime(const uint16_t nr_mins) {
setTime(remote_state + 2, nr_mins); setTime(remote_state + 2, nr_mins);
} }
uint8_t IRHaierAC::getSwing() { uint8_t IRHaierAC::getSwing() { return (remote_state[2] & 0b11000000) >> 6; }
return (remote_state[2] & 0b11000000) >> 6;
}
void IRHaierAC::setSwing(const uint8_t state) { void IRHaierAC::setSwing(const uint8_t state) {
if (state == getSwing()) return; // Nothing to do. if (state == getSwing()) return; // Nothing to do.
setCommand(HAIER_AC_CMD_SWING); setCommand(kHaierAcCmdSwing);
switch (state) { switch (state) {
case HAIER_AC_SWING_OFF: case kHaierAcSwingOff:
case HAIER_AC_SWING_UP: case kHaierAcSwingUp:
case HAIER_AC_SWING_DOWN: case kHaierAcSwingDown:
case HAIER_AC_SWING_CHG: case kHaierAcSwingChg:
remote_state[2] &= 0b00111111; remote_state[2] &= 0b00111111;
remote_state[2] |= (state << 6); remote_state[2] |= (state << 6);
break; break;
@@ -321,37 +323,37 @@ std::string IRHaierAC::toString() {
uint8_t cmd = getCommand(); uint8_t cmd = getCommand();
result += "Command: " + uint64ToString(cmd) + " ("; result += "Command: " + uint64ToString(cmd) + " (";
switch (cmd) { switch (cmd) {
case HAIER_AC_CMD_OFF: case kHaierAcCmdOff:
result += "Off"; result += "Off";
break; break;
case HAIER_AC_CMD_ON: case kHaierAcCmdOn:
result += "On"; result += "On";
break; break;
case HAIER_AC_CMD_MODE: case kHaierAcCmdMode:
result += "Mode"; result += "Mode";
break; break;
case HAIER_AC_CMD_FAN: case kHaierAcCmdFan:
result += "Fan"; result += "Fan";
break; break;
case HAIER_AC_CMD_TEMP_UP: case kHaierAcCmdTempUp:
result += "Temp Up"; result += "Temp Up";
break; break;
case HAIER_AC_CMD_TEMP_DOWN: case kHaierAcCmdTempDown:
result += "Temp Down"; result += "Temp Down";
break; break;
case HAIER_AC_CMD_SLEEP: case kHaierAcCmdSleep:
result += "Sleep"; result += "Sleep";
break; break;
case HAIER_AC_CMD_TIMER_SET: case kHaierAcCmdTimerSet:
result += "Timer Set"; result += "Timer Set";
break; break;
case HAIER_AC_CMD_TIMER_CANCEL: case kHaierAcCmdTimerCancel:
result += "Timer Cancel"; result += "Timer Cancel";
break; break;
case HAIER_AC_CMD_HEALTH: case kHaierAcCmdHealth:
result += "Health"; result += "Health";
break; break;
case HAIER_AC_CMD_SWING: case kHaierAcCmdSwing:
result += "Swing"; result += "Swing";
break; break;
default: default:
@@ -360,19 +362,19 @@ std::string IRHaierAC::toString() {
result += ")"; result += ")";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case HAIER_AC_AUTO: case kHaierAcAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case HAIER_AC_COOL: case kHaierAcCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case HAIER_AC_HEAT: case kHaierAcHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case HAIER_AC_DRY: case kHaierAcDry:
result += " (DRY)"; result += " (DRY)";
break; break;
case HAIER_AC_FAN: case kHaierAcFan:
result += " (FAN)"; result += " (FAN)";
break; break;
default: default:
@@ -381,25 +383,25 @@ std::string IRHaierAC::toString() {
result += ", Temp: " + uint64ToString(getTemp()) + "C"; result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan()); result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) { switch (getFan()) {
case HAIER_AC_FAN_AUTO: case kHaierAcFanAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case HAIER_AC_FAN_HIGH: case kHaierAcFanHigh:
result += " (MAX)"; result += " (MAX)";
break; break;
} }
result += ", Swing: " + uint64ToString(getSwing()) + " ("; result += ", Swing: " + uint64ToString(getSwing()) + " (";
switch (getSwing()) { switch (getSwing()) {
case HAIER_AC_SWING_OFF: case kHaierAcSwingOff:
result += "Off"; result += "Off";
break; break;
case HAIER_AC_SWING_UP: case kHaierAcSwingUp:
result += "Up"; result += "Up";
break; break;
case HAIER_AC_SWING_DOWN: case kHaierAcSwingDown:
result += "Down"; result += "Down";
break; break;
case HAIER_AC_SWING_CHG: case kHaierAcSwingChg:
result += "Chg"; result += "Chg";
break; break;
default: default:
@@ -430,13 +432,347 @@ std::string IRHaierAC::toString() {
return result; return result;
} }
// End of IRHaierAC class.
#if DECODE_HAIER_AC // Class for emulating a Haier YRW02 remote
// Decode the supplied Haier message. IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRHaierACYRW02::begin() { _irsend.begin(); }
#if SEND_HAIER_AC_YRW02
void IRHaierACYRW02::send(const uint16_t repeat) {
checksum();
_irsend.sendHaierACYRW02(remote_state, kHaierACYRW02StateLength, repeat);
}
#endif // SEND_HAIER_AC_YRW02
void IRHaierACYRW02::checksum() {
remote_state[kHaierACYRW02StateLength - 1] =
sumBytes(remote_state, kHaierACYRW02StateLength - 1);
}
bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) {
if (length < 2) return false; // 1 byte of data can't have a checksum.
return (state[length - 1] == sumBytes(state, length - 1));
}
void IRHaierACYRW02::stateReset() {
for (uint8_t i = 1; i < kHaierACYRW02StateLength; i++) remote_state[i] = 0x0;
remote_state[0] = kHaierAcYrw02Prefix;
setTemp(kHaierAcDefTemp);
setHealth(true);
setTurbo(kHaierAcYrw02TurboOff);
setSleep(false);
setFan(kHaierAcYrw02FanAuto);
setSwing(kHaierAcYrw02SwingOff);
setMode(kHaierAcYrw02Auto);
setPower(true);
}
uint8_t* IRHaierACYRW02::getRaw() {
checksum();
return remote_state;
}
void IRHaierACYRW02::setRaw(uint8_t new_code[]) {
for (uint8_t i = 0; i < kHaierACYRW02StateLength; i++) {
remote_state[i] = new_code[i];
}
}
void IRHaierACYRW02::setButton(uint8_t button) {
switch (button) {
case kHaierAcYrw02ButtonTempUp:
case kHaierAcYrw02ButtonTempDown:
case kHaierAcYrw02ButtonSwing:
case kHaierAcYrw02ButtonFan:
case kHaierAcYrw02ButtonPower:
case kHaierAcYrw02ButtonMode:
case kHaierAcYrw02ButtonHealth:
case kHaierAcYrw02ButtonTurbo:
case kHaierAcYrw02ButtonSleep:
remote_state[12] &= 0b11110000;
remote_state[12] |= (button & 0b00001111);
}
}
uint8_t IRHaierACYRW02::getButton() { return remote_state[12] & (0b00001111); }
void IRHaierACYRW02::setMode(uint8_t mode) {
uint8_t new_mode = mode;
setButton(kHaierAcYrw02ButtonMode);
switch (mode) {
case kHaierAcYrw02Auto:
case kHaierAcYrw02Cool:
case kHaierAcYrw02Dry:
case kHaierAcYrw02Heat:
case kHaierAcYrw02Fan:
break;
default: // If unexpected, default to auto mode.
new_mode = kHaierAcYrw02Auto;
}
remote_state[7] &= 0b0001111;
remote_state[7] |= (new_mode << 4);
}
uint8_t IRHaierACYRW02::getMode() { return remote_state[7] >> 4; }
void IRHaierACYRW02::setTemp(const uint8_t celcius) {
uint8_t temp = celcius;
if (temp < kHaierAcMinTemp)
temp = kHaierAcMinTemp;
else if (temp > kHaierAcMaxTemp)
temp = kHaierAcMaxTemp;
uint8_t old_temp = getTemp();
if (old_temp == temp) return;
if (old_temp > temp)
setButton(kHaierAcYrw02ButtonTempDown);
else
setButton(kHaierAcYrw02ButtonTempUp);
remote_state[1] &= 0b00001111; // Clear the previous temp.
remote_state[1] |= ((temp - kHaierAcMinTemp) << 4);
}
uint8_t IRHaierACYRW02::getTemp() {
return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp;
}
void IRHaierACYRW02::setHealth(bool state) {
setButton(kHaierAcYrw02ButtonHealth);
remote_state[3] &= 0b11111101;
remote_state[3] |= (state << 1);
}
bool IRHaierACYRW02::getHealth(void) { return remote_state[3] & 0b00000010; }
bool IRHaierACYRW02::getPower() { return remote_state[4] & kHaierAcYrw02Power; }
void IRHaierACYRW02::setPower(bool state) {
setButton(kHaierAcYrw02ButtonPower);
if (state)
remote_state[4] |= kHaierAcYrw02Power;
else
remote_state[4] &= ~kHaierAcYrw02Power;
}
void IRHaierACYRW02::on() { setPower(true); }
void IRHaierACYRW02::off() { setPower(false); }
bool IRHaierACYRW02::getSleep() { return remote_state[8] & kHaierAcYrw02Sleep; }
void IRHaierACYRW02::setSleep(bool state) {
setButton(kHaierAcYrw02ButtonSleep);
if (state)
remote_state[8] |= kHaierAcYrw02Sleep;
else
remote_state[8] &= ~kHaierAcYrw02Sleep;
}
uint8_t IRHaierACYRW02::getTurbo() { return remote_state[6] >> 6; }
void IRHaierACYRW02::setTurbo(uint8_t speed) {
switch (speed) {
case kHaierAcYrw02TurboOff:
case kHaierAcYrw02TurboLow:
case kHaierAcYrw02TurboHigh:
remote_state[6] &= 0b00111111;
remote_state[6] |= (speed << 6);
setButton(kHaierAcYrw02ButtonTurbo);
}
}
uint8_t IRHaierACYRW02::getFan() { return remote_state[5] >> 4; }
void IRHaierACYRW02::setFan(uint8_t speed) {
switch (speed) {
case kHaierAcYrw02FanLow:
case kHaierAcYrw02FanMed:
case kHaierAcYrw02FanHigh:
case kHaierAcYrw02FanAuto:
remote_state[5] &= 0b00001111;
remote_state[5] |= (speed << 4);
setButton(kHaierAcYrw02ButtonFan);
}
}
uint8_t IRHaierACYRW02::getSwing() { return remote_state[1] & 0b00001111; }
void IRHaierACYRW02::setSwing(uint8_t state) {
uint8_t newstate = state;
switch (state) {
case kHaierAcYrw02SwingOff:
case kHaierAcYrw02SwingAuto:
case kHaierAcYrw02SwingTop:
case kHaierAcYrw02SwingMiddle:
case kHaierAcYrw02SwingBottom:
case kHaierAcYrw02SwingDown:
setButton(kHaierAcYrw02ButtonSwing);
break;
default:
return; // Unexpected value so don't do anything.
}
// Heat mode has no MIDDLE setting, use BOTTOM instead.
if (state == kHaierAcYrw02SwingMiddle && getMode() == kHaierAcYrw02Heat)
newstate = kHaierAcYrw02SwingBottom;
// BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE.
if (state == kHaierAcYrw02SwingBottom && getMode() != kHaierAcYrw02Heat)
newstate = kHaierAcYrw02SwingMiddle;
remote_state[1] &= 0b11110000;
remote_state[1] |= newstate;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRHaierACYRW02::toString() {
String result = "";
#else
std::string IRHaierACYRW02::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
if (getPower())
result += "On";
else
result += "Off";
uint8_t cmd = getButton();
result += ", Button: " + uint64ToString(cmd) + " (";
switch (cmd) {
case kHaierAcYrw02ButtonPower:
result += "Power";
break;
case kHaierAcYrw02ButtonMode:
result += "Mode";
break;
case kHaierAcYrw02ButtonFan:
result += "Fan";
break;
case kHaierAcYrw02ButtonTempUp:
result += "Temp Up";
break;
case kHaierAcYrw02ButtonTempDown:
result += "Temp Down";
break;
case kHaierAcYrw02ButtonSleep:
result += "Sleep";
break;
case kHaierAcYrw02ButtonHealth:
result += "Health";
break;
case kHaierAcYrw02ButtonSwing:
result += "Swing";
break;
case kHaierAcYrw02ButtonTurbo:
result += "Turbo";
break;
default:
result += "Unknown";
}
result += ")";
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kHaierAcYrw02Auto:
result += " (Auto)";
break;
case kHaierAcYrw02Cool:
result += " (Cool)";
break;
case kHaierAcYrw02Heat:
result += " (Heat)";
break;
case kHaierAcYrw02Dry:
result += " (Dry)";
break;
case kHaierAcYrw02Fan:
result += " (Fan)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kHaierAcYrw02FanAuto:
result += " (Auto)";
break;
case kHaierAcYrw02FanHigh:
result += " (High)";
break;
case kHaierAcYrw02FanLow:
result += " (Low)";
break;
case kHaierAcYrw02FanMed:
result += " (Med)";
break;
default:
result += " (Unknown)";
}
result += ", Turbo: " + uint64ToString(getTurbo()) + " (";
switch (getTurbo()) {
case kHaierAcYrw02TurboOff:
result += "Off";
break;
case kHaierAcYrw02TurboLow:
result += "Low";
break;
case kHaierAcYrw02TurboHigh:
result += "High";
break;
default:
result += "Unknown";
}
result += ")";
result += ", Swing: " + uint64ToString(getSwing()) + " (";
switch (getSwing()) {
case kHaierAcYrw02SwingOff:
result += "Off";
break;
case kHaierAcYrw02SwingAuto:
result += "Auto";
break;
case kHaierAcYrw02SwingBottom:
result += "Bottom";
break;
case kHaierAcYrw02SwingDown:
result += "Down";
break;
case kHaierAcYrw02SwingTop:
result += "Top";
break;
case kHaierAcYrw02SwingMiddle:
result += "Middle";
break;
default:
result += "Unknown";
}
result += ")";
result += ", Sleep: ";
if (getSleep())
result += "On";
else
result += "Off";
result += ", Health: ";
if (getHealth())
result += "On";
else
result += "Off";
return result;
}
// End of IRHaierACYRW02 class.
#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02)
// Decode the supplied Haier HSU07-HEA03 remote message.
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically HAIER_AC_BITS. // nbits: The number of data bits to expect. Typically kHaierACBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -449,43 +785,40 @@ bool IRrecv::decodeHaierAC(decode_results *results, uint16_t nbits,
return false; return false;
if (strict) { if (strict) {
if (nbits != HAIER_AC_BITS) if (nbits != kHaierACBits)
return false; // Not strictly a HAIER_AC message. return false; // Not strictly a HAIER_AC message.
} }
if (results->rawlen < (2 * nbits + HEADER) + FOOTER - 1) if (results->rawlen < (2 * nbits + kHeader) + kFooter - 1)
return false; // Can't possibly be a valid HAIER_AC message. return false; // Can't possibly be a valid HAIER_AC message.
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false; if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false;
if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR)) return false; if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false;
if (!matchMark(results->rawbuf[offset++], HAIER_AC_HDR)) return false; if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false;
if (!matchSpace(results->rawbuf[offset++], HAIER_AC_HDR_GAP)) return false; if (!matchSpace(results->rawbuf[offset++], kHaierAcHdrGap)) return false;
// Data // Data
for (uint16_t i = 0; i < nbits / 8; i++) { for (uint16_t i = 0; i < nbits / 8; i++) {
match_result_t data_result = matchData(&(results->rawbuf[offset]), 8, match_result_t data_result =
HAIER_AC_BIT_MARK, matchData(&(results->rawbuf[offset]), 8, kHaierAcBitMark,
HAIER_AC_ONE_SPACE, kHaierAcOneSpace, kHaierAcBitMark, kHaierAcZeroSpace);
HAIER_AC_BIT_MARK,
HAIER_AC_ZERO_SPACE);
if (data_result.success == false) return false; if (data_result.success == false) return false;
offset += data_result.used; offset += data_result.used;
results->state[i] = (uint8_t)data_result.data; results->state[i] = (uint8_t)data_result.data;
} }
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], HAIER_AC_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kHaierAcBitMark)) return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], HAIER_AC_MIN_GAP)) !matchAtLeast(results->rawbuf[offset++], kHaierAcMinGap))
return false; return false;
// Compliance // Compliance
if (strict) { if (strict) {
if (results->state[0] != HAIER_AC_PREFIX) return false; if (results->state[0] != kHaierAcPrefix) return false;
if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false; if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false;
} }
@@ -494,4 +827,39 @@ bool IRrecv::decodeHaierAC(decode_results *results, uint16_t nbits,
results->bits = nbits; results->bits = nbits;
return true; return true;
} }
#endif // DECODE_HAIER_AC #endif // (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02)
#if DECODE_HAIER_AC_YRW02
// Decode the supplied Haier YR-W02 remote A/C 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 kHaierACYRW02Bits.
// 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 be working.
//
bool IRrecv::decodeHaierACYRW02(decode_results* results, uint16_t nbits,
bool strict) {
if (strict) {
if (nbits != kHaierACYRW02Bits)
return false; // Not strictly a HAIER_AC_YRW02 message.
}
// The protocol is almost exactly the same as HAIER_AC
if (!decodeHaierAC(results, nbits, false)) return false;
// Compliance
if (strict) {
if (results->state[0] != kHaierAcYrw02Prefix) return false;
if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8)) return false;
}
// Success
// It looks correct, but we haven't check the checksum etc.
results->decode_type = HAIER_AC_YRW02;
return true;
}
#endif // DECODE_HAIER_AC_YRW02
+204 -33
View File
@@ -21,55 +21,172 @@
// Ref: // Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/404 // https://github.com/markszabo/IRremoteESP8266/issues/404
// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 // https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
// https://github.com/markszabo/IRremoteESP8266/issues/485
// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
// Constants // Constants
// Haier HSU07-HEA03 remote
// Byte 0 // Byte 0
#define HAIER_AC_PREFIX 0b10100101 const uint8_t kHaierAcPrefix = 0b10100101;
// Byte 1 // Byte 1
#define HAIER_AC_MIN_TEMP 16 const uint8_t kHaierAcMinTemp = 16;
#define HAIER_AC_MAX_TEMP 30 const uint8_t kHaierAcDefTemp = 25;
#define HAIER_AC_DEF_TEMP 25 const uint8_t kHaierAcMaxTemp = 30;
const uint8_t kHaierAcCmdOff = 0b00000000;
#define HAIER_AC_CMD_OFF 0b00000000 const uint8_t kHaierAcCmdOn = 0b00000001;
#define HAIER_AC_CMD_ON 0b00000001 const uint8_t kHaierAcCmdMode = 0b00000010;
#define HAIER_AC_CMD_MODE 0b00000010 const uint8_t kHaierAcCmdFan = 0b00000011;
#define HAIER_AC_CMD_FAN 0b00000011 const uint8_t kHaierAcCmdTempUp = 0b00000110;
#define HAIER_AC_CMD_TEMP_UP 0b00000110 const uint8_t kHaierAcCmdTempDown = 0b00000111;
#define HAIER_AC_CMD_TEMP_DOWN 0b00000111 const uint8_t kHaierAcCmdSleep = 0b00001000;
#define HAIER_AC_CMD_SLEEP 0b00001000 const uint8_t kHaierAcCmdTimerSet = 0b00001001;
#define HAIER_AC_CMD_TIMER_SET 0b00001001 const uint8_t kHaierAcCmdTimerCancel = 0b00001010;
#define HAIER_AC_CMD_TIMER_CANCEL 0b00001010 const uint8_t kHaierAcCmdHealth = 0b00001100;
#define HAIER_AC_CMD_HEALTH 0b00001100 const uint8_t kHaierAcCmdSwing = 0b00001101;
#define HAIER_AC_CMD_SWING 0b00001101
// Byte 2 // Byte 2
#define HAIER_AC_SWING_OFF 0b00000000 const uint8_t kHaierAcSwingOff = 0b00000000;
#define HAIER_AC_SWING_UP 0b00000001 const uint8_t kHaierAcSwingUp = 0b00000001;
#define HAIER_AC_SWING_DOWN 0b00000010 const uint8_t kHaierAcSwingDown = 0b00000010;
#define HAIER_AC_SWING_CHG 0b00000011 const uint8_t kHaierAcSwingChg = 0b00000011;
// Byte 6 // Byte 6
#define HAIER_AC_AUTO 0 const uint8_t kHaierAcAuto = 0;
#define HAIER_AC_COOL 1 const uint8_t kHaierAcCool = 1;
#define HAIER_AC_DRY 2 const uint8_t kHaierAcDry = 2;
#define HAIER_AC_HEAT 3 const uint8_t kHaierAcHeat = 3;
#define HAIER_AC_FAN 4 const uint8_t kHaierAcFan = 4;
#define HAIER_AC_FAN_AUTO 0 const uint8_t kHaierAcFanAuto = 0;
#define HAIER_AC_FAN_LOW 1 const uint8_t kHaierAcFanLow = 1;
#define HAIER_AC_FAN_MED 2 const uint8_t kHaierAcFanMed = 2;
#define HAIER_AC_FAN_HIGH 3 const uint8_t kHaierAcFanHigh = 3;
#define HAIER_AC_MAX_TIME (23 * 60 + 59) const uint16_t kHaierAcMaxTime = (23 * 60) + 59;
// Legacy Haier AC defines.
#define HAIER_AC_MIN_TEMP kHaierAcMinTemp
#define HAIER_AC_DEF_TEMP kHaierAcDefTemp
#define HAIER_AC_MAX_TEMP kHaierAcMaxTemp
#define HAIER_AC_CMD_OFF kHaierAcCmdOff
#define HAIER_AC_CMD_ON kHaierAcCmdOn
#define HAIER_AC_CMD_MODE kHaierAcCmdMode
#define HAIER_AC_CMD_FAN kHaierAcCmdFan
#define HAIER_AC_CMD_TEMP_UP kHaierAcCmdTempUp
#define HAIER_AC_CMD_TEMP_DOWN kHaierAcCmdTempDown
#define HAIER_AC_CMD_SLEEP kHaierAcCmdSleep
#define HAIER_AC_CMD_TIMER_SET kHaierAcCmdTimerSet
#define HAIER_AC_CMD_TIMER_CANCEL kHaierAcCmdTimerCancel
#define HAIER_AC_CMD_HEALTH kHaierAcCmdHealth
#define HAIER_AC_CMD_SWING kHaierAcCmdSwing
#define HAIER_AC_SWING_OFF kHaierAcSwingOff
#define HAIER_AC_SWING_UP kHaierAcSwingUp
#define HAIER_AC_SWING_DOWN kHaierAcSwingDown
#define HAIER_AC_SWING_CHG kHaierAcSwingChg
#define HAIER_AC_AUTO kHaierAcAuto
#define HAIER_AC_COOL kHaierAcCool
#define HAIER_AC_DRY kHaierAcDry
#define HAIER_AC_HEAT kHaierAcHeat
#define HAIER_AC_FAN kHaierAcFan
#define HAIER_AC_FAN_AUTO kHaierAcFanAuto
#define HAIER_AC_FAN_LOW kHaierAcFanLow
#define HAIER_AC_FAN_MED kHaierAcFanMed
#define HAIER_AC_FAN_HIGH kHaierAcFanHigh
// Haier YRW02 remote
// Byte 0
const uint8_t kHaierAcYrw02Prefix = 0xA6;
// Byte 1
// Bits 0-3
// 0x0 = 16DegC, ... 0xE = 30DegC
// Bits 4-7 - Swing
const uint8_t kHaierAcYrw02SwingOff = 0x0;
const uint8_t kHaierAcYrw02SwingTop = 0x1;
const uint8_t kHaierAcYrw02SwingMiddle = 0x2; // Not available in heat mode.
const uint8_t kHaierAcYrw02SwingBottom = 0x3; // Only available in heat mode.
const uint8_t kHaierAcYrw02SwingDown = 0xA;
const uint8_t kHaierAcYrw02SwingAuto = 0xC; // Airflow
// Byte 3
// Bit 7 - Health mode
// Byte 4
const uint8_t kHaierAcYrw02Power = 0b01000000;
// Byte 5
// Bits 0-3
const uint8_t kHaierAcYrw02FanHigh = 0x2;
const uint8_t kHaierAcYrw02FanMed = 0x4;
const uint8_t kHaierAcYrw02FanLow = 0x6;
const uint8_t kHaierAcYrw02FanAuto = 0xA;
// Byte 6
// Bits 0-1
const uint8_t kHaierAcYrw02TurboOff = 0x0;
const uint8_t kHaierAcYrw02TurboHigh = 0x1;
const uint8_t kHaierAcYrw02TurboLow = 0x2;
// Byte 7
// Bits 0-3
const uint8_t kHaierAcYrw02Auto = 0x0;
const uint8_t kHaierAcYrw02Cool = 0x2;
const uint8_t kHaierAcYrw02Dry = 0x4;
const uint8_t kHaierAcYrw02Heat = 0x8;
const uint8_t kHaierAcYrw02Fan = 0xC;
// Byte 8
const uint8_t kHaierAcYrw02Sleep = 0b10000000;
// Byte 12
// Bits 4-7
const uint8_t kHaierAcYrw02ButtonTempUp = 0x0;
const uint8_t kHaierAcYrw02ButtonTempDown = 0x1;
const uint8_t kHaierAcYrw02ButtonSwing = 0x2;
const uint8_t kHaierAcYrw02ButtonFan = 0x4;
const uint8_t kHaierAcYrw02ButtonPower = 0x5;
const uint8_t kHaierAcYrw02ButtonMode = 0x6;
const uint8_t kHaierAcYrw02ButtonHealth = 0x7;
const uint8_t kHaierAcYrw02ButtonTurbo = 0x8;
const uint8_t kHaierAcYrw02ButtonSleep = 0xB;
// Legacy Haier YRW02 remote defines.
#define HAIER_AC_YRW02_SWING_OFF kHaierAcYrw02SwingOff
#define HAIER_AC_YRW02_SWING_TOP kHaierAcYrw02SwingTop
#define HAIER_AC_YRW02_SWING_MIDDLE kHaierAcYrw02SwingMiddle
#define HAIER_AC_YRW02_SWING_BOTTOM kHaierAcYrw02SwingBottom
#define HAIER_AC_YRW02_SWING_DOWN kHaierAcYrw02SwingDown
#define HAIER_AC_YRW02_SWING_AUTO kHaierAcYrw02SwingAuto
#define HAIER_AC_YRW02_FAN_HIGH kHaierAcYrw02FanHigh
#define HAIER_AC_YRW02_FAN_MED kHaierAcYrw02FanMed
#define HAIER_AC_YRW02_FAN_LOW kHaierAcYrw02FanLow
#define HAIER_AC_YRW02_FAN_AUTO kHaierAcYrw02FanAuto
#define HAIER_AC_YRW02_TURBO_OFF kHaierAcYrw02TurboOff
#define HAIER_AC_YRW02_TURBO_HIGH kHaierAcYrw02TurboHigh
#define HAIER_AC_YRW02_TURBO_LOW kHaierAcYrw02TurboLow
#define HAIER_AC_YRW02_AUTO kHaierAcYrw02Auto
#define HAIER_AC_YRW02_COOL kHaierAcYrw02Cool
#define HAIER_AC_YRW02_DRY kHaierAcYrw02Dry
#define HAIER_AC_YRW02_HEAT kHaierAcYrw02Heat
#define HAIER_AC_YRW02_FAN kHaierAcYrw02Fan
#define HAIER_AC_YRW02_BUTTON_TEMP_UP kHaierAcYrw02ButtonTempUp
#define HAIER_AC_YRW02_BUTTON_TEMP_DOWN kHaierAcYrw02ButtonTempDown
#define HAIER_AC_YRW02_BUTTON_SWING kHaierAcYrw02ButtonSwing
#define HAIER_AC_YRW02_BUTTON_FAN kHaierAcYrw02ButtonFan
#define HAIER_AC_YRW02_BUTTON_POWER kHaierAcYrw02ButtonPower
#define HAIER_AC_YRW02_BUTTON_MODE kHaierAcYrw02ButtonMode
#define HAIER_AC_YRW02_BUTTON_HEALTH kHaierAcYrw02ButtonHealth
#define HAIER_AC_YRW02_BUTTON_TURBO kHaierAcYrw02ButtonTurbo
#define HAIER_AC_YRW02_BUTTON_SLEEP kHaierAcYrw02ButtonSleep
class IRHaierAC { class IRHaierAC {
public: public:
explicit IRHaierAC(uint16_t pin); explicit IRHaierAC(uint16_t pin);
#if SEND_HAIER_AC #if SEND_HAIER_AC
void send(); void send(const uint16_t repeat = kHaierAcDefaultRepeat);
#endif // SEND_HAIER_AC #endif // SEND_HAIER_AC
void begin(); void begin();
@@ -105,7 +222,7 @@ class IRHaierAC {
uint8_t* getRaw(); uint8_t* getRaw();
void setRaw(uint8_t new_code[]); void setRaw(uint8_t new_code[]);
static bool validChecksum(uint8_t state[], static bool validChecksum(uint8_t state[],
const uint16_t length = HAIER_AC_STATE_LENGTH); const uint16_t length = kHaierACStateLength);
#ifdef ARDUINO #ifdef ARDUINO
String toString(); String toString();
static String timeToString(const uint16_t nr_mins); static String timeToString(const uint16_t nr_mins);
@@ -115,7 +232,7 @@ class IRHaierAC {
#endif #endif
private: private:
uint8_t remote_state[HAIER_AC_STATE_LENGTH]; uint8_t remote_state[kHaierACStateLength];
void stateReset(); void stateReset();
void checksum(); void checksum();
static uint16_t getTime(const uint8_t ptr[]); static uint16_t getTime(const uint8_t ptr[]);
@@ -123,4 +240,58 @@ class IRHaierAC {
IRsend _irsend; IRsend _irsend;
}; };
class IRHaierACYRW02 {
public:
explicit IRHaierACYRW02(uint16_t pin);
#if SEND_HAIER_AC_YRW02
void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat);
#endif // SEND_HAIER_AC_YRW02
void begin();
void setButton(const uint8_t button);
uint8_t getButton();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t speed);
uint8_t getFan();
uint8_t getMode();
void setMode(const uint8_t mode);
bool getPower();
void setPower(const bool state);
void on();
void off();
bool getSleep();
void setSleep(const bool state);
bool getHealth();
void setHealth(const bool state);
uint8_t getTurbo();
void setTurbo(const uint8_t speed);
uint8_t getSwing();
void setSwing(const uint8_t state);
uint8_t* getRaw();
void setRaw(uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kHaierACYRW02StateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
private:
uint8_t remote_state[kHaierACYRW02StateLength];
void stateReset();
void checksum();
IRsend _irsend;
};
#endif // IR_HAIER_H_ #endif // IR_HAIER_H_
+263 -54
View File
@@ -5,12 +5,13 @@
// * Hitachi RAS-35THA6 remote // * Hitachi RAS-35THA6 remote
// //
#include "ir_Hitachi.h"
#include <algorithm> #include <algorithm>
#ifndef ARDUINO #ifndef ARDUINO
#include <string> #include <string>
#endif #endif
#include "IRremoteESP8266.h"
#include "IRrecv.h" #include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
@@ -22,21 +23,21 @@
// Constants // Constants
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 // Ref: https://github.com/markszabo/IRremoteESP8266/issues/417
#define HITACHI_AC_HDR_MARK 3300U const uint16_t kHitachiAcHdrMark = 3300;
#define HITACHI_AC_HDR_SPACE 1700U const uint16_t kHitachiAcHdrSpace = 1700;
#define HITACHI_AC1_HDR_MARK 3400U const uint16_t kHitachiAc1HdrMark = 3400;
#define HITACHI_AC1_HDR_SPACE 3400U const uint16_t kHitachiAc1HdrSpace = 3400;
#define HITACHI_AC_BIT_MARK 400U const uint16_t kHitachiAcBitMark = 400;
#define HITACHI_AC_ONE_SPACE 1250U const uint16_t kHitachiAcOneSpace = 1250;
#define HITACHI_AC_ZERO_SPACE 500U const uint16_t kHitachiAcZeroSpace = 500;
#define HITACHI_AC_MIN_GAP 100000U // Completely made up value. const uint32_t kHitachiAcMinGap = 100000; // Completely made up value.
#if (SEND_HITACHI_AC || SEND_HITACHI_AC2) #if (SEND_HITACHI_AC || SEND_HITACHI_AC2)
// Send a Hitachi A/C message. // Send a Hitachi A/C message.
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kHitachiAcStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
@@ -45,13 +46,12 @@
// https://github.com/markszabo/IRremoteESP8266/issues/417 // https://github.com/markszabo/IRremoteESP8266/issues/417
void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < HITACHI_AC_STATE_LENGTH) if (nbytes < kHitachiAcStateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
sendGeneric(HITACHI_AC_HDR_MARK, HITACHI_AC_HDR_SPACE, sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark,
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE, kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace,
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE, kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, true,
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP, repeat, 50);
data, nbytes, 38, true, repeat, 50);
} }
#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2) #endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2)
@@ -63,7 +63,7 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC1_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kHitachiAc1StateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: BETA / Appears to work. // Status: BETA / Appears to work.
@@ -73,13 +73,12 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
// Basically the same as sendHitatchiAC() except different size and header. // Basically the same as sendHitatchiAC() except different size and header.
void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < HITACHI_AC1_STATE_LENGTH) if (nbytes < kHitachiAc1StateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
sendGeneric(HITACHI_AC1_HDR_MARK, HITACHI_AC1_HDR_SPACE, sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark,
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE, kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace,
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE, kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, true,
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP, repeat, 50);
data, nbytes, 38, true, repeat, 50);
} }
#endif // SEND_HITACHI_AC1 #endif // SEND_HITACHI_AC1
@@ -91,7 +90,7 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC2_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kHitachiAc2StateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: BETA / Appears to work. // Status: BETA / Appears to work.
@@ -101,19 +100,232 @@ void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
// Basically the same as sendHitatchiAC() except different size. // Basically the same as sendHitatchiAC() except different size.
void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < HITACHI_AC2_STATE_LENGTH) if (nbytes < kHitachiAc2StateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
sendHitachiAC(data, nbytes, repeat); sendHitachiAC(data, nbytes, repeat);
} }
#endif // SEND_HITACHI_AC2 #endif // SEND_HITACHI_AC2
// Class for handling the remote control oh a Hitachi 28 byte A/C message.
// Inspired by:
// https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp
IRHitachiAc::IRHitachiAc(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRHitachiAc::stateReset() {
remote_state[0] = 0x80;
remote_state[1] = 0x08;
remote_state[2] = 0x0C;
remote_state[3] = 0x02;
remote_state[4] = 0xFD;
remote_state[5] = 0x80;
remote_state[6] = 0x7F;
remote_state[7] = 0x88;
remote_state[8] = 0x48;
remote_state[9] = 0x10;
for (uint8_t i = 10; i < kHitachiAcStateLength; i++) remote_state[i] = 0x00;
remote_state[14] = 0x60;
remote_state[15] = 0x60;
remote_state[24] = 0x80;
setTemp(23);
}
void IRHitachiAc::begin() { _irsend.begin(); }
uint8_t IRHitachiAc::calcChecksum(const uint8_t state[],
const uint16_t length) {
int8_t sum = 62;
for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8);
return reverseBits((uint8_t)sum, 8);
}
void IRHitachiAc::checksum(const uint16_t length) {
remote_state[length - 1] = calcChecksum(remote_state, length);
}
bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) {
if (length < 2) return true; // Assume true for lengths that are too short.
return (state[length - 1] == calcChecksum(state, length));
}
uint8_t *IRHitachiAc::getRaw() {
checksum();
return remote_state;
}
void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) {
for (uint8_t i = 0; i < length && i < kHitachiAcStateLength; i++)
remote_state[i] = new_code[i];
}
#if SEND_HITACHI_AC
void IRHitachiAc::send(const uint16_t repeat) {
checksum();
_irsend.sendHitachiAC(remote_state, kHitachiAcStateLength, repeat);
}
#endif // SEND_HITACHI_AC
bool IRHitachiAc::getPower() { return (remote_state[17] & 0x01); }
void IRHitachiAc::setPower(const bool on) {
if (on)
remote_state[17] |= 0x01;
else
remote_state[17] &= 0xFE;
}
void IRHitachiAc::on() { setPower(true); }
void IRHitachiAc::off() { setPower(false); }
uint8_t IRHitachiAc::getMode() { return reverseBits(remote_state[10], 8); }
void IRHitachiAc::setMode(const uint8_t mode) {
uint8_t newmode = mode;
switch (mode) {
case kHitachiAcFan:
// Fan mode sets a special temp.
setTemp(64);
break;
case kHitachiAcAuto:
case kHitachiAcHeat:
case kHitachiAcCool:
case kHitachiAcDry:
break;
default:
newmode = kHitachiAcAuto;
}
remote_state[10] = reverseBits(newmode, 8);
if (mode != kHitachiAcFan) setTemp(_previoustemp);
setFan(getFan()); // Reset the fan speed after the mode change.
}
uint8_t IRHitachiAc::getTemp() { return reverseBits(remote_state[11], 8) >> 1; }
void IRHitachiAc::setTemp(const uint8_t celsius) {
uint8_t temp;
if (celsius != 64) _previoustemp = celsius;
switch (celsius) {
case 64:
temp = celsius;
break;
default:
temp = std::min(celsius, kHitachiAcMaxTemp);
temp = std::max(temp, kHitachiAcMinTemp);
}
remote_state[11] = reverseBits(temp << 1, 8);
if (temp == kHitachiAcMinTemp)
remote_state[9] = 0x90;
else
remote_state[9] = 0x10;
}
uint8_t IRHitachiAc::getFan() { return reverseBits(remote_state[13], 8); }
void IRHitachiAc::setFan(const uint8_t speed) {
uint8_t fanmin = kHitachiAcFanAuto;
uint8_t fanmax = kHitachiAcFanHigh;
switch (getMode()) {
case kHitachiAcDry: // Only 2 x low speeds in Dry mode.
fanmin = kHitachiAcFanLow;
fanmax = kHitachiAcFanLow + 1;
break;
case kHitachiAcFan:
fanmin = kHitachiAcFanLow; // No Auto in Fan mode.
break;
}
uint8_t newspeed = std::max(speed, fanmin);
newspeed = std::min(newspeed, fanmax);
remote_state[13] = reverseBits(newspeed, 8);
}
bool IRHitachiAc::getSwingVertical() { return remote_state[14] & 0x80; }
void IRHitachiAc::setSwingVertical(const bool on) {
if (on)
remote_state[14] |= 0x80;
else
remote_state[14] &= 0x7F;
}
bool IRHitachiAc::getSwingHorizontal() { return remote_state[15] & 0x80; }
void IRHitachiAc::setSwingHorizontal(const bool on) {
if (on)
remote_state[15] |= 0x80;
else
remote_state[15] &= 0x7F;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRHitachiAc::toString() {
String result = "";
#else
std::string IRHitachiAc::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
if (getPower())
result += "On";
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kHitachiAcAuto:
result += " (AUTO)";
break;
case kHitachiAcCool:
result += " (COOL)";
break;
case kHitachiAcHeat:
result += " (HEAT)";
break;
case kHitachiAcDry:
result += " (DRY)";
break;
case kHitachiAcFan:
result += " (FAN)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kHitachiAcFanAuto:
result += " (AUTO)";
break;
case kHitachiAcFanLow:
result += " (LOW)";
break;
case kHitachiAcFanHigh:
result += " (HIGH)";
break;
default:
result += " (UNKNOWN)";
break;
}
result += ", Swing (Vertical): ";
if (getSwingVertical())
result += "On";
else
result += "Off";
result += ", Swing (Horizontal): ";
if (getSwingHorizontal())
result += "On";
else
result += "Off";
return result;
}
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2)
// Decode the supplied Hitachi A/C message. // Decode the supplied Hitachi A/C message.
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. // nbits: The number of data bits to expect.
// Typically HITACHI_AC_BITS, HITACHI_AC1_BITS, HITACHI_AC2_BITS // Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -129,80 +341,77 @@ void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes,
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
const uint8_t kTolerance = 30; const uint8_t kTolerance = 30;
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid HitachiAC message. return false; // Can't possibly be a valid HitachiAC message.
if (strict) { if (strict) {
switch (nbits) { switch (nbits) {
case HITACHI_AC_BITS: case kHitachiAcBits:
case HITACHI_AC1_BITS: case kHitachiAc1Bits:
case HITACHI_AC2_BITS: case kHitachiAc2Bits:
break; // Okay to continue. break; // Okay to continue.
default: default:
return false; // Not strictly a Hitachi message. return false; // Not strictly a Hitachi message.
} }
} }
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0; uint16_t dataBitsSoFar = 0;
match_result_t data_result; match_result_t data_result;
// Header // Header
if (nbits == HITACHI_AC1_BITS) { if (nbits == kHitachiAc1Bits) {
if (!matchMark(results->rawbuf[offset++], HITACHI_AC1_HDR_MARK, kTolerance)) if (!matchMark(results->rawbuf[offset++], kHitachiAc1HdrMark, kTolerance))
return false; return false;
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC1_HDR_SPACE, if (!matchSpace(results->rawbuf[offset++], kHitachiAc1HdrSpace, kTolerance))
kTolerance))
return false; return false;
} else { // Everything else. } else { // Everything else.
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_HDR_MARK, kTolerance)) if (!matchMark(results->rawbuf[offset++], kHitachiAcHdrMark, kTolerance))
return false; return false;
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC_HDR_SPACE, if (!matchSpace(results->rawbuf[offset++], kHitachiAcHdrSpace, kTolerance))
kTolerance))
return false; return false;
} }
// Data // Data
// Keep reading bytes until we either run out of message or state to fill. // Keep reading bytes until we either run out of message or state to fill.
for (uint16_t i = 0; for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8;
offset <= results->rawlen - 16 && i < nbits / 8;
i++, dataBitsSoFar += 8, offset += data_result.used) { i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8, data_result = matchData(&(results->rawbuf[offset]), 8, kHitachiAcBitMark,
HITACHI_AC_BIT_MARK, kHitachiAcOneSpace, kHitachiAcBitMark,
HITACHI_AC_ONE_SPACE, kHitachiAcZeroSpace, kTolerance);
HITACHI_AC_BIT_MARK,
HITACHI_AC_ZERO_SPACE,
kTolerance);
if (data_result.success == false) break; // Fail if (data_result.success == false) break; // Fail
results->state[i] = (uint8_t)data_result.data; results->state[i] = (uint8_t)data_result.data;
} }
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK, kTolerance)) if (!matchMark(results->rawbuf[offset++], kHitachiAcBitMark, kTolerance))
return false; return false;
if (offset <= results->rawlen && if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP, kTolerance)) !matchAtLeast(results->rawbuf[offset], kHitachiAcMinGap, kTolerance))
return false; return false;
// Compliance // Compliance
if (strict) { if (strict) {
// Re-check we got the correct size/length due to the way we read the data. // Re-check we got the correct size/length due to the way we read the data.
switch (dataBitsSoFar / 8) { switch (dataBitsSoFar / 8) {
case HITACHI_AC_STATE_LENGTH: case kHitachiAcStateLength:
case HITACHI_AC1_STATE_LENGTH: case kHitachiAc1StateLength:
case HITACHI_AC2_STATE_LENGTH: case kHitachiAc2StateLength:
break; // Continue break; // Continue
default: default:
return false; return false;
} }
if (dataBitsSoFar / 8 == kHitachiAcStateLength &&
!IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength))
return false;
} }
// Success // Success
switch (dataBitsSoFar) { switch (dataBitsSoFar) {
case HITACHI_AC1_BITS: case kHitachiAc1Bits:
results->decode_type = HITACHI_AC1; results->decode_type = HITACHI_AC1;
break; break;
case HITACHI_AC2_BITS: case kHitachiAc2Bits:
results->decode_type = HITACHI_AC2; results->decode_type = HITACHI_AC2;
break; break;
case HITACHI_AC_BITS: case kHitachiAcBits:
default: default:
results->decode_type = HITACHI_AC; results->decode_type = HITACHI_AC;
} }
+76
View File
@@ -0,0 +1,76 @@
// Hitachi A/C
//
// Copyright 2018 David Conran
#ifndef IR_HITACHI_H_
#define IR_HITACHI_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// Constants
const uint8_t kHitachiAcAuto = 2;
const uint8_t kHitachiAcHeat = 3;
const uint8_t kHitachiAcCool = 4;
const uint8_t kHitachiAcDry = 5;
const uint8_t kHitachiAcFan = 0xC;
const uint8_t kHitachiAcFanAuto = 1;
const uint8_t kHitachiAcFanLow = 2;
const uint8_t kHitachiAcFanHigh = 5;
const uint8_t kHitachiAcMinTemp = 16; // 16C
const uint8_t kHitachiAcMaxTemp = 32; // 32C
const uint8_t kHitachiAcAutoTemp = 23; // 23C
// Classes
class IRHitachiAc {
public:
explicit IRHitachiAc(uint16_t pin);
void stateReset();
#if SEND_HITACHI_AC
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif // SEND_HITACHI_AC
void begin();
void on();
void off();
void setPower(const bool on);
bool getPower();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t speed);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
void setSwingVertical(const bool on);
bool getSwingVertical();
void setSwingHorizontal(const bool on);
bool getSwingHorizontal();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAcStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kHitachiAcStateLength);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kHitachiAcStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
private:
// The state of the IR remote in IR code form.
uint8_t remote_state[kHitachiAcStateLength];
void checksum(const uint16_t length = kHitachiAcStateLength);
IRsend _irsend;
uint8_t _previoustemp;
};
#endif // IR_HITACHI_H_
+42 -46
View File
@@ -19,31 +19,31 @@
// Constants // Constants
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/jvc.php // http://www.sbprojects.com/knowledge/ir/jvc.php
#define JVC_TICK 75U const uint16_t kJvcTick = 75;
#define JVC_HDR_MARK_TICKS 112U const uint16_t kJvcHdrMarkTicks = 112;
#define JVC_HDR_MARK (JVC_HDR_MARK_TICKS * JVC_TICK) const uint16_t kJvcHdrMark = kJvcHdrMarkTicks * kJvcTick;
#define JVC_HDR_SPACE_TICKS 56U const uint16_t kJvcHdrSpaceTicks = 56;
#define JVC_HDR_SPACE (JVC_HDR_SPACE_TICKS * JVC_TICK) const uint16_t kJvcHdrSpace = kJvcHdrSpaceTicks * kJvcTick;
#define JVC_BIT_MARK_TICKS 7U const uint16_t kJvcBitMarkTicks = 7;
#define JVC_BIT_MARK (JVC_BIT_MARK_TICKS * JVC_TICK) const uint16_t kJvcBitMark = kJvcBitMarkTicks * kJvcTick;
#define JVC_ONE_SPACE_TICKS 23U const uint16_t kJvcOneSpaceTicks = 23;
#define JVC_ONE_SPACE (JVC_ONE_SPACE_TICKS * JVC_TICK) const uint16_t kJvcOneSpace = kJvcOneSpaceTicks * kJvcTick;
#define JVC_ZERO_SPACE_TICKS 7U const uint16_t kJvcZeroSpaceTicks = 7;
#define JVC_ZERO_SPACE (JVC_ZERO_SPACE_TICKS * JVC_TICK) const uint16_t kJvcZeroSpace = kJvcZeroSpaceTicks * kJvcTick;
#define JVC_RPT_LENGTH_TICKS 800U const uint16_t kJvcRptLengthTicks = 800;
#define JVC_RPT_LENGTH (JVC_RPT_LENGTH_TICKS * JVC_TICK) const uint16_t kJvcRptLength = kJvcRptLengthTicks * kJvcTick;
#define JVC_MIN_GAP_TICKS (JVC_RPT_LENGTH_TICKS - \ const uint16_t kJvcMinGapTicks =
(JVC_HDR_MARK_TICKS + JVC_HDR_SPACE_TICKS + \ kJvcRptLengthTicks -
JVC_BITS * (JVC_BIT_MARK_TICKS + JVC_ONE_SPACE_TICKS) + \ (kJvcHdrMarkTicks + kJvcHdrSpaceTicks +
JVC_BIT_MARK_TICKS)) kJvcBits * (kJvcBitMarkTicks + kJvcOneSpaceTicks) + kJvcBitMarkTicks);
#define JVC_MIN_GAP (JVC_MIN_GAP_TICKS * JVC_TICK) const uint16_t kJvcMinGap = kJvcMinGapTicks * kJvcTick;
#if SEND_JVC #if SEND_JVC
// Send a JVC message. // Send a JVC message.
// //
// Args: // Args:
// data: The contents of the command you want to send. // data: The contents of the command you want to send.
// nbits: The bit size of the command being sent. (JVC_BITS) // nbits: The bit size of the command being sent. (kJvcBits)
// repeat: The number of times you want the command to be repeated. // repeat: The number of times you want the command to be repeated.
// //
// Status: STABLE. // Status: STABLE.
@@ -57,23 +57,21 @@ void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) {
IRtimer usecs = IRtimer(); IRtimer usecs = IRtimer();
// Header // Header
// Only sent for the first message. // Only sent for the first message.
mark(JVC_HDR_MARK); mark(kJvcHdrMark);
space(JVC_HDR_SPACE); space(kJvcHdrSpace);
// We always send the data & footer at least once, hence '<= repeat'. // We always send the data & footer at least once, hence '<= repeat'.
for (uint16_t i = 0; i <= repeat; i++) { for (uint16_t i = 0; i <= repeat; i++) {
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
JVC_BIT_MARK, JVC_ONE_SPACE, kJvcBitMark, kJvcOneSpace, kJvcBitMark, kJvcZeroSpace,
JVC_BIT_MARK, JVC_ZERO_SPACE, kJvcBitMark, kJvcMinGap, data, nbits, 38, true,
JVC_BIT_MARK, JVC_MIN_GAP, 0, // Repeats are handles elsewhere.
data, nbits, 38, true, 0, // Repeats are handles elsewhere.
33); 33);
// Wait till the end of the repeat time window before we send another code. // Wait till the end of the repeat time window before we send another code.
uint32_t elapsed = usecs.elapsed(); uint32_t elapsed = usecs.elapsed();
// Avoid potential unsigned integer underflow. // Avoid potential unsigned integer underflow.
// e.g. when elapsed > JVC_RPT_LENGTH. // e.g. when elapsed > kJvcRptLength.
if (elapsed < JVC_RPT_LENGTH) if (elapsed < kJvcRptLength) space(kJvcRptLength - elapsed);
space(JVC_RPT_LENGTH - elapsed);
usecs.reset(); usecs.reset();
} }
} }
@@ -100,7 +98,7 @@ uint16_t IRsend::encodeJVC(uint8_t address, uint8_t command) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits of data to expect. Typically JVC_BITS. // nbits: Nr. of bits of data to expect. Typically kJvcBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -112,49 +110,47 @@ uint16_t IRsend::encodeJVC(uint8_t address, uint8_t command) {
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/jvc.php // http://www.sbprojects.com/knowledge/ir/jvc.php
bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) {
if (strict && nbits != JVC_BITS) if (strict && nbits != kJvcBits)
return false; // Must be called with the correct nr. of bits. return false; // Must be called with the correct nr. of bits.
if (results->rawlen < 2 * nbits + FOOTER - 1) if (results->rawlen < 2 * nbits + kFooter - 1)
return false; // Can't possibly be a valid JVC message. return false; // Can't possibly be a valid JVC message.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
bool isRepeat = true; bool isRepeat = true;
uint32_t m_tick; uint32_t m_tick;
uint32_t s_tick; uint32_t s_tick;
// Header // Header
// (Optional as repeat codes don't have the header) // (Optional as repeat codes don't have the header)
if (matchMark(results->rawbuf[offset], JVC_HDR_MARK)) { if (matchMark(results->rawbuf[offset], kJvcHdrMark)) {
isRepeat = false; isRepeat = false;
m_tick = results->rawbuf[offset++] * RAWTICK / JVC_HDR_MARK_TICKS; m_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrMarkTicks;
if (results->rawlen < 2 * nbits + 4) if (results->rawlen < 2 * nbits + 4)
return false; // Can't possibly be a valid JVC message with a header. return false; // Can't possibly be a valid JVC message with a header.
if (!matchSpace(results->rawbuf[offset], JVC_HDR_SPACE)) if (!matchSpace(results->rawbuf[offset], kJvcHdrSpace)) return false;
return false; s_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrSpaceTicks;
s_tick = results->rawbuf[offset++] * RAWTICK / JVC_HDR_SPACE_TICKS;
} else { } else {
// We can't easily auto-calibrate as there is no header, so assume // We can't easily auto-calibrate as there is no header, so assume
// the default tick time. // the default tick time.
m_tick = JVC_TICK; m_tick = kJvcTick;
s_tick = JVC_TICK; s_tick = kJvcTick;
} }
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
JVC_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits, kJvcBitMarkTicks * m_tick,
JVC_ONE_SPACE_TICKS * s_tick, kJvcOneSpaceTicks * s_tick, kJvcBitMarkTicks * m_tick,
JVC_BIT_MARK_TICKS * m_tick, kJvcZeroSpaceTicks * s_tick);
JVC_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], JVC_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kJvcBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], JVC_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kJvcMinGapTicks * s_tick))
return false; return false;
// Success // Success
+158 -170
View File
@@ -30,128 +30,120 @@
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR // KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
// Constants // Constants
#define KELVINATOR_TICK 85U
#define KELVINATOR_HDR_MARK_TICKS 106U
#define KELVINATOR_HDR_MARK (KELVINATOR_HDR_MARK_TICKS * KELVINATOR_TICK)
#define KELVINATOR_HDR_SPACE_TICKS 53U
#define KELVINATOR_HDR_SPACE (KELVINATOR_HDR_SPACE_TICKS * KELVINATOR_TICK)
#define KELVINATOR_BIT_MARK_TICKS 8U
#define KELVINATOR_BIT_MARK (KELVINATOR_BIT_MARK_TICKS * KELVINATOR_TICK)
#define KELVINATOR_ONE_SPACE_TICKS 18U
#define KELVINATOR_ONE_SPACE (KELVINATOR_ONE_SPACE_TICKS * KELVINATOR_TICK)
#define KELVINATOR_ZERO_SPACE_TICKS 6U
#define KELVINATOR_ZERO_SPACE (KELVINATOR_ZERO_SPACE_TICKS * KELVINATOR_TICK)
#define KELVINATOR_GAP_SPACE_TICKS 235U
#define KELVINATOR_GAP_SPACE (KELVINATOR_GAP_SPACE_TICKS * KELVINATOR_TICK)
#define KELVINATOR_CMD_FOOTER 2U
#define KELVINATOR_CMD_FOOTER_BITS 3U
#define KELVINATOR_POWER 8U const uint16_t kKelvinatorTick = 85;
#define KELVINATOR_MODE_MASK 0xF8U const uint16_t kKelvinatorHdrMarkTicks = 106;
#define KELVINATOR_FAN_OFFSET 4U const uint16_t kKelvinatorHdrMark = kKelvinatorHdrMarkTicks * kKelvinatorTick;
#define KELVINATOR_BASIC_FAN_MASK uint8_t(0xFFU ^ (3U << KELVINATOR_FAN_OFFSET)) const uint16_t kKelvinatorHdrSpaceTicks = 53;
#define KELVINATOR_FAN_MASK uint8_t(0xFFU ^ (7U << KELVINATOR_FAN_OFFSET)) const uint16_t kKelvinatorHdrSpace = kKelvinatorHdrSpaceTicks * kKelvinatorTick;
#define KELVINATOR_CHECKSUM_START 10U const uint16_t kKelvinatorBitMarkTicks = 8;
#define KELVINATOR_VENT_SWING_OFFSET 6U const uint16_t kKelvinatorBitMark = kKelvinatorBitMarkTicks * kKelvinatorTick;
#define KELVINATOR_VENT_SWING uint8_t(1U << KELVINATOR_VENT_SWING_OFFSET) const uint16_t kKelvinatorOneSpaceTicks = 18;
#define KELVINATOR_VENT_SWING_V uint8_t(1U) const uint16_t kKelvinatorOneSpace = kKelvinatorOneSpaceTicks * kKelvinatorTick;
#define KELVINATOR_VENT_SWING_H uint8_t(1U << 4) const uint16_t kKelvinatorZeroSpaceTicks = 6;
#define KELVINATOR_SLEEP_1_AND_3 uint8_t(1U << 7) const uint16_t kKelvinatorZeroSpace =
#define KELVINATOR_QUIET_OFFSET 7U kKelvinatorZeroSpaceTicks * kKelvinatorTick;
#define KELVINATOR_QUIET uint8_t(1U << KELVINATOR_QUIET_OFFSET) const uint16_t kKelvinatorGapSpaceTicks = 235;
#define KELVINATOR_ION_FILTER_OFFSET 6U const uint16_t kKelvinatorGapSpace = kKelvinatorGapSpaceTicks * kKelvinatorTick;
#define KELVINATOR_ION_FILTER uint8_t(1U << KELVINATOR_ION_FILTER_OFFSET)
#define KELVINATOR_LIGHT_OFFSET 5U const uint8_t kKelvinatorCmdFooter = 2;
#define KELVINATOR_LIGHT uint8_t(1U << KELVINATOR_LIGHT_OFFSET) const uint8_t kKelvinatorCmdFooterBits = 3;
#define KELVINATOR_XFAN_OFFSET 7U
#define KELVINATOR_XFAN uint8_t(1U << KELVINATOR_XFAN_OFFSET) const uint8_t kKelvinatorPower = 8;
#define KELVINATOR_TURBO_OFFSET 4U const uint8_t kKelvinatorModeMask = 0xF8;
#define KELVINATOR_TURBO uint8_t(1U << KELVINATOR_TURBO_OFFSET) const uint8_t kKelvinatorFanOffset = 4;
const uint8_t kKelvinatorBasicFanMask = 0xFF ^ (3U << kKelvinatorFanOffset);
const uint8_t kKelvinatorFanMask = 0xFF ^ (7U << kKelvinatorFanOffset);
const uint8_t kKelvinatorChecksumStart = 10;
const uint8_t kKelvinatorVentSwingOffset = 6;
const uint8_t kKelvinatorVentSwing = 1 << kKelvinatorVentSwingOffset;
const uint8_t kKelvinatorVentSwingV = 1;
const uint8_t kKelvinatorVentSwingH = 1 << 4;
const uint8_t kKelvinatorSleep1And3 = 1 << 7;
const uint8_t kKelvinatorQuietOffset = 7;
const uint8_t kKelvinatorQuiet = 1 << kKelvinatorQuietOffset;
const uint8_t kKelvinatorIonFilterOffset = 6;
const uint8_t kKelvinatorIonFilter = 1 << kKelvinatorIonFilterOffset;
const uint8_t kKelvinatorLightOffset = 5;
const uint8_t kKelvinatorLight = 1 << kKelvinatorLightOffset;
const uint8_t kKelvinatorXfanOffset = 7;
const uint8_t kKelvinatorXfan = 1 << kKelvinatorXfanOffset;
const uint8_t kKelvinatorTurboOffset = 4;
const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset;
#if SEND_KELVINATOR #if SEND_KELVINATOR
// Send a Kelvinator A/C message. // Send a Kelvinator A/C message.
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=KELVINATOR_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kKelvinatorStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0). // repeat: Nr. of times the message is to be repeated. (Default = 0).
// //
// Status: STABLE / Known working. // Status: STABLE / Known working.
// //
void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < KELVINATOR_STATE_LENGTH) if (nbytes < kKelvinatorStateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
// Command Block #1 (4 bytes) // Command Block #1 (4 bytes)
sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE, sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
0, 0, // No Footer yet. 0, 0, // No Footer yet.
data, 4, 38, false, 0, 50); data, 4, 38, false, 0, 50);
// Send Footer for the command block (3 bits (B010)) // Send Footer for the command block (3 bits (b010))
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace,
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE, kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0,
KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS, 50);
38, false, 0, 50);
// Data Block #1 (4 bytes) // Data Block #1 (4 bytes)
sendGeneric(0, 0, // No header sendGeneric(0, 0, // No header
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, kKelvinatorZeroSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2, kKelvinatorGapSpace * 2, data + 4, 4, 38, false, 0, 50);
data + 4, 4, 38, false, 0, 50);
// Command Block #2 (4 bytes) // Command Block #2 (4 bytes)
sendGeneric(KELVINATOR_HDR_MARK, KELVINATOR_HDR_SPACE, sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE,
0, 0, // No Footer yet. 0, 0, // No Footer yet.
data + 8, 4, 38, false, 0, 50); data + 8, 4, 38, false, 0, 50);
// Send Footer for the command block (3 bits (B010)) // Send Footer for the command block (3 bits (B010))
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace,
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE, kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0,
KELVINATOR_CMD_FOOTER, KELVINATOR_CMD_FOOTER_BITS, 50);
38, false, 0, 50);
// Data Block #2 (4 bytes) // Data Block #2 (4 bytes)
sendGeneric(0, 0, // No header sendGeneric(0, 0, // No header
KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_ZERO_SPACE, kKelvinatorZeroSpace, kKelvinatorBitMark,
KELVINATOR_BIT_MARK, KELVINATOR_GAP_SPACE * 2, kKelvinatorGapSpace * 2, data + 12, 4, 38, false, 0, 50);
data + 12, 4, 38, false, 0, 50);
} }
} }
#endif // SEND_KELVINATOR #endif // SEND_KELVINATOR
IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
void IRKelvinatorAC::stateReset() { void IRKelvinatorAC::stateReset() {
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++) for (uint8_t i = 0; i < kKelvinatorStateLength; i++) remote_state[i] = 0x0;
remote_state[i] = 0x0;
remote_state[3] = 0x50; remote_state[3] = 0x50;
remote_state[11] = 0x70; remote_state[11] = 0x70;
} }
void IRKelvinatorAC::begin() { void IRKelvinatorAC::begin() { _irsend.begin(); }
_irsend.begin();
}
void IRKelvinatorAC::fixup() { void IRKelvinatorAC::fixup() {
// X-Fan mode is only valid in COOL or DRY modes. // X-Fan mode is only valid in COOL or DRY modes.
if (getMode() != KELVINATOR_COOL && getMode() != KELVINATOR_DRY) if (getMode() != kKelvinatorCool && getMode() != kKelvinatorDry)
setXFan(false); setXFan(false);
checksum(); // Calculate the checksums checksum(); // Calculate the checksums
} }
#if SEND_KELVINATOR #if SEND_KELVINATOR
void IRKelvinatorAC::send() { void IRKelvinatorAC::send(const uint16_t repeat) {
fixup(); // Ensure correct settings before sending. fixup(); // Ensure correct settings before sending.
_irsend.sendKelvinator(remote_state); _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat);
} }
#endif // SEND_KELVINATOR #endif // SEND_KELVINATOR
@@ -161,20 +153,19 @@ uint8_t* IRKelvinatorAC::getRaw() {
} }
void IRKelvinatorAC::setRaw(uint8_t new_code[]) { void IRKelvinatorAC::setRaw(uint8_t new_code[]) {
for (uint8_t i = 0; i < KELVINATOR_STATE_LENGTH; i++) { for (uint8_t i = 0; i < kKelvinatorStateLength; i++) {
remote_state[i] = new_code[i]; remote_state[i] = new_code[i];
} }
} }
uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block, uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block,
const uint16_t length) { const uint16_t length) {
uint8_t sum = KELVINATOR_CHECKSUM_START; uint8_t sum = kKelvinatorChecksumStart;
// Sum the lower half of the first 4 bytes of this block. // Sum the lower half of the first 4 bytes of this block.
for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++) for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++)
sum += (*block & 0x0FU); sum += (*block & 0x0FU);
// then sum the upper half of the next 3 bytes. // then sum the upper half of the next 3 bytes.
for (uint8_t i = 4; i < length - 1; i++, block++) for (uint8_t i = 4; i < length - 1; i++, block++) sum += (*block >> 4);
sum += (*block >> 4);
// Trim it down to fit into the 4 bits allowed. i.e. Mod 16. // Trim it down to fit into the 4 bits allowed. i.e. Mod 16.
return sum & 0x0FU; return sum & 0x0FU;
} }
@@ -205,12 +196,12 @@ bool IRKelvinatorAC::validChecksum(const uint8_t state[],
} }
void IRKelvinatorAC::on() { void IRKelvinatorAC::on() {
remote_state[0] |= KELVINATOR_POWER; remote_state[0] |= kKelvinatorPower;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
} }
void IRKelvinatorAC::off() { void IRKelvinatorAC::off() {
remote_state[0] &= ~KELVINATOR_POWER; remote_state[0] &= ~kKelvinatorPower;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
} }
@@ -222,140 +213,138 @@ void IRKelvinatorAC::setPower(bool state) {
} }
bool IRKelvinatorAC::getPower() { bool IRKelvinatorAC::getPower() {
return ((remote_state[0] & KELVINATOR_POWER) != 0); return ((remote_state[0] & kKelvinatorPower) != 0);
} }
// Set the temp. in deg C // Set the temp. in deg C
void IRKelvinatorAC::setTemp(uint8_t temp) { void IRKelvinatorAC::setTemp(uint8_t temp) {
temp = std::max((uint8_t) KELVINATOR_MIN_TEMP, temp); temp = std::max(kKelvinatorMinTemp, temp);
temp = std::min((uint8_t) KELVINATOR_MAX_TEMP, temp); temp = std::min(kKelvinatorMaxTemp, temp);
remote_state[1] = (remote_state[1] & 0xF0U) | (temp - KELVINATOR_MIN_TEMP); remote_state[1] = (remote_state[1] & 0xF0U) | (temp - kKelvinatorMinTemp);
remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk. remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk.
} }
// Return the set temp. in deg C // Return the set temp. in deg C
uint8_t IRKelvinatorAC::getTemp() { uint8_t IRKelvinatorAC::getTemp() {
return ((remote_state[1] & 0xFU) + KELVINATOR_MIN_TEMP); return ((remote_state[1] & 0xFU) + kKelvinatorMinTemp);
} }
// Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed // Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed
void IRKelvinatorAC::setFan(uint8_t fan) { void IRKelvinatorAC::setFan(uint8_t fan) {
fan = std::min((uint8_t) KELVINATOR_FAN_MAX, fan); // Bounds check fan = std::min(kKelvinatorFanMax, fan); // Bounds check
// Only change things if we need to. // Only change things if we need to.
if (fan != getFan()) { if (fan != getFan()) {
// Set the basic fan values. // Set the basic fan values.
uint8_t fan_basic = std::min((uint8_t) KELVINATOR_BASIC_FAN_MAX, fan); uint8_t fan_basic = std::min(kKelvinatorBasicFanMax, fan);
remote_state[0] = (remote_state[0] & KELVINATOR_BASIC_FAN_MASK) | remote_state[0] = (remote_state[0] & kKelvinatorBasicFanMask) |
(fan_basic << KELVINATOR_FAN_OFFSET); (fan_basic << kKelvinatorFanOffset);
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
// Set the advanced(?) fan value. // Set the advanced(?) fan value.
remote_state[14] = (remote_state[14] & KELVINATOR_FAN_MASK) | remote_state[14] =
(fan << KELVINATOR_FAN_OFFSET); (remote_state[14] & kKelvinatorFanMask) | (fan << kKelvinatorFanOffset);
setTurbo(false); // Turbo mode is turned off if we change the fan settings. setTurbo(false); // Turbo mode is turned off if we change the fan settings.
} }
} }
uint8_t IRKelvinatorAC::getFan() { uint8_t IRKelvinatorAC::getFan() {
return ((remote_state[14] & ~KELVINATOR_FAN_MASK) >> KELVINATOR_FAN_OFFSET); return ((remote_state[14] & ~kKelvinatorFanMask) >> kKelvinatorFanOffset);
} }
uint8_t IRKelvinatorAC::getMode() { uint8_t IRKelvinatorAC::getMode() {
return (remote_state[0] & ~KELVINATOR_MODE_MASK); return (remote_state[0] & ~kKelvinatorModeMask);
} }
void IRKelvinatorAC::setMode(uint8_t mode) { void IRKelvinatorAC::setMode(uint8_t mode) {
// If we get an unexpected mode, default to AUTO. // If we get an unexpected mode, default to AUTO.
if (mode > KELVINATOR_HEAT) mode = KELVINATOR_AUTO; if (mode > kKelvinatorHeat) mode = kKelvinatorAuto;
remote_state[0] = (remote_state[0] & KELVINATOR_MODE_MASK) | mode; remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode;
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
if (mode == KELVINATOR_AUTO || KELVINATOR_DRY) if (mode == kKelvinatorAuto || kKelvinatorDry)
// When the remote is set to Auto or Dry, it defaults to 25C and doesn't // When the remote is set to Auto or Dry, it defaults to 25C and doesn't
// show it. // show it.
setTemp(KELVINATOR_AUTO_TEMP); setTemp(kKelvinatorAutoTemp);
} }
void IRKelvinatorAC::setSwingVertical(bool state) { void IRKelvinatorAC::setSwingVertical(bool state) {
if (state) { if (state) {
remote_state[0] |= KELVINATOR_VENT_SWING; remote_state[0] |= kKelvinatorVentSwing;
remote_state[4] |= KELVINATOR_VENT_SWING_V; remote_state[4] |= kKelvinatorVentSwingV;
} else { } else {
remote_state[4] &= ~KELVINATOR_VENT_SWING_V; remote_state[4] &= ~kKelvinatorVentSwingV;
if (!getSwingHorizontal()) if (!getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing;
remote_state[0] &= ~KELVINATOR_VENT_SWING;
} }
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getSwingVertical() { bool IRKelvinatorAC::getSwingVertical() {
return ((remote_state[4] & KELVINATOR_VENT_SWING_V) != 0); return ((remote_state[4] & kKelvinatorVentSwingV) != 0);
} }
void IRKelvinatorAC::setSwingHorizontal(bool state) { void IRKelvinatorAC::setSwingHorizontal(bool state) {
if (state) { if (state) {
remote_state[0] |= KELVINATOR_VENT_SWING; remote_state[0] |= kKelvinatorVentSwing;
remote_state[4] |= KELVINATOR_VENT_SWING_H; remote_state[4] |= kKelvinatorVentSwingH;
} else { } else {
remote_state[4] &= ~KELVINATOR_VENT_SWING_H; remote_state[4] &= ~kKelvinatorVentSwingH;
if (!getSwingVertical()) if (!getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing;
remote_state[0] &= ~KELVINATOR_VENT_SWING;
} }
remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getSwingHorizontal() { bool IRKelvinatorAC::getSwingHorizontal() {
return ((remote_state[4] & KELVINATOR_VENT_SWING_H) != 0); return ((remote_state[4] & kKelvinatorVentSwingH) != 0);
} }
void IRKelvinatorAC::setQuiet(bool state) { void IRKelvinatorAC::setQuiet(bool state) {
remote_state[12] &= ~KELVINATOR_QUIET; remote_state[12] &= ~kKelvinatorQuiet;
remote_state[12] |= (state << KELVINATOR_QUIET_OFFSET); remote_state[12] |= (state << kKelvinatorQuietOffset);
} }
bool IRKelvinatorAC::getQuiet() { bool IRKelvinatorAC::getQuiet() {
return ((remote_state[12] & KELVINATOR_QUIET) != 0); return ((remote_state[12] & kKelvinatorQuiet) != 0);
} }
void IRKelvinatorAC::setIonFilter(bool state) { void IRKelvinatorAC::setIonFilter(bool state) {
remote_state[2] &= ~KELVINATOR_ION_FILTER; remote_state[2] &= ~kKelvinatorIonFilter;
remote_state[2] |= (state << KELVINATOR_ION_FILTER_OFFSET); remote_state[2] |= (state << kKelvinatorIonFilterOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getIonFilter() { bool IRKelvinatorAC::getIonFilter() {
return ((remote_state[2] & KELVINATOR_ION_FILTER) != 0); return ((remote_state[2] & kKelvinatorIonFilter) != 0);
} }
void IRKelvinatorAC::setLight(bool state) { void IRKelvinatorAC::setLight(bool state) {
remote_state[2] &= ~KELVINATOR_LIGHT; remote_state[2] &= ~kKelvinatorLight;
remote_state[2] |= (state << KELVINATOR_LIGHT_OFFSET); remote_state[2] |= (state << kKelvinatorLightOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getLight() { bool IRKelvinatorAC::getLight() {
return ((remote_state[2] & KELVINATOR_LIGHT) != 0); return ((remote_state[2] & kKelvinatorLight) != 0);
} }
// Note: XFan mode is only valid in Cool or Dry mode. // Note: XFan mode is only valid in Cool or Dry mode.
void IRKelvinatorAC::setXFan(bool state) { void IRKelvinatorAC::setXFan(bool state) {
remote_state[2] &= ~KELVINATOR_XFAN; remote_state[2] &= ~kKelvinatorXfan;
remote_state[2] |= (state << KELVINATOR_XFAN_OFFSET); remote_state[2] |= (state << kKelvinatorXfanOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getXFan() { bool IRKelvinatorAC::getXFan() {
return ((remote_state[2] & KELVINATOR_XFAN) != 0); return ((remote_state[2] & kKelvinatorXfan) != 0);
} }
// Note: Turbo mode is turned off if the fan speed is changed. // Note: Turbo mode is turned off if the fan speed is changed.
void IRKelvinatorAC::setTurbo(bool state) { void IRKelvinatorAC::setTurbo(bool state) {
remote_state[2] &= ~KELVINATOR_TURBO; remote_state[2] &= ~kKelvinatorTurbo;
remote_state[2] |= (state << KELVINATOR_TURBO_OFFSET); remote_state[2] |= (state << kKelvinatorTurboOffset);
remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk.
} }
bool IRKelvinatorAC::getTurbo() { bool IRKelvinatorAC::getTurbo() {
return ((remote_state[2] & KELVINATOR_TURBO) != 0); return ((remote_state[2] & kKelvinatorTurbo) != 0);
} }
// Convert the internal state into a human readable string. // Convert the internal state into a human readable string.
@@ -373,19 +362,19 @@ std::string IRKelvinatorAC::toString() {
result += "Off"; result += "Off";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case KELVINATOR_AUTO: case kKelvinatorAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case KELVINATOR_COOL: case kKelvinatorCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case KELVINATOR_HEAT: case kKelvinatorHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case KELVINATOR_DRY: case kKelvinatorDry:
result += " (DRY)"; result += " (DRY)";
break; break;
case KELVINATOR_FAN: case kKelvinatorFan:
result += " (FAN)"; result += " (FAN)";
break; break;
default: default:
@@ -394,10 +383,10 @@ std::string IRKelvinatorAC::toString() {
result += ", Temp: " + uint64ToString(getTemp()) + "C"; result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan()); result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) { switch (getFan()) {
case KELVINATOR_FAN_AUTO: case kKelvinatorFanAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case KELVINATOR_FAN_MAX: case kKelvinatorFanMax:
result += " (MAX)"; result += " (MAX)";
break; break;
} }
@@ -444,7 +433,7 @@ std::string IRKelvinatorAC::toString() {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically KELVINATOR_BITS. // nbits: The number of data bits to expect. Typically kKelvinatorBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -452,14 +441,14 @@ std::string IRKelvinatorAC::toString() {
// Status: ALPHA / Untested. // Status: ALPHA / Untested.
bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * (nbits + KELVINATOR_CMD_FOOTER_BITS) + if (results->rawlen <
(HEADER + FOOTER + 1) * 2 - 1) 2 * (nbits + kKelvinatorCmdFooterBits) + (kHeader + kFooter + 1) * 2 - 1)
return false; // Can't possibly be a valid Kelvinator message. return false; // Can't possibly be a valid Kelvinator message.
if (strict && nbits != KELVINATOR_BITS) if (strict && nbits != kKelvinatorBits)
return false; // Not strictly a Kelvinator message. return false; // Not strictly a Kelvinator message.
uint32_t data; uint32_t data;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// There are two messages back-to-back in a full Kelvinator IR message // There are two messages back-to-back in a full Kelvinator IR message
// sequence. // sequence.
@@ -468,77 +457,76 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
match_result_t data_result; match_result_t data_result;
// Header // Header
if (!matchMark(results->rawbuf[offset], KELVINATOR_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kKelvinatorHdrMark)) return false;
// Calculate how long the lowest tick time is based on the header mark. // Calculate how long the lowest tick time is based on the header mark.
uint32_t mark_tick = results->rawbuf[offset++] * RAWTICK / uint32_t mark_tick =
KELVINATOR_HDR_MARK_TICKS; results->rawbuf[offset++] * kRawTick / kKelvinatorHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], KELVINATOR_HDR_SPACE)) if (!matchSpace(results->rawbuf[offset], kKelvinatorHdrSpace)) return false;
return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t space_tick = results->rawbuf[offset++] * RAWTICK / uint32_t space_tick =
KELVINATOR_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kKelvinatorHdrSpaceTicks;
// Data (Command) (32 bits) // Data (Command) (32 bits)
data_result = matchData(&(results->rawbuf[offset]), 32, data_result = matchData(
KELVINATOR_BIT_MARK_TICKS * mark_tick, &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ONE_SPACE_TICKS * space_tick, kKelvinatorOneSpaceTicks * space_tick,
KELVINATOR_BIT_MARK_TICKS * mark_tick, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ZERO_SPACE_TICKS * space_tick); kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Record command data in the state. // Record command data in the state.
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) for (uint16_t i = 0; i < 4; i++, data >>= 8)
results->state[i] = reverseBits(data & 0xFF, 8); results->state[state_pos + i] = data & 0xFF;
state_pos += 4; state_pos += 4;
// Command data footer (3 bits, B010) // Command data footer (3 bits, B010)
data_result = matchData(&(results->rawbuf[offset]), data_result = matchData(
KELVINATOR_CMD_FOOTER_BITS, &(results->rawbuf[offset]), kKelvinatorCmdFooterBits,
KELVINATOR_BIT_MARK_TICKS * mark_tick, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ONE_SPACE_TICKS * space_tick, kKelvinatorOneSpaceTicks * space_tick,
KELVINATOR_BIT_MARK_TICKS * mark_tick, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ZERO_SPACE_TICKS * space_tick); kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
if (data_result.data != KELVINATOR_CMD_FOOTER) return false; if (data_result.data != kKelvinatorCmdFooter) return false;
offset += data_result.used; offset += data_result.used;
// Interdata gap. // Interdata gap.
if (!matchMark(results->rawbuf[offset++], if (!matchMark(results->rawbuf[offset++],
KELVINATOR_BIT_MARK_TICKS * mark_tick)) kKelvinatorBitMarkTicks * mark_tick))
return false; return false;
if (!matchSpace(results->rawbuf[offset++], if (!matchSpace(results->rawbuf[offset++],
KELVINATOR_GAP_SPACE_TICKS * space_tick)) kKelvinatorGapSpaceTicks * space_tick))
return false; return false;
// Data (Options) (32 bits) // Data (Options) (32 bits)
data_result = matchData(&(results->rawbuf[offset]), 32, data_result = matchData(
KELVINATOR_BIT_MARK_TICKS * mark_tick, &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ONE_SPACE_TICKS * space_tick, kKelvinatorOneSpaceTicks * space_tick,
KELVINATOR_BIT_MARK_TICKS * mark_tick, kKelvinatorBitMarkTicks * mark_tick,
KELVINATOR_ZERO_SPACE_TICKS * space_tick); kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Record option data in the state. // Record option data in the state.
for (int i = state_pos + 3; i >= state_pos; i--, data >>= 8) for (uint16_t i = 0; i < 4; i++, data >>= 8)
results->state[i] = reverseBits(data & 0xFF, 8); results->state[state_pos + i] = data & 0xFF;
state_pos += 4; state_pos += 4;
// Inter-sequence gap. (Double length gap) // Inter-sequence gap. (Double length gap)
if (!matchMark(results->rawbuf[offset++], if (!matchMark(results->rawbuf[offset++],
KELVINATOR_BIT_MARK_TICKS * mark_tick)) kKelvinatorBitMarkTicks * mark_tick))
return false; return false;
if (s == 0) { if (s == 0) {
if (!matchSpace(results->rawbuf[offset++], if (!matchSpace(results->rawbuf[offset++],
KELVINATOR_GAP_SPACE_TICKS * space_tick * 2)) kKelvinatorGapSpaceTicks * space_tick * 2))
return false; return false;
} else { } else {
if (offset <= results->rawlen && if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], !matchAtLeast(results->rawbuf[offset],
KELVINATOR_GAP_SPACE_TICKS * 2 * space_tick)) kKelvinatorGapSpaceTicks * 2 * space_tick))
return false; return false;
} }
} }
@@ -546,7 +534,7 @@ bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits,
// Compliance // Compliance
if (strict) { if (strict) {
// Correct size/length) // Correct size/length)
if (state_pos != KELVINATOR_STATE_LENGTH) return false; if (state_pos != kKelvinatorStateLength) return false;
// Verify the message's checksum is correct. // Verify the message's checksum is correct.
if (!IRKelvinatorAC::validChecksum(results->state)) return false; if (!IRKelvinatorAC::validChecksum(results->state)) return false;
} }
+29 -17
View File
@@ -22,17 +22,30 @@
// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR // KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR
// Constants // Constants
#define KELVINATOR_AUTO 0U const uint8_t kKelvinatorAuto = 0;
#define KELVINATOR_COOL 1U const uint8_t kKelvinatorCool = 1;
#define KELVINATOR_DRY 2U const uint8_t kKelvinatorDry = 2;
#define KELVINATOR_FAN 3U const uint8_t kKelvinatorFan = 3;
#define KELVINATOR_HEAT 4U const uint8_t kKelvinatorHeat = 4;
#define KELVINATOR_BASIC_FAN_MAX 3U const uint8_t kKelvinatorBasicFanMax = 3;
#define KELVINATOR_FAN_AUTO 0U const uint8_t kKelvinatorFanAuto = 0;
#define KELVINATOR_FAN_MAX 5U const uint8_t kKelvinatorFanMax = 5;
#define KELVINATOR_MIN_TEMP 16U // 16C const uint8_t kKelvinatorMinTemp = 16; // 16C
#define KELVINATOR_MAX_TEMP 30U // 30C const uint8_t kKelvinatorMaxTemp = 30; // 30C
#define KELVINATOR_AUTO_TEMP 25U // 25C const uint8_t kKelvinatorAutoTemp = 25; // 25C
// Legacy defines (Deprecated)
#define KELVINATOR_MIN_TEMP kKelvinatorMinTemp
#define KELVINATOR_MAX_TEMP kKelvinatorMaxTemp
#define KELVINATOR_HEAT kKelvinatorHeat
#define KELVINATOR_FAN_MAX kKelvinatorFanMax
#define KELVINATOR_FAN_AUTO kKelvinatorFanAuto
#define KELVINATOR_FAN kKelvinatorFan
#define KELVINATOR_DRY kKelvinatorDry
#define KELVINATOR_COOL kKelvinatorCool
#define KELVINATOR_BASIC_FAN_MAX kKelvinatorBasicFanMax
#define KELVINATOR_AUTO_TEMP kKelvinatorAutoTemp
#define KELVINATOR_AUTO kKelvinatorAuto
/* /*
Kelvinator AC map Kelvinator AC map
@@ -117,7 +130,7 @@ class IRKelvinatorAC {
void stateReset(); void stateReset();
#if SEND_KELVINATOR #if SEND_KELVINATOR
void send(); void send(const uint16_t repeat = kKelvinatorDefaultRepeat);
#endif // SEND_KELVINATOR #endif // SEND_KELVINATOR
void begin(); void begin();
void on(); void on();
@@ -147,10 +160,9 @@ class IRKelvinatorAC {
uint8_t* getRaw(); uint8_t* getRaw();
void setRaw(uint8_t new_code[]); void setRaw(uint8_t new_code[]);
static uint8_t calcBlockChecksum( static uint8_t calcBlockChecksum(
const uint8_t *block, const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2);
const uint16_t length = KELVINATOR_STATE_LENGTH / 2);
static bool validChecksum(const uint8_t state[], static bool validChecksum(const uint8_t state[],
const uint16_t length = KELVINATOR_STATE_LENGTH); const uint16_t length = kKelvinatorStateLength);
#ifdef ARDUINO #ifdef ARDUINO
String toString(); String toString();
#else #else
@@ -159,8 +171,8 @@ class IRKelvinatorAC {
private: private:
// The state of the IR remote in IR code form. // The state of the IR remote in IR code form.
uint8_t remote_state[KELVINATOR_STATE_LENGTH]; uint8_t remote_state[kKelvinatorStateLength];
void checksum(const uint16_t length = KELVINATOR_STATE_LENGTH); void checksum(const uint16_t length = kKelvinatorStateLength);
void fixup(); void fixup();
IRsend _irsend; IRsend _irsend;
}; };
+142 -70
View File
@@ -1,6 +1,6 @@
// Copyright 2015 Darryl Smith // Copyright 2015 Darryl Smith
// Copyright 2015 cheaplin // Copyright 2015 cheaplin
// Copyright 2017 David Conran // Copyright 2017, 2018 David Conran
#include "ir_LG.h" #include "ir_LG.h"
#include <algorithm> #include <algorithm>
@@ -16,31 +16,44 @@
// LG decode originally added by Darryl Smith (based on the JVC protocol) // LG decode originally added by Darryl Smith (based on the JVC protocol)
// LG send originally added by https://github.com/chaeplin // LG send originally added by https://github.com/chaeplin
//
// Known supported devices:
// IR Remotes:
// 6711A20083V
// AKB74395308
// Constants // Constants
#define LG_TICK 50U const uint16_t kLgTick = 50;
#define LG_HDR_MARK_TICKS 160U const uint16_t kLgHdrMarkTicks = 170;
#define LG_HDR_MARK (LG_HDR_MARK_TICKS * LG_TICK) const uint16_t kLgHdrMark = kLgHdrMarkTicks * kLgTick; // 8500
#define LG_HDR_SPACE_TICKS 80U const uint16_t kLgHdrSpaceTicks = 85;
#define LG_HDR_SPACE (LG_HDR_SPACE_TICKS * LG_TICK) const uint16_t kLgHdrSpace = kLgHdrSpaceTicks * kLgTick; // 4250
#define LG_BIT_MARK_TICKS 11U const uint16_t kLgBitMarkTicks = 11;
#define LG_BIT_MARK (LG_BIT_MARK_TICKS * LG_TICK) const uint16_t kLgBitMark = kLgBitMarkTicks * kLgTick; // 550
#define LG_ONE_SPACE_TICKS 32U const uint16_t kLgOneSpaceTicks = 32;
#define LG_ONE_SPACE (LG_ONE_SPACE_TICKS * LG_TICK) const uint16_t kLgOneSpace = kLgOneSpaceTicks * kLgTick; // 1600
#define LG_ZERO_SPACE_TICKS 11U const uint16_t kLgZeroSpaceTicks = 11;
#define LG_ZERO_SPACE (LG_ZERO_SPACE_TICKS * LG_TICK) const uint16_t kLgZeroSpace = kLgZeroSpaceTicks * kLgTick; // 550
#define LG_RPT_SPACE_TICKS 45U const uint16_t kLgRptSpaceTicks = 45;
#define LG_RPT_SPACE (LG_RPT_SPACE_TICKS * LG_TICK) const uint16_t kLgRptSpace = kLgRptSpaceTicks * kLgTick; // 2250
#define LG_MIN_GAP_TICKS 795U const uint16_t kLgMinGapTicks = 795;
#define LG_MIN_GAP (LG_MIN_GAP_TICKS * LG_TICK) const uint16_t kLgMinGap = kLgMinGapTicks * kLgTick; // 39750
#define LG_MIN_MESSAGE_LENGTH_TICKS 2161U const uint16_t kLgMinMessageLengthTicks = 2161;
#define LG_MIN_MESSAGE_LENGTH (LG_MIN_MESSAGE_LENGTH_TICKS * LG_TICK) const uint32_t kLgMinMessageLength = kLgMinMessageLengthTicks * kLgTick;
#define LG32_HDR_MARK_TICKS 90U
#define LG32_HDR_MARK (LG32_HDR_MARK_TICKS * LG_TICK) const uint16_t kLg32HdrMarkTicks = 90;
#define LG32_HDR_SPACE_TICKS 89U const uint16_t kLg32HdrMark = kLg32HdrMarkTicks * kLgTick; // 4500
#define LG32_HDR_SPACE (LG32_HDR_SPACE_TICKS * LG_TICK) const uint16_t kLg32HdrSpaceTicks = 89;
#define LG32_RPT_HDR_MARK_TICKS 179U const uint16_t kLg32HdrSpace = kLg32HdrSpaceTicks * kLgTick; // 4450
#define LG32_RPT_HDR_MARK (LG32_RPT_HDR_MARK_TICKS * LG_TICK) const uint16_t kLg32RptHdrMarkTicks = 179;
const uint16_t kLg32RptHdrMark = kLg32RptHdrMarkTicks * kLgTick; // 8950
const uint16_t kLg2HdrMarkTicks = 64;
const uint16_t kLg2HdrMark = kLg2HdrMarkTicks * kLgTick; // 3200
const uint16_t kLg2HdrSpaceTicks = 197;
const uint16_t kLg2HdrSpace = kLg2HdrSpaceTicks * kLgTick; // 9850
const uint16_t kLg2BitMarkTicks = 10;
const uint16_t kLg2BitMark = kLg2BitMarkTicks * kLgTick; // 500
#if (SEND_LG || DECODE_LG) #if (SEND_LG || DECODE_LG)
// Calculate the rolling 4-bit wide checksum over all of the data. // Calculate the rolling 4-bit wide checksum over all of the data.
@@ -50,7 +63,8 @@
// A 4-bit checksum. // A 4-bit checksum.
uint8_t calcLGChecksum(uint16_t data) { uint8_t calcLGChecksum(uint16_t data) {
return (((data >> 12) + ((data >> 8) & 0xF) + ((data >> 4) & 0xF) + return (((data >> 12) + ((data >> 8) & 0xF) + ((data >> 4) & 0xF) +
(data & 0xF)) & 0xF); (data & 0xF)) &
0xF);
} }
#endif #endif
@@ -60,50 +74,83 @@ uint8_t calcLGChecksum(uint16_t data) {
// Args: // Args:
// data: The contents of the message you want to send. // data: The contents of the message you want to send.
// nbits: The bit size of the message being sent. // nbits: The bit size of the message being sent.
// Typically LG_BITS or LG32_BITS. // Typically kLgBits or kLg32Bits.
// repeat: The number of times you want the message to be repeated. // repeat: The number of times you want the message to be repeated.
// //
// Status: Beta / Should be working. // Status: Beta / Should be working.
// //
// Notes: // Notes:
// LG has a separate message to indicate a repeat, like NEC does. // LG has a separate message to indicate a repeat, like NEC does.
// Supports:
// IR Remote models: 6711A20083V
void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint16_t repeatHeaderMark = 0; uint16_t repeatHeaderMark = 0;
if (nbits >= LG32_BITS) { if (nbits >= kLg32Bits) {
// LG 32bit protocol is near identical to Samsung except for repeats. // LG 32bit protocol is near identical to Samsung except for repeats.
sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message. sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message.
repeatHeaderMark = LG32_RPT_HDR_MARK; repeatHeaderMark = kLg32RptHdrMark;
repeat++; repeat++;
} else { } else {
// LG (28-bit) protocol. // LG (28-bit) protocol.
repeatHeaderMark = LG_HDR_MARK; repeatHeaderMark = kLgHdrMark;
sendGeneric(LG_HDR_MARK, LG_HDR_SPACE, sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark,
LG_BIT_MARK, LG_ONE_SPACE, kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data,
LG_BIT_MARK, LG_ZERO_SPACE, nbits, 38, true, 0, // Repeats are handled later.
LG_BIT_MARK,
LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH,
data, nbits, 38, true, 0, // Repeats are handled later.
50); 50);
} }
// Repeat // Repeat
// Protocol has a mandatory repeat-specific code sent after every command. // Protocol has a mandatory repeat-specific code sent after every command.
if (repeat) if (repeat)
sendGeneric(repeatHeaderMark, LG_RPT_SPACE, sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent.
0, 0, 0, 0, // No data is sent. kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data.
LG_BIT_MARK, LG_MIN_GAP, LG_MIN_MESSAGE_LENGTH,
0, 0, // No data.
38, true, repeat - 1, 50); 38, true, repeat - 1, 50);
} }
// Construct a raw 28-bit LG message from the supplied address & command. // Send an LG Variant-2 formatted message.
//
// Args:
// data: The contents of the message you want to send.
// nbits: The bit size of the message being sent.
// Typically kLgBits or kLg32Bits.
// repeat: The number of times you want the message to be repeated.
//
// Status: Beta / Should be working.
//
// Notes:
// LG has a separate message to indicate a repeat, like NEC does.
// Supports:
// IR Remote models: AKB74395308
void IRsend::sendLG2(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits >= kLg32Bits) {
// Let the original routine handle it.
sendLG(data, nbits, repeat); // Send it as a single Samsung message.
return;
}
// LGv2 (28-bit) protocol.
sendGeneric(kLg2HdrMark, kLg2HdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark,
kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data,
nbits, 38, true, 0, // Repeats are handled later.
50);
// TODO(crackn): Verify the details of what repeat messages look like.
// Repeat
// Protocol has a mandatory repeat-specific code sent after every command.
if (repeat)
sendGeneric(kLg2HdrMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent.
kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data.
38, true, repeat - 1, 50);
}
// Construct a raw 28-bit LG message code from the supplied address & command.
// //
// Args: // Args:
// address: The address code. // address: The address code.
// command: The command code. // command: The command code.
// Returns: // Returns:
// A raw 28-bit LG message suitable for sendLG(). // A raw 28-bit LG message code suitable for sendLG() etc.
// //
// Status: BETA / Should work. // Status: BETA / Should work.
// //
@@ -125,7 +172,7 @@ uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) {
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion. // nbits: Nr. of bits to expect in the data portion.
// Typically LG_BITS or LG32_BITS. // Typically kLgBits or kLg32Bits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -135,69 +182,91 @@ uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) {
// Note: // Note:
// LG 32bit protocol appears near identical to the Samsung protocol. // LG 32bit protocol appears near identical to the Samsung protocol.
// They possibly differ on how they repeat and initial HDR mark. // They possibly differ on how they repeat and initial HDR mark.
//
// Supports:
// IR Remote models: 6711A20083V, AKB74395308
// Ref: // Ref:
// https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/ // https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/
bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {
if (nbits >= LG32_BITS) { if (nbits >= kLg32Bits) {
if (results->rawlen < 2 * nbits + 2 * (HEADER + FOOTER) - 1) if (results->rawlen < 2 * nbits + 2 * (kHeader + kFooter) - 1)
return false; // Can't possibly be a valid LG32 message. return false; // Can't possibly be a valid LG32 message.
} else { } else {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid LG message. return false; // Can't possibly be a valid LG message.
} }
if (strict && nbits != LG_BITS && nbits != LG32_BITS) if (strict && nbits != kLgBits && nbits != kLg32Bits)
return false; // Doesn't comply with expected LG protocol. return false; // Doesn't comply with expected LG protocol.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
bool isLg2 = false;
// Header // Header
if (!matchMark(results->rawbuf[offset], LG_HDR_MARK) &&
!matchMark(results->rawbuf[offset], LG32_HDR_MARK)) return false;
uint32_t m_tick; uint32_t m_tick;
if (matchMark(results->rawbuf[offset], LG_HDR_MARK)) if (matchMark(results->rawbuf[offset], kLgHdrMark)) {
m_tick = results->rawbuf[offset++] * RAWTICK / LG_HDR_MARK_TICKS; m_tick = results->rawbuf[offset++] * kRawTick / kLgHdrMarkTicks;
else } else if (matchMark(results->rawbuf[offset], kLg2HdrMark)) {
m_tick = results->rawbuf[offset++] * RAWTICK / LG32_HDR_MARK_TICKS; m_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], LG_HDR_SPACE) && isLg2 = true;
!matchSpace(results->rawbuf[offset], LG32_HDR_SPACE)) return false; } else if (matchMark(results->rawbuf[offset], kLg32HdrMark)) {
m_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrMarkTicks;
} else {
return false;
}
uint32_t s_tick; uint32_t s_tick;
if (matchSpace(results->rawbuf[offset], LG_HDR_SPACE)) if (isLg2) {
s_tick = results->rawbuf[offset++] * RAWTICK / LG_HDR_SPACE_TICKS; if (matchSpace(results->rawbuf[offset], kLg2HdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrSpaceTicks;
else else
s_tick = results->rawbuf[offset++] * RAWTICK / LG32_HDR_SPACE_TICKS; return false;
} else {
if (matchSpace(results->rawbuf[offset], kLgHdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLgHdrSpaceTicks;
else if (matchSpace(results->rawbuf[offset], kLg2HdrSpace))
s_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrSpaceTicks;
else
return false;
}
// Set up the expected tick sizes based on variant.
uint16_t bitmarkticks;
if (isLg2) {
bitmarkticks = kLg2BitMarkTicks;
} else {
bitmarkticks = kLgBitMarkTicks;
}
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
LG_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits, bitmarkticks * m_tick,
LG_ONE_SPACE_TICKS * s_tick, kLgOneSpaceTicks * s_tick, bitmarkticks * m_tick,
LG_BIT_MARK_TICKS * m_tick, kLgZeroSpaceTicks * s_tick, kTolerance, 0);
LG_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], LG_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick))
return false; return false;
// Repeat // Repeat
if (nbits >= LG32_BITS) { if (nbits >= kLg32Bits) {
// If we are expecting the LG 32-bit protocol, there is always // If we are expecting the LG 32-bit protocol, there is always
// a repeat message. So, check for it. // a repeat message. So, check for it.
offset++; offset++;
if (!matchMark(results->rawbuf[offset++], LG32_RPT_HDR_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kLg32RptHdrMarkTicks * m_tick))
return false; return false;
if (!matchSpace(results->rawbuf[offset++], LG_RPT_SPACE_TICKS * s_tick)) if (!matchSpace(results->rawbuf[offset++], kLgRptSpaceTicks * s_tick))
return false; return false;
if (!matchMark(results->rawbuf[offset++], LG_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], LG_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick))
return false; return false;
} }
@@ -208,6 +277,9 @@ bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) {
return false; // The last 4 bits sent are the expected checksum. return false; // The last 4 bits sent are the expected checksum.
// Success // Success
if (isLg2)
results->decode_type = LG2;
else
results->decode_type = LG; results->decode_type = LG;
results->bits = nbits; results->bits = nbits;
results->value = data; results->value = data;
+26 -28
View File
@@ -12,14 +12,14 @@
// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG // LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG
// Constants // Constants
#define MIN_LASERTAG_SAMPLES 13U const uint16_t kLasertagMinSamples = 13;
#define LASERTAG_TICK 333U const uint16_t kLasertagTick = 333;
#define LASERTAG_MIN_GAP 100000UL // Completely made up amount. const uint32_t kLasertagMinGap = 100000; // Completely made up amount.
#define LASERTAG_TOLERANCE 0U // Percentage error margin const uint8_t kLasertagTolerance = 0; // Percentage error margin.
#define LASERTAG_EXCESS 0U // See MARK_EXCESS const uint16_t kLasertagExcess = 0; // See kMarkExcess.
#define LASERTAG_DELTA 150U // Use instead of EXCESS and TOLERANCE. const uint16_t kLasertagDelta = 150; // Use instead of Excess and Tolerance.
const int16_t kSPACE = 1; const int16_t kSpace = 1;
const int16_t kMARK = 0; const int16_t kMark = 0;
#if SEND_LASERTAG #if SEND_LASERTAG
// Send a Lasertag packet. // Send a Lasertag packet.
@@ -32,10 +32,8 @@ const int16_t kMARK = 0;
// //
// Status: STABLE / Working. // Status: STABLE / Working.
// //
void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits > sizeof(data) * 8) if (nbits > sizeof(data) * 8) return; // We can't send something that big.
return; // We can't send something that big.
// Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle.
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols. // NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
@@ -45,14 +43,14 @@ void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Data // Data
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
if (data & mask) { // 1 if (data & mask) { // 1
space(LASERTAG_TICK); // 1 is space, then mark. space(kLasertagTick); // 1 is space, then mark.
mark(LASERTAG_TICK); mark(kLasertagTick);
} else { // 0 } else { // 0
mark(LASERTAG_TICK); // 0 is mark, then space. mark(kLasertagTick); // 0 is mark, then space.
space(LASERTAG_TICK); space(kLasertagTick);
} }
// Footer // Footer
space(LASERTAG_MIN_GAP); space(kLasertagMinGap);
} }
} }
#endif // SEND_LASERTAG #endif // SEND_LASERTAG
@@ -76,12 +74,12 @@ void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) {
// https://en.wikipedia.org/wiki/Manchester_code // https://en.wikipedia.org/wiki/Manchester_code
bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits, bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < MIN_LASERTAG_SAMPLES) return false; if (results->rawlen < kLasertagMinSamples) return false;
// Compliance // Compliance
if (strict && nbits != LASERTAG_BITS) return false; if (strict && nbits != kLasertagBits) return false;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t used = 0; uint16_t used = 0;
uint64_t data = 0; uint64_t data = 0;
uint16_t actual_bits = 0; uint16_t actual_bits = 0;
@@ -90,16 +88,16 @@ bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits,
// Data // Data
for (; offset <= results->rawlen; actual_bits++) { for (; offset <= results->rawlen; actual_bits++) {
int16_t levelA = getRClevel(results, &offset, &used, LASERTAG_TICK, int16_t levelA =
LASERTAG_TOLERANCE, LASERTAG_EXCESS, getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance,
LASERTAG_DELTA); kLasertagExcess, kLasertagDelta);
int16_t levelB = getRClevel(results, &offset, &used, LASERTAG_TICK, int16_t levelB =
LASERTAG_TOLERANCE, LASERTAG_EXCESS, getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance,
LASERTAG_DELTA); kLasertagExcess, kLasertagDelta);
if (levelA == kSPACE && levelB == kMARK) { if (levelA == kSpace && levelB == kMark) {
data = (data << 1) | 1; // 1 data = (data << 1) | 1; // 1
} else { } else {
if (levelA == kMARK && levelB == kSPACE) { if (levelA == kMark && levelB == kSpace) {
data <<= 1; // 0 data <<= 1; // 0
} else { } else {
break; break;
@@ -110,7 +108,7 @@ bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits,
// Compliance // Compliance
if (actual_bits < nbits) return false; // Less data than we expected. if (actual_bits < nbits) return false; // Less data than we expected.
if (strict && actual_bits != LASERTAG_BITS) return false; if (strict && actual_bits != kLasertagBits) return false;
// Success // Success
results->decode_type = LASERTAG; results->decode_type = LASERTAG;
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2018 David Conran
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// LL UU UU TTTTTTT RRRRRR OOOOO NN NN
// LL UU UU TTT RR RR OO OO NNN NN
// LL UU UU TTT RRRRRR OO OO NN N NN
// LL UU UU TTT RR RR OO OO NN NNN
// LLLLLLL UUUUU TTT RR RR OOOO0 NN NN
// Notes:
// The Lutron protocol uses a sort of Run Length encoding to encode
// its data. There is no header or footer per-se.
// As a mark is the first data we will notice, we always assume the First
// bit of the technically 36-bit protocol is '1'. So it is assumed, and thus
// we only care about the 35 bits of data.
// Constants
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/515
const uint16_t kLutronTick = 2288;
const uint32_t kLutronGap = 150000; // Completely made up value.
const uint16_t kLutronDelta = 400; // +/- 300 usecs.
#if SEND_LUTRON
// Send a Lutron formatted message.
//
// Args:
// data: The message to be sent.
// nbits: The number of bits of the message to be sent. Typically kLutronBits
// repeat: The number of times the command is to be repeated.
//
// Status: Stable / Appears to be working for real devices.
// Notes:
// Protocol is really 36 bits long, but the first bit is always a 1.
// So, assume the 1 and only have a normal payload of 35 bits.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/515
void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) {
enableIROut(40000, 40); // 40Khz & 40% dutycycle.
for (uint16_t r = 0; r <= repeat; r++) {
mark(kLutronTick); // 1st bit is always '1'.
// Send the supplied data in MSB First order.
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
if (data & mask)
mark(kLutronTick); // Send a 1
else
space(kLutronTick); // Send a 0
space(kLutronGap); // Inter-message gap.
}
}
#endif // SEND_LUTRON
#if DECODE_LUTRON
// Decode the supplied Lutron 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 kLutronBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Untested.
//
// Notes:
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/515
bool IRrecv::decodeLutron(decode_results *results, uint16_t nbits,
bool strict) {
// Technically the smallest number of entries for the smallest message is '1'.
// i.e. All the bits set to 1, would produce a single huge mark signal.
// So no minimum length check is required.
if (strict && nbits != kLutronBits)
return false; // Not strictly an Lutron message.
uint64_t data = 0;
int16_t bitsSoFar = -1;
if (nbits > sizeof(data) * 8) return false; // To large to store the data.
for (uint16_t offset = kStartOffset;
bitsSoFar < nbits && offset < results->rawlen; offset++) {
uint16_t entry = results->rawbuf[offset];
// It has to be large enough to qualify as a bit.
if (!matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) {
DPRINTLN("Entry too small. Aborting.");
return false;
}
// Keep reading bits of the same value until we run out.
while (entry != 0 && matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) {
bitsSoFar++;
DPRINT("Bit: ");
DPRINT(bitsSoFar);
if (offset % 2) { // Is Odd?
data = (data << 1) + 1; // Append a '1'.
DPRINTLN(" is a 1.");
} else { // Is it Even?
data <<= 1; // Append a '0'.
DPRINTLN(" is a 0.");
if (bitsSoFar == nbits && matchAtLeast(entry, kLutronGap))
break; // We've likely reached the end of a message.
}
// Remove a bit length from the current entry.
entry = std::max(entry, (uint16_t)(kLutronTick / kRawTick)) -
kLutronTick / kRawTick;
}
if (offset % 2 && !match(entry, kLutronDelta, 0, kLutronDelta)) {
DPRINT("offset = ");
DPRINTLN(offset);
DPRINT("rawlen = ");
DPRINTLN(results->rawlen);
DPRINT("entry = ");
DPRINTLN(entry);
DPRINTLN("Odd Entry has too much left over. Aborting.");
return false; // Too much left over to be a good value. Reject it.
}
if (offset % 2 == 0 && offset <= results->rawlen - 1 &&
!matchAtLeast(entry, kLutronDelta, 0, kLutronDelta)) {
DPRINT("offset = ");
DPRINTLN(offset);
DPRINT("rawlen = ");
DPRINTLN(results->rawlen);
DPRINT("entry = ");
DPRINTLN(entry);
DPRINTLN("Entry has too much left over. Aborting.");
return false; // Too much left over to be a good value. Reject it.
}
}
// We got too many bits.
if (bitsSoFar > nbits || bitsSoFar < 0) {
DPRINTLN("Wrong number of bits found. Aborting.");
return false;
}
// If we got less bits than we were expecting, we need to pad with zeros
// until we get the correct number of bits.
if (bitsSoFar < nbits) data <<= (nbits - bitsSoFar);
// Success
DPRINTLN("Lutron Success!");
results->decode_type = LUTRON;
results->bits = bitsSoFar;
results->value = data ^ (1ULL << nbits); // Mask off the initial '1'.
results->address = 0;
results->command = 0;
return true;
}
#endif // DECODE_LUTRON
+202
View File
@@ -0,0 +1,202 @@
// Copyright 2018 Brett T. Warden
// derived from ir_Lasertag.cpp, Copyright 2017 David Conran
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// MM MM WW WW MM MM
// MMM MMM WW WW MMM MMM
// MM M MM WW W WW MM M MM
// MM MM WWW WWW MM MM
// MM MM WW WW MM MM
// Constants
const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2
// samples
const uint16_t kMWMTick = 417;
const uint32_t kMWMMinGap = 30000; // Typical observed delay b/w commands
const uint8_t kMWMTolerance = 0; // Percentage error margin.
const uint16_t kMWMExcess = 0; // See kMarkExcess.
const uint16_t kMWMDelta = 150; // Use instead of Excess and Tolerance.
const uint8_t kMWMMaxWidth = 9; // Maximum number of successive bits at a
// single level - worst case
const int16_t kSpace = 1;
const int16_t kMark = 0;
#if SEND_MWM
// Send a MWM packet.
// This protocol is 2400 bps serial, 1 start bit (mark), 1 stop bit (space), no
// parity
//
// Args:
// data: The message you wish to send.
// nbits: Bit size of the protocol you want to send.
// repeat: Nr. of extra times the data will be sent.
//
// Status: Implemented.
//
void IRsend::sendMWM(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
if (nbytes < 3) return; // Shortest possible message is 3 bytes
// Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle.
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
enableIROut(38, 25);
for (uint16_t r = 0; r <= repeat; r++) {
// Data
for (uint16_t i = 0; i < nbytes; i++) {
uint8_t byte = data[i];
// Start bit
mark(kMWMTick);
// LSB first, space=1
for (uint8_t mask = 0x1; mask; mask <<= 1) {
if (byte & mask) { // 1
space(kMWMTick);
} else { // 0
mark(kMWMTick);
}
}
// Stop bit
space(kMWMTick);
}
// Footer
space(kMWMMinGap);
}
}
#endif // SEND_MWM
#if DECODE_MWM
// Decode the supplied MWM message.
// This protocol is 2400 bps serial, 1 start bit (mark), 1 stop bit (space), no
// parity
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: Implemented.
//
bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) {
DPRINTLN("DEBUG: decodeMWM");
// Compliance
if (results->rawlen < kMWMMinSamples) {
DPRINTLN("DEBUG: decodeMWM: too few samples");
return false;
}
uint16_t offset = kStartOffset;
uint16_t used = 0;
uint64_t data = 0;
uint16_t frame_bits = 0;
uint16_t data_bits = 0;
// No Header
// Data
uint8_t bits_per_frame = 10;
for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax;
frame_bits++) {
DPRINT("DEBUG: decodeMWM: offset = ");
DPRINTLN(uint64ToString(offset));
int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance,
kMWMExcess, kMWMDelta, kMWMMaxWidth);
if (level < 0) {
DPRINTLN("DEBUG: decodeMWM: getRClevel returned error");
break;
}
switch (frame_bits % bits_per_frame) {
case 0:
// Start bit
if (level != kMark) {
DPRINTLN("DEBUG: decodeMWM: framing error - invalid start bit");
goto done;
}
break;
case 9:
// Stop bit
if (level != kSpace) {
DPRINTLN("DEBUG: decodeMWM: framing error - invalid stop bit");
return false;
} else {
DPRINT("DEBUG: decodeMWM: data_bits = ");
DPRINTLN(data_bits);
DPRINT("DEBUG: decodeMWM: Finished byte: ");
DPRINTLN(data);
results->state[data_bits / 8 - 1] = data & 0xFF;
results->bits = data_bits;
data = 0;
}
break;
default:
// Data bits
DPRINT("DEBUG: decodeMWM: Storing bit: ");
DPRINTLN((level == kSpace));
// Transmission is LSB-first, space=1
data |= ((level == kSpace)) << 8;
data >>= 1;
data_bits++;
break;
}
}
done:
// Footer (None)
// Compliance
DPRINT("DEBUG: decodeMWM: frame_bits = ");
DPRINTLN(frame_bits);
DPRINT("DEBUG: decodeMWM: data_bits = ");
DPRINTLN(data_bits);
if (data_bits < nbits) {
DPRINT("DEBUG: decodeMWM: too few bits; expected ");
DPRINTLN(nbits);
return false; // Less data than we expected.
}
uint16_t payload_length = 0;
switch (results->state[0] & 0xf0) {
case 0x90:
case 0xf0:
// Normal commands
payload_length = results->state[0] & 0x0f;
DPRINT("DEBUG: decodeMWM: payload_length = ");
DPRINTLN(payload_length);
break;
default:
if (strict) {
// Show commands
if (results->state[0] != 0x55 && results->state[1] != 0xAA) {
return false;
}
}
break;
}
if (data_bits < (payload_length + 3) * 8) {
DPRINT("DEBUG: decodeMWM: too few bytes; expected ");
DPRINTLN((payload_length + 3));
return false;
}
if (strict) {
if (payload_length && (data_bits > (payload_length + 3) * 8)) {
DPRINT("DEBUG: decodeMWM: too many bytes; expected ");
DPRINTLN((payload_length + 3));
return false;
}
}
// Success
results->decode_type = MWM;
results->repeat = false;
return true;
}
#endif // DECODE_MWM
// vim: et:ts=2:sw=2
+28 -22
View File
@@ -8,8 +8,8 @@
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
#define IS_ZERO(m, s) (((m) * 100 / ((m) + (s))) <= MAGIQUEST_ZERO_RATIO) #define IS_ZERO(m, s) (((m)*100 / ((m) + (s))) <= kMagiQuestZeroRatio)
#define IS_ONE(m, s) (((m) * 100 / ((m) + (s))) >= MAGIQUEST_ONE_RATIO) #define IS_ONE(m, s) (((m)*100 / ((m) + (s))) >= kMagiQuestOneRatio)
// Strips taken from: // Strips taken from:
// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp // https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
@@ -24,7 +24,7 @@
// Args: // Args:
// data: The contents of the message you want to send. // data: The contents of the message you want to send.
// nbits: The bit size of the message being sent. // nbits: The bit size of the message being sent.
// Typically MAGIQUEST_BITS. // Typically kMagiquestBits.
// repeat: The number of times you want the message to be repeated. // repeat: The number of times you want the message to be repeated.
// //
// Status: Alpha / Should be working. // Status: Alpha / Should be working.
@@ -32,11 +32,10 @@
void IRsend::sendMagiQuest(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendMagiQuest(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(0, 0, // No Headers - Technically it's included in the data. sendGeneric(0, 0, // No Headers - Technically it's included in the data.
// i.e. 8 zeros. // i.e. 8 zeros.
MAGIQUEST_MARK_ONE, MAGIQUEST_SPACE_ONE, kMagiQuestMarkOne, kMagiQuestSpaceOne, kMagiQuestMarkZero,
MAGIQUEST_MARK_ZERO, MAGIQUEST_SPACE_ZERO, kMagiQuestSpaceZero,
0, // No footer mark. 0, // No footer mark.
MAGIQUEST_GAP, kMagiQuestGap, data, nbits, 36, true, repeat, 50);
data, nbits, 36, true, repeat, 50);
} }
// Encode a MagiQuest wand_id, and a magnitude into a single 64bit value. // Encode a MagiQuest wand_id, and a magnitude into a single 64bit value.
@@ -54,7 +53,8 @@ uint64_t IRsend::encodeMagiQuest(uint32_t wand_id, uint16_t magnitude) {
} }
#endif #endif
// Source: https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp // Source:
// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
#if DECODE_MAGIQUEST #if DECODE_MAGIQUEST
// Decode the supplied MagiQuest message. // Decode the supplied MagiQuest message.
@@ -68,7 +68,7 @@ uint64_t IRsend::encodeMagiQuest(uint32_t wand_id, uint16_t magnitude) {
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion, inc. the 8 bit header. // nbits: Nr. of bits to expect in the data portion, inc. the 8 bit header.
// Typically MAGIQUEST_BITS. // Typically kMagiquestBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -81,18 +81,18 @@ bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
uint16_t bits = 0; uint16_t bits = 0;
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
if (results->rawlen < (2 * MAGIQUEST_BITS)) { if (results->rawlen < (2 * kMagiquestBits)) {
DPRINT("Not enough bits to be Magiquest - Rawlen: "); DPRINT("Not enough bits to be Magiquest - Rawlen: ");
DPRINT(results->rawlen); DPRINT(results->rawlen);
DPRINT(" Expected: "); DPRINT(" Expected: ");
DPRINTLN((2 * MAGIQUEST_BITS)); DPRINTLN((2 * kMagiquestBits));
return false; return false;
} }
// Compliance // Compliance
if (strict && nbits != MAGIQUEST_BITS) return false; if (strict && nbits != kMagiquestBits) return false;
// Of six wands as datapoints, so far they all start with 8 ZEROs. // Of six wands as datapoints, so far they all start with 8 ZEROs.
// For example, here is the data from two wands // For example, here is the data from two wands
@@ -103,7 +103,7 @@ bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits,
while (offset + 1 < results->rawlen && bits < nbits - 1) { while (offset + 1 < results->rawlen && bits < nbits - 1) {
uint16_t mark = results->rawbuf[offset]; uint16_t mark = results->rawbuf[offset];
uint16_t space = results->rawbuf[offset + 1]; uint16_t space = results->rawbuf[offset + 1];
if (!matchMark(mark + space, MAGIQUEST_TOTAL_USEC)) { if (!matchMark(mark + space, kMagiQuestTotalUsec)) {
DPRINT("Not enough time to be Magiquest - Mark: "); DPRINT("Not enough time to be Magiquest - Mark: ");
DPRINT(mark); DPRINT(mark);
DPRINT(" Space: "); DPRINT(" Space: ");
@@ -111,13 +111,16 @@ bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits,
DPRINT(" Total: "); DPRINT(" Total: ");
DPRINT(mark + space); DPRINT(mark + space);
DPRINT("Expected: "); DPRINT("Expected: ");
DPRINTLN(MAGIQUEST_TOTAL_USEC); DPRINTLN(kMagiQuestTotalUsec);
return false; return false;
} }
if (IS_ZERO(mark, space)) data = (data << 1) | 0; if (IS_ZERO(mark, space))
else if (IS_ONE( mark, space)) data = (data << 1) | 1; data = (data << 1) | 0;
else return false; else if (IS_ONE(mark, space))
data = (data << 1) | 1;
else
return false;
bits++; bits++;
offset += 2; offset += 2;
@@ -132,11 +135,14 @@ bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits,
// Grab the last MARK bit, assuming a good SPACE after it // Grab the last MARK bit, assuming a good SPACE after it
if (offset < results->rawlen) { if (offset < results->rawlen) {
uint16_t mark = results->rawbuf[offset]; uint16_t mark = results->rawbuf[offset];
uint16_t space = (MAGIQUEST_TOTAL_USEC / RAWTICK) - mark; uint16_t space = (kMagiQuestTotalUsec / kRawTick) - mark;
if (IS_ZERO(mark, space)) data = (data << 1) | 0; if (IS_ZERO(mark, space))
else if (IS_ONE( mark, space)) data = (data << 1) | 1; data = (data << 1) | 0;
else return false; else if (IS_ONE(mark, space))
data = (data << 1) | 1;
else
return false;
bits++; bits++;
} }
+8 -10
View File
@@ -24,14 +24,12 @@ union magiquest {
} cmd; } cmd;
}; };
#define MAGIQUEST_TOTAL_USEC 1150U const uint16_t kMagiQuestTotalUsec = 1150;
#define MAGIQUEST_ZERO_RATIO 30U // usually <= ~25% const uint8_t kMagiQuestZeroRatio = 30; // usually <= ~25%
#define MAGIQUEST_ONE_RATIO 38U // usually >= ~50% const uint8_t kMagiQuestOneRatio = 38; // usually >= ~50%
const uint16_t kMagiQuestMarkZero = 280;
#define MAGIQUEST_PERIOD 1150U const uint16_t kMagiQuestSpaceZero = 850;
#define MAGIQUEST_MARK_ZERO 280U const uint16_t kMagiQuestMarkOne = 580;
#define MAGIQUEST_SPACE_ZERO 850U const uint16_t kMagiQuestSpaceOne = 600;
#define MAGIQUEST_MARK_ONE 580U const uint32_t kMagiQuestGap = 100000; // A guess of the gap between messages
#define MAGIQUEST_SPACE_ONE 600U
#define MAGIQUEST_GAP 100000UL // A guess of the gap between messages
#endif // IR_MAGIQUEST_H_ #endif // IR_MAGIQUEST_H_
+87 -110
View File
@@ -26,35 +26,34 @@
// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing // https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
// Constants // Constants
#define MIDEA_TICK 80U const uint16_t kMideaTick = 80;
#define MIDEA_BIT_MARK_TICKS 7U const uint16_t kMideaBitMarkTicks = 7;
#define MIDEA_BIT_MARK (MIDEA_BIT_MARK_TICKS * MIDEA_TICK) const uint16_t kMideaBitMark = kMideaBitMarkTicks * kMideaTick;
#define MIDEA_ONE_SPACE_TICKS 21U const uint16_t kMideaOneSpaceTicks = 21;
#define MIDEA_ONE_SPACE (MIDEA_ONE_SPACE_TICKS * MIDEA_TICK) const uint16_t kMideaOneSpace = kMideaOneSpaceTicks * kMideaTick;
#define MIDEA_ZERO_SPACE_TICKS 7U const uint16_t kMideaZeroSpaceTicks = 7;
#define MIDEA_ZERO_SPACE (MIDEA_ZERO_SPACE_TICKS * MIDEA_TICK) const uint16_t kMideaZeroSpace = kMideaZeroSpaceTicks * kMideaTick;
#define MIDEA_HDR_MARK_TICKS 56U const uint16_t kMideaHdrMarkTicks = 56;
#define MIDEA_HDR_MARK (MIDEA_HDR_MARK_TICKS * MIDEA_TICK) const uint16_t kMideaHdrMark = kMideaHdrMarkTicks * kMideaTick;
#define MIDEA_HDR_SPACE_TICKS 56U const uint16_t kMideaHdrSpaceTicks = 56;
#define MIDEA_HDR_SPACE (MIDEA_HDR_SPACE_TICKS * MIDEA_TICK) const uint16_t kMideaHdrSpace = kMideaHdrSpaceTicks * kMideaTick;
#define MIDEA_MIN_GAP_TICKS (MIDEA_HDR_MARK_TICKS + MIDEA_ZERO_SPACE_TICKS \ const uint16_t kMideaMinGapTicks =
+ MIDEA_BIT_MARK_TICKS) kMideaHdrMarkTicks + kMideaZeroSpaceTicks + kMideaBitMarkTicks;
#define MIDEA_MIN_GAP (MIDEA_MIN_GAP_TICKS * MIDEA_TICK) const uint16_t kMideaMinGap = kMideaMinGapTicks * kMideaTick;
#define MIDEA_TOLERANCE 30U // Percent const uint8_t kMideaTolerance = 30; // Percent
#if SEND_MIDEA #if SEND_MIDEA
// Send a Midea message // Send a Midea message
// //
// Args: // Args:
// data: Contents of the message to be sent. // data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically MIDEA_BITS. // nbits: Nr. of bits of data to be sent. Typically kMideaBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: Alpha / Needs testing against a real device. // Status: Alpha / Needs testing against a real device.
// //
void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits % 8 != 0) if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.
return; // nbits is required to be a multiple of 8.
// Set IR carrier frequency // Set IR carrier frequency
enableIROut(38); enableIROut(38);
@@ -64,21 +63,20 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
// inverted payload. // inverted payload.
for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) { for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) {
// Header // Header
mark(MIDEA_HDR_MARK); mark(kMideaHdrMark);
space(MIDEA_HDR_SPACE); space(kMideaHdrSpace);
// Data // Data
// Break data into byte segments, starting at the Most Significant // Break data into byte segments, starting at the Most Significant
// Byte. Each byte then being sent normal, then followed inverted. // Byte. Each byte then being sent normal, then followed inverted.
for (uint16_t i = 8; i <= nbits; i += 8) { for (uint16_t i = 8; i <= nbits; i += 8) {
// Grab a bytes worth of data. // Grab a bytes worth of data.
uint8_t segment = (data >> (nbits - i)) & 0xFF; uint8_t segment = (data >> (nbits - i)) & 0xFF;
sendData(MIDEA_BIT_MARK, MIDEA_ONE_SPACE, sendData(kMideaBitMark, kMideaOneSpace, kMideaBitMark, kMideaZeroSpace,
MIDEA_BIT_MARK, MIDEA_ZERO_SPACE,
segment, 8, true); segment, 8, true);
} }
// Footer // Footer
mark(MIDEA_BIT_MARK); mark(kMideaBitMark);
space(MIDEA_MIN_GAP); // Pause before repeating space(kMideaMinGap); // Pause before repeating
// Invert the data for the 2nd phase of the message. // Invert the data for the 2nd phase of the message.
// As we get called twice in the inner loop, we will always revert // As we get called twice in the inner loop, we will always revert
@@ -93,9 +91,7 @@ void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Warning: Consider this very alpha code. // Warning: Consider this very alpha code.
// Initialise the object. // Initialise the object.
IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
// Reset the state of the remote to a known good state/sequence. // Reset the state of the remote to a known good state/sequence.
void IRMideaAC::stateReset() { void IRMideaAC::stateReset() {
@@ -104,38 +100,32 @@ void IRMideaAC::stateReset() {
} }
// Configure the pin for output. // Configure the pin for output.
void IRMideaAC::begin() { void IRMideaAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_MIDEA #if SEND_MIDEA
// Send the current desired state to the IR LED. // Send the current desired state to the IR LED.
void IRMideaAC::send() { void IRMideaAC::send(const uint16_t repeat) {
checksum(); // Ensure correct checksum before sending. checksum(); // Ensure correct checksum before sending.
_irsend.sendMidea(remote_state); _irsend.sendMidea(remote_state, kMideaBits, repeat);
} }
#endif // SEND_MIDEA #endif // SEND_MIDEA
// Return a pointer to the internal state date of the remote. // Return a pointer to the internal state date of the remote.
uint64_t IRMideaAC::getRaw() { uint64_t IRMideaAC::getRaw() {
checksum(); checksum();
return remote_state & MIDEA_AC_STATE_MASK; return remote_state & kMideaACStateMask;
} }
// Override the internal state with the new state. // Override the internal state with the new state.
void IRMideaAC::setRaw(uint64_t newState) { void IRMideaAC::setRaw(uint64_t newState) {
remote_state = newState & MIDEA_AC_STATE_MASK; remote_state = newState & kMideaACStateMask;
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRMideaAC::on() { void IRMideaAC::on() { remote_state |= kMideaACPower; }
remote_state |= MIDEA_AC_POWER;
}
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRMideaAC::off() { void IRMideaAC::off() { remote_state &= (kMideaACStateMask ^ kMideaACPower); }
remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_POWER);
}
// Set the requested power state of the A/C. // Set the requested power state of the A/C.
void IRMideaAC::setPower(const bool state) { void IRMideaAC::setPower(const bool state) {
@@ -146,35 +136,33 @@ void IRMideaAC::setPower(const bool state) {
} }
// Return the requested power state of the A/C. // Return the requested power state of the A/C.
bool IRMideaAC::getPower() { bool IRMideaAC::getPower() { return (remote_state & kMideaACPower); }
return (remote_state & MIDEA_AC_POWER);
}
// Set the temperature. // Set the temperature.
// Args: // Args:
// temp: Temp. in degrees. // temp: Temp. in degrees.
// useCelsius: Degree type to use. celsius (true) or fahrenheit (false) // useCelsius: Degree type to use. Celsius (true) or Fahrenheit (false)
void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) {
uint8_t new_temp = temp; uint8_t new_temp = temp;
if (useCelsius) { if (useCelsius) {
new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_C, new_temp); new_temp = std::max(kMideaACMinTempC, new_temp);
new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_C, new_temp); new_temp = std::min(kMideaACMaxTempC, new_temp);
new_temp = (uint8_t)((new_temp * 1.8) + 32.5); // 0.5 so we rounding. new_temp = (uint8_t)((new_temp * 1.8) + 32.5); // 0.5 so we rounding.
} }
new_temp = std::max((uint8_t) MIDEA_AC_MIN_TEMP_F, new_temp); new_temp = std::max(kMideaACMinTempF, new_temp);
new_temp = std::min((uint8_t) MIDEA_AC_MAX_TEMP_F, new_temp); new_temp = std::min(kMideaACMaxTempF, new_temp);
new_temp -= MIDEA_AC_MIN_TEMP_F; new_temp -= kMideaACMinTempF;
remote_state &= MIDEA_AC_TEMP_MASK; remote_state &= kMideaACTempMask;
remote_state |= ((uint64_t)new_temp << 24); remote_state |= ((uint64_t)new_temp << 24);
} }
// Return the set temp. // Return the set temp.
// Args: // Args:
// useCelsius: Flag indicating if the results are in celsius or fahrenheit. // useCelsius: Flag indicating if the results are in Celsius or Fahrenheit.
// Returns: // Returns:
// A uint8_t containing the temperature. // A uint8_t containing the temperature.
uint8_t IRMideaAC::getTemp(const bool useCelsius) { uint8_t IRMideaAC::getTemp(const bool useCelsius) {
uint8_t temp = ((remote_state >> 24) & 0x1F) + MIDEA_AC_MIN_TEMP_F; uint8_t temp = ((remote_state >> 24) & 0x1F) + kMideaACMinTempF;
if (useCelsius) { if (useCelsius) {
temp = (uint8_t)((temp - 32) / 1.8); temp = (uint8_t)((temp - 32) / 1.8);
} }
@@ -186,61 +174,55 @@ uint8_t IRMideaAC::getTemp(const bool useCelsius) {
void IRMideaAC::setFan(const uint8_t fan) { void IRMideaAC::setFan(const uint8_t fan) {
uint64_t new_fan; uint64_t new_fan;
switch (fan) { switch (fan) {
case MIDEA_AC_FAN_LOW: case kMideaACFanLow:
case MIDEA_AC_FAN_MED: case kMideaACFanMed:
case MIDEA_AC_FAN_HI: case kMideaACFanHigh:
new_fan = fan; new_fan = fan;
break; break;
default: default:
new_fan = MIDEA_AC_FAN_AUTO; new_fan = kMideaACFanAuto;
} }
remote_state &= MIDEA_AC_FAN_MASK; remote_state &= kMideaACFanMask;
remote_state |= (new_fan << 35); remote_state |= (new_fan << 35);
} }
// Return the requested state of the unit's fan. // Return the requested state of the unit's fan.
uint8_t IRMideaAC::getFan() { uint8_t IRMideaAC::getFan() { return (remote_state >> 35) & 0b111; }
return (remote_state >> 35) & 0b111;
}
// Get the requested climate operation mode of the a/c unit. // Get the requested climate operation mode of the a/c unit.
// Returns: // Returns:
// A uint8_t containing the A/C mode. // A uint8_t containing the A/C mode.
uint8_t IRMideaAC::getMode() { uint8_t IRMideaAC::getMode() { return ((remote_state >> 32) & 0b111); }
return ((remote_state >> 32) & 0b111);
}
// Set the requested climate operation mode of the a/c unit. // Set the requested climate operation mode of the a/c unit.
void IRMideaAC::setMode(const uint8_t mode) { void IRMideaAC::setMode(const uint8_t mode) {
// If we get an unexpected mode, default to AUTO. // If we get an unexpected mode, default to AUTO.
uint64_t new_mode; uint64_t new_mode;
switch (mode) { switch (mode) {
case MIDEA_AC_AUTO: case kMideaACAuto:
case MIDEA_AC_COOL: case kMideaACCool:
case MIDEA_AC_HEAT: case kMideaACHeat:
case MIDEA_AC_DRY: case kMideaACDry:
case MIDEA_AC_FAN: case kMideaACFan:
new_mode = mode; new_mode = mode;
break; break;
default: default:
new_mode = MIDEA_AC_AUTO; new_mode = kMideaACAuto;
} }
remote_state &= MIDEA_AC_MODE_MASK; remote_state &= kMideaACModeMask;
remote_state |= (new_mode << 32); remote_state |= (new_mode << 32);
} }
// Set the Sleep state of the A/C. // Set the Sleep state of the A/C.
void IRMideaAC::setSleep(const bool state) { void IRMideaAC::setSleep(const bool state) {
if (state) if (state)
remote_state |= MIDEA_AC_SLEEP; remote_state |= kMideaACSleep;
else else
remote_state &= (MIDEA_AC_STATE_MASK ^ MIDEA_AC_SLEEP); remote_state &= (kMideaACStateMask ^ kMideaACSleep);
} }
// Return the Sleep state of the A/C. // Return the Sleep state of the A/C.
bool IRMideaAC::getSleep() { bool IRMideaAC::getSleep() { return (remote_state & kMideaACSleep); }
return (remote_state & MIDEA_AC_SLEEP);
}
// Calculate the checksum for a given array. // Calculate the checksum for a given array.
// Args: // Args:
@@ -271,7 +253,7 @@ bool IRMideaAC::validChecksum(const uint64_t state) {
// Calculate & set the checksum for the current internal state of the remote. // Calculate & set the checksum for the current internal state of the remote.
void IRMideaAC::checksum() { void IRMideaAC::checksum() {
// Stored the checksum value in the last byte. // Stored the checksum value in the last byte.
remote_state &= MIDEA_AC_CHECKSUM_MASK; remote_state &= kMideaACChecksumMask;
remote_state |= calcChecksum(remote_state); remote_state |= calcChecksum(remote_state);
} }
@@ -290,19 +272,19 @@ std::string IRMideaAC::toString() {
result += "Off"; result += "Off";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case MIDEA_AC_AUTO: case kMideaACAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case MIDEA_AC_COOL: case kMideaACCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case MIDEA_AC_HEAT: case kMideaACHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case MIDEA_AC_DRY: case kMideaACDry:
result += " (DRY)"; result += " (DRY)";
break; break;
case MIDEA_AC_FAN: case kMideaACFan:
result += " (FAN)"; result += " (FAN)";
break; break;
default: default:
@@ -312,16 +294,16 @@ std::string IRMideaAC::toString() {
uint64ToString(getTemp(false)) + "F"; uint64ToString(getTemp(false)) + "F";
result += ", Fan: " + uint64ToString(getFan()); result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) { switch (getFan()) {
case MIDEA_AC_FAN_AUTO: case kMideaACFanAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case MIDEA_AC_FAN_LOW: case kMideaACFanLow:
result += " (LOW)"; result += " (LOW)";
break; break;
case MIDEA_AC_FAN_MED: case kMideaACFanMed:
result += " (MED)"; result += " (MED)";
break; break;
case MIDEA_AC_FAN_HI: case kMideaACFanHigh:
result += " (HI)"; result += " (HI)";
break; break;
} }
@@ -338,55 +320,51 @@ std::string IRMideaAC::toString() {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically MIDEA_BITS. // nbits: The number of data bits to expect. Typically kMideaBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
// //
// Status: Alpha / Needs testing against a real device. // Status: Alpha / Needs testing against a real device.
// //
bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) {
bool strict) {
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
return false; return false;
uint8_t min_nr_of_messages = 1; uint8_t min_nr_of_messages = 1;
if (strict) { if (strict) {
if (nbits != MIDEA_BITS) if (nbits != kMideaBits) return false; // Not strictly a MIDEA message.
return false; // Not strictly a MIDEA message.
min_nr_of_messages = 2; min_nr_of_messages = 2;
} }
// The protocol sends the data normal + inverted, alternating on // The protocol sends the data normal + inverted, alternating on
// each byte. Hence twice the number of expected data bits. // each byte. Hence twice the number of expected data bits.
if (results->rawlen < min_nr_of_messages * (2 * nbits + HEADER + FOOTER) - 1) if (results->rawlen <
min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1)
return false; // Can't possibly be a valid MIDEA message. return false; // Can't possibly be a valid MIDEA message.
uint64_t data = 0; uint64_t data = 0;
uint64_t inverted = 0; uint64_t inverted = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
if (nbits > sizeof(data) * 8) if (nbits > sizeof(data) * 8)
return false; // We can't possibly capture a Midea packet that big. return false; // We can't possibly capture a Midea packet that big.
for (uint8_t i = 0; i < min_nr_of_messages; i++) { for (uint8_t i = 0; i < min_nr_of_messages; i++) {
// Header // Header
if (!matchMark(results->rawbuf[offset], MIDEA_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kMideaHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kMideaHdrMarkTicks;
MIDEA_HDR_MARK_TICKS; if (!matchSpace(results->rawbuf[offset], kMideaHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset], MIDEA_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick =
MIDEA_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kMideaHdrSpaceTicks;
// Data (Normal) // Data (Normal)
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result = matchData(
MIDEA_BIT_MARK_TICKS * m_tick, &(results->rawbuf[offset]), nbits, kMideaBitMarkTicks * m_tick,
MIDEA_ONE_SPACE_TICKS * s_tick, kMideaOneSpaceTicks * s_tick, kMideaBitMarkTicks * m_tick,
MIDEA_BIT_MARK_TICKS * m_tick, kMideaZeroSpaceTicks * s_tick, kMideaTolerance);
MIDEA_ZERO_SPACE_TICKS * s_tick,
MIDEA_TOLERANCE);
if (data_result.success == false) return false; if (data_result.success == false) return false;
offset += data_result.used; offset += data_result.used;
if (i % 2 == 0) if (i % 2 == 0)
@@ -395,22 +373,21 @@ bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits,
inverted = data_result.data; inverted = data_result.data;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], MIDEA_BIT_MARK_TICKS * m_tick, if (!matchMark(results->rawbuf[offset++], kMideaBitMarkTicks * m_tick,
MIDEA_TOLERANCE)) kMideaTolerance))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], MIDEA_MIN_GAP_TICKS * s_tick, !matchAtLeast(results->rawbuf[offset++], kMideaMinGapTicks * s_tick,
MIDEA_TOLERANCE)) kMideaTolerance))
return false; return false;
} }
// Compliance // Compliance
if (strict) { if (strict) {
// Protocol requires a second message with all the data bits inverted. // Protocol requires a second message with all the data bits inverted.
// We should have checked we got a second message in the previous loop. // We should have checked we got a second message in the previous loop.
// Just need to check it's value is an inverted copy of the first message. // Just need to check it's value is an inverted copy of the first message.
uint64_t mask = (1ULL << MIDEA_BITS) - 1; uint64_t mask = (1ULL << kMideaBits) - 1;
if ((data & mask) != ((inverted ^ mask) & mask)) return false; if ((data & mask) != ((inverted ^ mask) & mask)) return false;
if (!IRMideaAC::validChecksum(data)) return false; if (!IRMideaAC::validChecksum(data)) return false;
} }
+38 -22
View File
@@ -23,26 +23,43 @@
// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing // https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
// Constants // Constants
#define MIDEA_AC_COOL 0U // 0b000 const uint8_t kMideaACCool = 0; // 0b000
#define MIDEA_AC_DRY 1U // 0b001 const uint8_t kMideaACDry = 1; // 0b001
#define MIDEA_AC_AUTO 2U // 0b010 const uint8_t kMideaACAuto = 2; // 0b010
#define MIDEA_AC_HEAT 3U // 0b011 const uint8_t kMideaACHeat = 3; // 0b011
#define MIDEA_AC_FAN 4U // 0b100 const uint8_t kMideaACFan = 4; // 0b100
#define MIDEA_AC_POWER 1ULL << 39 const uint8_t kMideaACFanAuto = 0; // 0b000
#define MIDEA_AC_SLEEP 1ULL << 38 const uint8_t kMideaACFanLow = 1; // 0b001
#define MIDEA_AC_FAN_AUTO 0U // 0b000 const uint8_t kMideaACFanMed = 2; // 0b010
#define MIDEA_AC_FAN_LOW 1U // 0b001 const uint8_t kMideaACFanHigh = 3; // 0b011
#define MIDEA_AC_FAN_MED 2U // 0b010 const uint64_t kMideaACPower = 1ULL << 39;
#define MIDEA_AC_FAN_HI 3U // 0b011 const uint64_t kMideaACSleep = 1ULL << 38;
#define MIDEA_AC_MIN_TEMP_F 62U // 62F const uint8_t kMideaACMinTempF = 62; // Fahrenheit
#define MIDEA_AC_MAX_TEMP_F 86U // 86F const uint8_t kMideaACMaxTempF = 86; // Fahrenheit
#define MIDEA_AC_MIN_TEMP_C 16U // 16C const uint8_t kMideaACMinTempC = 16; // Celsius
#define MIDEA_AC_MAX_TEMP_C 30U // 30C const uint8_t kMideaACMaxTempC = 30; // Celsius
#define MIDEA_AC_STATE_MASK 0x0000FFFFFFFFFFFFULL const uint64_t kMideaACStateMask = 0x0000FFFFFFFFFFFF;
#define MIDEA_AC_TEMP_MASK 0x0000FFFFE0FFFFFFULL const uint64_t kMideaACTempMask = 0x0000FFFFE0FFFFFF;
#define MIDEA_AC_FAN_MASK 0x0000FFC7FFFFFFFFULL const uint64_t kMideaACFanMask = 0x0000FFC7FFFFFFFF;
#define MIDEA_AC_MODE_MASK 0x0000FFF8FFFFFFFFULL const uint64_t kMideaACModeMask = 0x0000FFF8FFFFFFFF;
#define MIDEA_AC_CHECKSUM_MASK 0x0000FFFFFFFFFF00ULL const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00;
// Legacy defines. (Deprecated)
#define MIDEA_AC_COOL kMideaACCool
#define MIDEA_AC_DRY kMideaACDry
#define MIDEA_AC_AUTO kMideaACAuto
#define MIDEA_AC_HEAT kMideaACHeat
#define MIDEA_AC_FAN kMideaACFan
#define MIDEA_AC_FAN_AUTO kMideaACFanAuto
#define MIDEA_AC_FAN_LOW kMideaACFanLow
#define MIDEA_AC_FAN_MED kMideaACFanMed
#define MIDEA_AC_FAN_HI kMideaACFanHigh
#define MIDEA_AC_POWER kMideaACPower
#define MIDEA_AC_SLEEP kMideaACSleep
#define MIDEA_AC_MIN_TEMP_F kMideaACMinTempF
#define MIDEA_AC_MAX_TEMP_F kMideaACMaxTempF
#define MIDEA_AC_MIN_TEMP_C kMideaACMinTempC
#define MIDEA_AC_MAX_TEMP_C kMideaACMaxTempC
class IRMideaAC { class IRMideaAC {
public: public:
@@ -50,7 +67,7 @@ class IRMideaAC {
void stateReset(); void stateReset();
#if SEND_MIDEA #if SEND_MIDEA
void send(); void send(const uint16_t repeat = kMideaMinRepeat);
#endif // SEND_MIDEA #endif // SEND_MIDEA
void begin(); void begin();
void on(); void on();
@@ -83,5 +100,4 @@ class IRMideaAC {
IRsend _irsend; IRsend _irsend;
}; };
#endif // IR_MIDEA_H_ #endif // IR_MIDEA_H_
+387 -127
View File
@@ -1,8 +1,12 @@
// Copyright 2009 Ken Shirriff // Copyright 2009 Ken Shirriff
// Copyright 2017 David Conran // Copyright 2017-2018 David Conran
// Copyright 2018 Denes Varga
#include "ir_Mitsubishi.h" #include "ir_Mitsubishi.h"
#include <algorithm> #include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h" #include "IRrecv.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
@@ -22,50 +26,49 @@
// Ref: // Ref:
// GlobalCache's Control Tower's Mitsubishi TV data. // GlobalCache's Control Tower's Mitsubishi TV data.
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp
#define MITSUBISHI_TICK 30U const uint16_t kMitsubishiTick = 30;
#define MITSUBISHI_BIT_MARK_TICKS 10U const uint16_t kMitsubishiBitMarkTicks = 10;
#define MITSUBISHI_BIT_MARK (MITSUBISHI_BIT_MARK_TICKS * \ const uint16_t kMitsubishiBitMark = kMitsubishiBitMarkTicks * kMitsubishiTick;
MITSUBISHI_TICK) const uint16_t kMitsubishiOneSpaceTicks = 70;
#define MITSUBISHI_ONE_SPACE_TICKS 70U const uint16_t kMitsubishiOneSpace = kMitsubishiOneSpaceTicks * kMitsubishiTick;
#define MITSUBISHI_ONE_SPACE (MITSUBISHI_ONE_SPACE_TICKS * \ const uint16_t kMitsubishiZeroSpaceTicks = 30;
MITSUBISHI_TICK) const uint16_t kMitsubishiZeroSpace =
#define MITSUBISHI_ZERO_SPACE_TICKS 30U kMitsubishiZeroSpaceTicks * kMitsubishiTick;
#define MITSUBISHI_ZERO_SPACE (MITSUBISHI_ZERO_SPACE_TICKS * \ const uint16_t kMitsubishiMinCommandLengthTicks = 1786;
MITSUBISHI_TICK) const uint16_t kMitsubishiMinCommandLength =
#define MITSUBISHI_MIN_COMMAND_LENGTH_TICKS 1786U kMitsubishiMinCommandLengthTicks * kMitsubishiTick;
#define MITSUBISHI_MIN_COMMAND_LENGTH (MITSUBISHI_MIN_COMMAND_LENGTH_TICKS * \ const uint16_t kMitsubishiMinGapTicks = 936;
MITSUBISHI_TICK) const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick;
#define MITSUBISHI_MIN_GAP_TICKS 936U
#define MITSUBISHI_MIN_GAP (MITSUBISHI_MIN_GAP_TICKS * \
MITSUBISHI_TICK)
// Mitsubishi Projector (HC3000) // Mitsubishi Projector (HC3000)
// Ref: // Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/441 // https://github.com/markszabo/IRremoteESP8266/issues/441
#define MITSUBISHI2_HDR_MARK 8400U
#define MITSUBISHI2_HDR_SPACE (MITSUBISHI2_HDR_MARK / 2) const uint16_t kMitsubishi2HdrMark = 8400;
#define MITSUBISHI2_BIT_MARK 560U const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2;
#define MITSUBISHI2_ZERO_SPACE 520U const uint16_t kMitsubishi2BitMark = 560;
#define MITSUBISHI2_ONE_SPACE (MITSUBISHI2_ZERO_SPACE * 3) const uint16_t kMitsubishi2ZeroSpace = 520;
#define MITSUBISHI2_MIN_GAP 28500U const uint16_t kMitsubishi2OneSpace = kMitsubishi2ZeroSpace * 3;
const uint16_t kMitsubishi2MinGap = 28500;
// Mitsubishi A/C // Mitsubishi A/C
// Ref: // Ref:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84 // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84
#define MITSUBISHI_AC_HDR_MARK 3400U
#define MITSUBISHI_AC_HDR_SPACE 1750U const uint16_t kMitsubishiAcHdrMark = 3400;
#define MITSUBISHI_AC_BIT_MARK 450U const uint16_t kMitsubishiAcHdrSpace = 1750;
#define MITSUBISHI_AC_ONE_SPACE 1300U const uint16_t kMitsubishiAcBitMark = 450;
#define MITSUBISHI_AC_ZERO_SPACE 420U const uint16_t kMitsubishiAcOneSpace = 1300;
#define MITSUBISHI_AC_RPT_MARK 440U const uint16_t kMitsubishiAcZeroSpace = 420;
#define MITSUBISHI_AC_RPT_SPACE 17100UL const uint16_t kMitsubishiAcRptMark = 440;
const uint16_t kMitsubishiAcRptSpace = 17100;
#if SEND_MITSUBISHI #if SEND_MITSUBISHI
// Send a Mitsubishi message // Send a Mitsubishi message
// //
// Args: // Args:
// data: Contents of the message to be sent. // data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically MITSUBISHI_BITS. // nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: ALPHA / untested. // Status: ALPHA / untested.
@@ -77,11 +80,9 @@
// GlobalCache's Control Tower's Mitsubishi TV data. // GlobalCache's Control Tower's Mitsubishi TV data.
void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
MITSUBISHI_BIT_MARK, MITSUBISHI_ONE_SPACE, kMitsubishiBitMark, kMitsubishiOneSpace, kMitsubishiBitMark,
MITSUBISHI_BIT_MARK, MITSUBISHI_ZERO_SPACE, kMitsubishiZeroSpace, kMitsubishiBitMark, kMitsubishiMinGap,
MITSUBISHI_BIT_MARK, MITSUBISHI_MIN_GAP, kMitsubishiMinCommandLength, data, nbits, 33, true, repeat, 50);
MITSUBISHI_MIN_COMMAND_LENGTH,
data, nbits, 33, true, repeat, 50);
} }
#endif // SEND_MITSUBISHI #endif // SEND_MITSUBISHI
@@ -104,44 +105,40 @@ void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) {
// GlobalCache's Control Tower's Mitsubishi TV data. // GlobalCache's Control Tower's Mitsubishi TV data.
bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * nbits + FOOTER - 1) if (results->rawlen < 2 * nbits + kFooter - 1)
return false; // Shorter than shortest possibly expected. return false; // Shorter than shortest possibly expected.
if (strict && nbits != MITSUBISHI_BITS) if (strict && nbits != kMitsubishiBits)
return false; // Request is out of spec. return false; // Request is out of spec.
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint64_t data = 0; uint64_t data = 0;
// No Header // No Header
// But try to auto-calibrate off the initial mark signal. // But try to auto-calibrate off the initial mark signal.
if (!matchMark(results->rawbuf[offset], MITSUBISHI_BIT_MARK, 30)) if (!matchMark(results->rawbuf[offset], kMitsubishiBitMark, 30)) return false;
return false;
// Calculate how long the common tick time is based on the initial mark. // Calculate how long the common tick time is based on the initial mark.
uint32_t tick = results->rawbuf[offset] * RAWTICK / MITSUBISHI_BIT_MARK_TICKS; uint32_t tick = results->rawbuf[offset] * kRawTick / kMitsubishiBitMarkTicks;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result = matchData(
MITSUBISHI_BIT_MARK_TICKS * tick, &(results->rawbuf[offset]), nbits, kMitsubishiBitMarkTicks * tick,
MITSUBISHI_ONE_SPACE_TICKS * tick, kMitsubishiOneSpaceTicks * tick, kMitsubishiBitMarkTicks * tick,
MITSUBISHI_BIT_MARK_TICKS * tick, kMitsubishiZeroSpaceTicks * tick);
MITSUBISHI_ZERO_SPACE_TICKS * tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
uint16_t actualBits = data_result.used / 2; uint16_t actualBits = data_result.used / 2;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], MITSUBISHI_BIT_MARK_TICKS * tick, if (!matchMark(results->rawbuf[offset++], kMitsubishiBitMarkTicks * tick, 30))
30)) return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], MITSUBISHI_MIN_GAP_TICKS * tick)) !matchAtLeast(results->rawbuf[offset], kMitsubishiMinGapTicks * tick))
return false; return false;
// Compliance // Compliance
if (actualBits < nbits) if (actualBits < nbits) return false;
return false; if (strict && actualBits != nbits) return false; // Not as we expected.
if (strict && actualBits != nbits)
return false; // Not as we expected.
// Success // Success
results->decode_type = MITSUBISHI; results->decode_type = MITSUBISHI;
@@ -158,7 +155,7 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits,
// //
// Args: // Args:
// data: Contents of the message to be sent. // data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically MITSUBISHI_BITS. // nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: ALPHA / untested. // Status: ALPHA / untested.
@@ -175,16 +172,15 @@ bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits,
void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) {
for (uint16_t i = 0; i <= repeat; i++) { for (uint16_t i = 0; i <= repeat; i++) {
// First half of the data. // First half of the data.
sendGeneric(MITSUBISHI2_HDR_MARK, MITSUBISHI2_HDR_SPACE, sendGeneric(kMitsubishi2HdrMark, kMitsubishi2HdrSpace, kMitsubishi2BitMark,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE, kMitsubishi2OneSpace, kMitsubishi2BitMark,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE, kMitsubishi2ZeroSpace, kMitsubishi2BitMark,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_HDR_SPACE, kMitsubishi2HdrSpace, data >> (nbits / 2), nbits / 2, 33, true,
data >> (nbits / 2), nbits / 2, 33, true, 0, 50); 0, 50);
// Second half of the data. // Second half of the data.
sendGeneric(0, 0, // No header for the second data block sendGeneric(0, 0, // No header for the second data block
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ONE_SPACE, kMitsubishi2BitMark, kMitsubishi2OneSpace, kMitsubishi2BitMark,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_ZERO_SPACE, kMitsubishi2ZeroSpace, kMitsubishi2BitMark, kMitsubishi2MinGap,
MITSUBISHI2_BIT_MARK, MITSUBISHI2_MIN_GAP,
data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50); data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50);
} }
} }
@@ -210,28 +206,24 @@ void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) {
// https://github.com/markszabo/IRremoteESP8266/issues/441 // https://github.com/markszabo/IRremoteESP8266/issues/441
bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * nbits + HEADER + (FOOTER * 2) - 1) if (results->rawlen < 2 * nbits + kHeader + (kFooter * 2) - 1)
return false; // Shorter than shortest possibly expected. return false; // Shorter than shortest possibly expected.
if (strict && nbits != MITSUBISHI_BITS) if (strict && nbits != kMitsubishiBits)
return false; // Request is out of spec. return false; // Request is out of spec.
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint64_t data = 0; uint64_t data = 0;
uint16_t actualBits = 0; uint16_t actualBits = 0;
// Header // Header
if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false;
return false; if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace))
if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE))
return false; return false;
for (uint8_t i = 1; i <= 2; i++) { for (uint8_t i = 1; i <= 2; i++) {
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), match_result_t data_result = matchData(
nbits / 2, &(results->rawbuf[offset]), nbits / 2, kMitsubishi2BitMark,
MITSUBISHI2_BIT_MARK, kMitsubishi2OneSpace, kMitsubishi2BitMark, kMitsubishi2ZeroSpace);
MITSUBISHI2_ONE_SPACE,
MITSUBISHI2_BIT_MARK,
MITSUBISHI2_ZERO_SPACE);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data <<= nbits / 2; data <<= nbits / 2;
data += data_result.data; data += data_result.data;
@@ -239,24 +231,21 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits,
actualBits += data_result.used / 2; actualBits += data_result.used / 2;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], MITSUBISHI2_BIT_MARK)) if (!matchMark(results->rawbuf[offset++], kMitsubishi2BitMark))
return false; return false;
if (i % 2) { // Every odd data block, we expect a HDR space. if (i % 2) { // Every odd data block, we expect a HDR space.
if (!matchSpace(results->rawbuf[offset++], MITSUBISHI2_HDR_SPACE)) if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace))
return false; return false;
} else { // Every even data block, we expect Min Gap or end of the message. } else { // Every even data block, we expect Min Gap or end of the message.
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], MITSUBISHI2_MIN_GAP)) !matchAtLeast(results->rawbuf[offset++], kMitsubishi2MinGap))
return false; return false;
} }
} }
// Compliance // Compliance
if (actualBits < nbits) if (actualBits < nbits) return false;
return false; if (strict && actualBits != nbits) return false; // Not as we expected.
if (strict && actualBits != nbits)
return false; // Not as we expected.
// Success // Success
results->decode_type = MITSUBISHI2; results->decode_type = MITSUBISHI2;
@@ -273,25 +262,155 @@ bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits,
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=MITSUBISHI_AC_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kMitsubishiACStateLength)
// repeat: Nr. of times the message is to be repeated. // repeat: Nr. of times the message is to be repeated.
// (Default = MITSUBISHI_AC_MIN_REPEAT). // (Default = kMitsubishiACMinRepeat).
// //
// Status: BETA / Appears to be working. // Status: BETA / Appears to be working.
// //
void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < MITSUBISHI_AC_STATE_LENGTH) if (nbytes < kMitsubishiACStateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
sendGeneric(MITSUBISHI_AC_HDR_MARK, MITSUBISHI_AC_HDR_SPACE, sendGeneric(kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, kMitsubishiAcBitMark,
MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ONE_SPACE, kMitsubishiAcOneSpace, kMitsubishiAcBitMark,
MITSUBISHI_AC_BIT_MARK, MITSUBISHI_AC_ZERO_SPACE, kMitsubishiAcZeroSpace, kMitsubishiAcRptMark,
MITSUBISHI_AC_RPT_MARK, MITSUBISHI_AC_RPT_SPACE, kMitsubishiAcRptSpace, data, nbytes, 38, false, repeat, 50);
data, nbytes, 38, false, repeat, 50);
} }
#endif // SEND_MITSUBISHI_AC #endif // SEND_MITSUBISHI_AC
#if DECODE_MITSUBISHI_AC
// Decode the supplied Mitsubishi message.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of data bits to expect.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Under development
//
// Ref:
// https://www.analysir.com/blog/2015/01/06/reverse-engineering-mitsubishi-ac-infrared-protocol/
bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < ((kMitsubishiACBits * 2) + 2)) {
DPRINTLN("Shorter than shortest possibly expected.");
return false; // Shorter than shortest possibly expected.
}
if (strict && nbits != kMitsubishiACBits) {
DPRINTLN("Request is out of spec.");
return false; // Request is out of spec.
}
uint16_t offset = kStartOffset;
for (uint8_t i = 0; i < kMitsubishiACStateLength; i++) {
results->state[i] = 0;
}
bool failure = false;
uint8_t rep = 0;
do {
failure = false;
// Header:
// Somtime happens that junk signals arrives before the real message
bool headerFound = false;
while (!headerFound &&
offset < (results->rawlen - (kMitsubishiACBits * 2 + 2))) {
headerFound =
matchMark(results->rawbuf[offset++], kMitsubishiAcHdrMark) &&
matchSpace(results->rawbuf[offset++], kMitsubishiAcHdrSpace);
}
if (!headerFound) {
DPRINTLN("Header mark not found.");
failure = true;
}
// Decode byte-by-byte:
match_result_t data_result;
for (uint8_t i = 0; i < kMitsubishiACStateLength && !failure; i++) {
results->state[i] = 0;
data_result =
matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark,
kMitsubishiAcOneSpace, kMitsubishiAcBitMark,
kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false) {
failure = true;
DPRINT("Byte decode failed at #");
DPRINTLN((uint16_t)i);
} else {
results->state[i] = data_result.data;
offset += data_result.used;
DPRINT((uint16_t)results->state[i]);
DPRINT(",");
}
DPRINTLN("");
}
// HEADER validation:
if (failure || results->state[0] != 0x23 || results->state[1] != 0xCB ||
results->state[2] != 0x26 || results->state[3] != 0x01 ||
results->state[4] != 0x00) {
DPRINTLN("Header mismatch.");
failure = true;
} else {
// DATA part:
// FOOTER checksum:
if (IRMitsubishiAC::calculateChecksum(results->state) !=
results->state[kMitsubishiACStateLength - 1]) {
DPRINTLN("Checksum error.");
failure = true;
}
}
if (rep != kMitsubishiACMinRepeat && failure) {
bool repeatMarkFound = false;
while (!repeatMarkFound &&
offset < (results->rawlen - (kMitsubishiACBits * 2 + 4))) {
repeatMarkFound =
matchMark(results->rawbuf[offset++], kMitsubishiAcRptMark) &&
matchSpace(results->rawbuf[offset++], kMitsubishiAcRptSpace);
}
if (!repeatMarkFound) {
DPRINTLN("First attempt failure and repeat mark not found.");
return false;
}
}
rep++;
// Check if the repeat is correct if we need strict decode:
if (strict && !failure) {
DPRINTLN("Strict repeat check enabled.");
// Repeat mark and space:
if (!matchMark(results->rawbuf[offset++], kMitsubishiAcRptMark) ||
!matchSpace(results->rawbuf[offset++], kMitsubishiAcRptSpace)) {
DPRINTLN("Repeat mark error.");
return false;
}
// Header mark and space:
if (!matchMark(results->rawbuf[offset++], kMitsubishiAcHdrMark) ||
!matchSpace(results->rawbuf[offset++], kMitsubishiAcHdrSpace)) {
DPRINTLN("Repeat header error.");
return false;
}
// Payload:
for (uint8_t i = 0; i < kMitsubishiACStateLength; i++) {
data_result =
matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark,
kMitsubishiAcOneSpace, kMitsubishiAcBitMark,
kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false ||
data_result.data != results->state[i]) {
DPRINTLN("Repeat payload error.");
return false;
}
offset += data_result.used;
}
} // strict repeat check
} while (failure && rep <= kMitsubishiACMinRepeat);
results->decode_type = MITSUBISHI_AC;
results->bits = kMitsubishiACStateLength * 8;
return true;
}
#endif // DECODE_MITSUBISHI_AC
// Code to emulate Mitsubishi A/C IR remote control unit. // Code to emulate Mitsubishi A/C IR remote control unit.
// Inspired and derived from the work done at: // Inspired and derived from the work done at:
// https://github.com/r45635/HVAC-IR-Control // https://github.com/r45635/HVAC-IR-Control
@@ -301,9 +420,7 @@ void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes,
// Equipment it seems compatible with: // Equipment it seems compatible with:
// * <Add models (A/C & remotes) you've gotten it working with here> // * <Add models (A/C & remotes) you've gotten it working with here>
// Initialise the object. // Initialise the object.
IRMitsubishiAC::IRMitsubishiAC(uint16_t pin) : _irsend(pin) { IRMitsubishiAC::IRMitsubishiAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
// Reset the state of the remote to a known good state/sequence. // Reset the state of the remote to a known good state/sequence.
void IRMitsubishiAC::stateReset() { void IRMitsubishiAC::stateReset() {
@@ -311,7 +428,7 @@ void IRMitsubishiAC::stateReset() {
// Known good state obtained from: // Known good state obtained from:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108
// Note: Can't use the following because it requires -std=c++11 // Note: Can't use the following because it requires -std=c++11
// uint8_t known_good_state[MITSUBISHI_AC_STATE_LENGTH] = { // uint8_t known_good_state[kMitsubishiACStateLength] = {
// 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67, 0x00, // 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F}; // 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F};
remote_state[0] = 0x23; remote_state[0] = 0x23;
@@ -325,22 +442,20 @@ void IRMitsubishiAC::stateReset() {
remote_state[8] = 0x30; remote_state[8] = 0x30;
remote_state[9] = 0x45; remote_state[9] = 0x45;
remote_state[10] = 0x67; remote_state[10] = 0x67;
for (uint8_t i = 11; i < MITSUBISHI_AC_STATE_LENGTH - 1; i++) for (uint8_t i = 11; i < kMitsubishiACStateLength - 1; i++)
remote_state[i] = 0; remote_state[i] = 0;
remote_state[MITSUBISHI_AC_STATE_LENGTH - 1] = 0x1F; remote_state[kMitsubishiACStateLength - 1] = 0x1F;
checksum(); // Calculate the checksum checksum(); // Calculate the checksum
} }
// Configure the pin for output. // Configure the pin for output.
void IRMitsubishiAC::begin() { void IRMitsubishiAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_MITSUBISHI_AC #if SEND_MITSUBISHI_AC
// Send the current desired state to the IR LED. // Send the current desired state to the IR LED.
void IRMitsubishiAC::send() { void IRMitsubishiAC::send(const uint16_t repeat) {
checksum(); // Ensure correct checksum before sending. checksum(); // Ensure correct checksum before sending.
_irsend.sendMitsubishiAC(remote_state); _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat);
} }
#endif // SEND_MITSUBISHI_AC #endif // SEND_MITSUBISHI_AC
@@ -350,26 +465,36 @@ uint8_t* IRMitsubishiAC::getRaw() {
return remote_state; return remote_state;
} }
void IRMitsubishiAC::setRaw(uint8_t *data) {
for (uint8_t i = 0; i < (kMitsubishiACStateLength - 1); i++) {
remote_state[i] = data[i];
}
checksum();
}
// Calculate the checksum for the current internal state of the remote. // Calculate the checksum for the current internal state of the remote.
void IRMitsubishiAC::checksum() { void IRMitsubishiAC::checksum() {
remote_state[17] = calculateChecksum(remote_state);
}
uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) {
uint8_t sum = 0; uint8_t sum = 0;
// Checksum is simple addition of all previous bytes stored // Checksum is simple addition of all previous bytes stored
// as a 8 bit value. // as an 8 bit value.
for (uint8_t i = 0; i < 17; i++) for (uint8_t i = 0; i < 17; i++) sum += data[i];
sum += remote_state[i]; return sum & 0xFFU;
remote_state[17] = sum & 0xFFU;
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRMitsubishiAC::on() { void IRMitsubishiAC::on() {
// state = ON; // state = ON;
remote_state[5] |= MITSUBISHI_AC_POWER; remote_state[5] |= kMitsubishiAcPower;
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRMitsubishiAC::off() { void IRMitsubishiAC::off() {
// state = OFF; // state = OFF;
remote_state[5] &= ~MITSUBISHI_AC_POWER; remote_state[5] &= ~kMitsubishiAcPower;
} }
// Set the requested power state of the A/C. // Set the requested power state of the A/C.
@@ -382,31 +507,31 @@ void IRMitsubishiAC::setPower(bool state) {
// Return the requested power state of the A/C. // Return the requested power state of the A/C.
bool IRMitsubishiAC::getPower() { bool IRMitsubishiAC::getPower() {
return((remote_state[5] & MITSUBISHI_AC_POWER) != 0); return ((remote_state[5] & kMitsubishiAcPower) != 0);
} }
// Set the temp. in deg C // Set the temp. in deg C
void IRMitsubishiAC::setTemp(uint8_t temp) { void IRMitsubishiAC::setTemp(uint8_t temp) {
temp = std::max((uint8_t) MITSUBISHI_AC_MIN_TEMP, temp); temp = std::max((uint8_t)kMitsubishiAcMinTemp, temp);
temp = std::min((uint8_t) MITSUBISHI_AC_MAX_TEMP, temp); temp = std::min((uint8_t)kMitsubishiAcMaxTemp, temp);
remote_state[7] = temp - MITSUBISHI_AC_MIN_TEMP; remote_state[7] = temp - kMitsubishiAcMinTemp;
} }
// Return the set temp. in deg C // Return the set temp. in deg C
uint8_t IRMitsubishiAC::getTemp() { uint8_t IRMitsubishiAC::getTemp() {
return(remote_state[7] + MITSUBISHI_AC_MIN_TEMP); return (remote_state[7] + kMitsubishiAcMinTemp);
} }
// Set the speed of the fan, 0-6. // Set the speed of the fan, 0-6.
// 0 is auto, 1-5 is the speed, 6 is silent. // 0 is auto, 1-5 is the speed, 6 is silent.
void IRMitsubishiAC::setFan(uint8_t fan) { void IRMitsubishiAC::setFan(uint8_t fan) {
// Bounds check // Bounds check
if (fan > MITSUBISHI_AC_FAN_SILENT) if (fan > kMitsubishiAcFanSilent)
fan = MITSUBISHI_AC_FAN_MAX; // Set the fan to maximum if out of range. fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range.
if (fan == MITSUBISHI_AC_FAN_AUTO) { // Automatic is a special case. if (fan == kMitsubishiAcFanAuto) { // Automatic is a special case.
remote_state[9] = 0b10000000 | (remote_state[9] & 0b01111000); remote_state[9] = 0b10000000 | (remote_state[9] & 0b01111000);
return; return;
} else if (fan >= MITSUBISHI_AC_FAN_MAX) { } else if (fan >= kMitsubishiAcFanMax) {
fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist. fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist.
} }
remote_state[9] &= 0b01111000; // Clear the previous state remote_state[9] &= 0b01111000; // Clear the previous state
@@ -416,25 +541,32 @@ void IRMitsubishiAC::setFan(uint8_t fan) {
// Return the requested state of the unit's fan. // Return the requested state of the unit's fan.
uint8_t IRMitsubishiAC::getFan() { uint8_t IRMitsubishiAC::getFan() {
uint8_t fan = remote_state[9] & 0b111; uint8_t fan = remote_state[9] & 0b111;
if (fan == MITSUBISHI_AC_FAN_MAX) if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent;
return MITSUBISHI_AC_FAN_SILENT;
return fan; return fan;
} }
// Return the requested climate operation mode of the a/c unit. // Return the requested climate operation mode of the a/c unit.
uint8_t IRMitsubishiAC::getMode() { uint8_t IRMitsubishiAC::getMode() { return (remote_state[6]); }
return(remote_state[6]);
}
// Set the requested climate operation mode of the a/c unit. // Set the requested climate operation mode of the a/c unit.
void IRMitsubishiAC::setMode(uint8_t mode) { void IRMitsubishiAC::setMode(uint8_t mode) {
// If we get an unexpected mode, default to AUTO. // If we get an unexpected mode, default to AUTO.
switch (mode) { switch (mode) {
case MITSUBISHI_AC_AUTO: break; case kMitsubishiAcAuto:
case MITSUBISHI_AC_COOL: break; remote_state[8] = 0b00110000;
case MITSUBISHI_AC_DRY: break; break;
case MITSUBISHI_AC_HEAT: break; case kMitsubishiAcCool:
default: mode = MITSUBISHI_AC_AUTO; remote_state[8] = 0b00110110;
break;
case kMitsubishiAcDry:
remote_state[8] = 0b00110010;
break;
case kMitsubishiAcHeat:
remote_state[8] = 0b00110000;
break;
default:
mode = kMitsubishiAcAuto;
remote_state[8] = 0b00110000;
} }
remote_state[6] = mode; remote_state[6] = mode;
} }
@@ -452,3 +584,131 @@ void IRMitsubishiAC::setVane(uint8_t mode) {
uint8_t IRMitsubishiAC::getVane() { uint8_t IRMitsubishiAC::getVane() {
return ((remote_state[9] & 0b00111000) >> 3); return ((remote_state[9] & 0b00111000) >> 3);
} }
// Return the clock setting of the message. 1=1/6 hour. e.g. 4pm = 48
uint8_t IRMitsubishiAC::getClock() { return remote_state[10]; }
// Set the current time. 1 = 1/6 hour. e.g. 6am = 36.
void IRMitsubishiAC::setClock(uint8_t clock) { remote_state[10] = clock; }
// Return the desired start time. 1 = 1/6 hour. e.g. 1am = 6
uint8_t IRMitsubishiAC::getStartClock() { return remote_state[12]; }
// Set the desired start tiem of the AC. 1 = 1/6 hour. e.g. 8pm = 120
void IRMitsubishiAC::setStartClock(uint8_t clock) { remote_state[12] = clock; }
// Return the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132
uint8_t IRMitsubishiAC::getStopClock() { return remote_state[11]; }
// Set the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132
void IRMitsubishiAC::setStopClock(uint8_t clock) { remote_state[11] = clock; }
// Return the timer setting. Possible values: kMitsubishiAcNoTimer,
// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer,
// kMitsubishiAcStartStopTimer
uint8_t IRMitsubishiAC::getTimer() { return remote_state[13] & 0b111; }
// Set the timer setting. Possible values: kMitsubishiAcNoTimer,
// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer,
// kMitsubishiAcStartStopTimer
void IRMitsubishiAC::setTimer(uint8_t timer) {
remote_state[13] = timer & 0b111;
}
#ifdef ARDUINO
String IRMitsubishiAC::timeToString(uint64_t time) {
String result = "";
#else
std::string IRMitsubishiAC::timeToString(uint64_t time) {
std::string result = "";
#endif // ARDUINO
if (time / 6 < 10) result += "0";
result += uint64ToString(time / 6);
result += ":";
if (time * 10 % 60 < 10) result += "0";
result += uint64ToString(time * 10 % 60);
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRMitsubishiAC::toString() {
String result = "";
#else
std::string IRMitsubishiAC::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
if (getPower())
result += "On";
else
result += "Off";
switch (getMode()) {
case MITSUBISHI_AC_AUTO:
result += " (AUTO)";
break;
case MITSUBISHI_AC_COOL:
result += " (COOL)";
break;
case MITSUBISHI_AC_DRY:
result += " (DRY)";
break;
case MITSUBISHI_AC_HEAT:
result += " (HEAT)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", FAN: ";
switch (getFan()) {
case MITSUBISHI_AC_FAN_AUTO:
result += "AUTO";
break;
case MITSUBISHI_AC_FAN_MAX:
result += "MAX";
break;
case MITSUBISHI_AC_FAN_SILENT:
result += "SILENT";
break;
default:
result += uint64ToString(getFan());
}
result += ", VANE: ";
switch (getVane()) {
case MITSUBISHI_AC_VANE_AUTO:
result += "AUTO";
break;
case MITSUBISHI_AC_VANE_AUTO_MOVE:
result += "AUTO MOVE";
break;
default:
result += uint64ToString(getVane());
}
result += ", Time: ";
result += timeToString(getClock());
result += ", On timer: ";
result += timeToString(getStartClock());
result += ", Off timer: ";
result += timeToString(getStopClock());
result += ", Timer: ";
switch (getTimer()) {
case kMitsubishiAcNoTimer:
result += "-";
break;
case kMitsubishiAcStartTimer:
result += "Start";
break;
case kMitsubishiAcStopTimer:
result += "Stop";
break;
case kMitsubishiAcStartStopTimer:
result += "Start+Stop";
break;
default:
result += "? (";
result += getTimer();
result += ")\n";
}
return result;
}
+60 -16
View File
@@ -5,6 +5,11 @@
#define __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS
#include <stdint.h> #include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h" #include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
@@ -18,27 +23,48 @@
// Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran
// Constants // Constants
#define MITSUBISHI_AC_AUTO 0x20U const uint8_t kMitsubishiAcAuto = 0x20;
#define MITSUBISHI_AC_COOL 0x18U const uint8_t kMitsubishiAcCool = 0x18;
#define MITSUBISHI_AC_DRY 0x10U const uint8_t kMitsubishiAcDry = 0x10;
#define MITSUBISHI_AC_HEAT 0x08U const uint8_t kMitsubishiAcHeat = 0x08;
#define MITSUBISHI_AC_POWER 0x20U const uint8_t kMitsubishiAcPower = 0x20;
#define MITSUBISHI_AC_FAN_AUTO 0U const uint8_t kMitsubishiAcFanAuto = 0;
#define MITSUBISHI_AC_FAN_MAX 5U const uint8_t kMitsubishiAcFanMax = 5;
#define MITSUBISHI_AC_FAN_REAL_MAX 4U const uint8_t kMitsubishiAcFanRealMax = 4;
#define MITSUBISHI_AC_FAN_SILENT 6U const uint8_t kMitsubishiAcFanSilent = 6;
#define MITSUBISHI_AC_MIN_TEMP 16U // 16C const uint8_t kMitsubishiAcMinTemp = 16; // 16C
#define MITSUBISHI_AC_MAX_TEMP 31U // 31C const uint8_t kMitsubishiAcMaxTemp = 31; // 31C
#define MITSUBISHI_AC_VANE_AUTO 0U const uint8_t kMitsubishiAcVaneAuto = 0;
#define MITSUBISHI_AC_VANE_AUTO_MOVE 7U const uint8_t kMitsubishiAcVaneAutoMove = 7;
const uint8_t kMitsubishiAcNoTimer = 0;
const uint8_t kMitsubishiAcStartTimer = 5;
const uint8_t kMitsubishiAcStopTimer = 3;
const uint8_t kMitsubishiAcStartStopTimer = 7;
// Legacy defines (Deprecated)
#define MITSUBISHI_AC_VANE_AUTO_MOVE kMitsubishiAcVaneAutoMove
#define MITSUBISHI_AC_VANE_AUTO kMitsubishiAcVaneAuto
#define MITSUBISHI_AC_POWER kMitsubishiAcPower
#define MITSUBISHI_AC_MIN_TEMP kMitsubishiAcMinTemp
#define MITSUBISHI_AC_MAX_TEMP kMitsubishiAcMaxTemp
#define MITSUBISHI_AC_HEAT kMitsubishiAcHeat
#define MITSUBISHI_AC_FAN_SILENT kMitsubishiAcFanSilent
#define MITSUBISHI_AC_FAN_REAL_MAX kMitsubishiAcFanRealMax
#define MITSUBISHI_AC_FAN_MAX kMitsubishiAcFanMax
#define MITSUBISHI_AC_FAN_AUTO kMitsubishiAcFanAuto
#define MITSUBISHI_AC_DRY kMitsubishiAcDry
#define MITSUBISHI_AC_COOL kMitsubishiAcCool
#define MITSUBISHI_AC_AUTO kMitsubishiAcAuto
class IRMitsubishiAC { class IRMitsubishiAC {
public: public:
explicit IRMitsubishiAC(uint16_t pin); explicit IRMitsubishiAC(uint16_t pin);
static uint8_t calculateChecksum(uint8_t* data);
void stateReset(); void stateReset();
#if SEND_MITSUBISHI_AC #if SEND_MITSUBISHI_AC
void send(); void send(const uint16_t repeat = kMitsubishiACMinRepeat);
#endif // SEND_MITSUBISHI_AC #endif // SEND_MITSUBISHI_AC
void begin(); void begin();
void on(); void on();
@@ -54,12 +80,30 @@ class IRMitsubishiAC {
void setVane(uint8_t mode); void setVane(uint8_t mode);
uint8_t getVane(); uint8_t getVane();
uint8_t* getRaw(); uint8_t* getRaw();
void setRaw(uint8_t* data);
uint8_t getClock();
void setClock(uint8_t clock);
uint8_t getStartClock();
void setStartClock(uint8_t clock);
uint8_t getStopClock();
void setStopClock(uint8_t clock);
uint8_t getTimer();
void setTimer(uint8_t timer);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
private: private:
uint8_t remote_state[MITSUBISHI_AC_STATE_LENGTH]; #ifdef ARDUINO
String timeToString(uint64_t time);
#else
std::string timeToString(uint64_t time);
#endif
uint8_t remote_state[kMitsubishiACStateLength];
void checksum(); void checksum();
IRsend _irsend; IRsend _irsend;
}; };
#endif // IR_MITSUBISHI_H_ #endif // IR_MITSUBISHI_H_
+32 -63
View File
@@ -2,6 +2,7 @@
// Copyright 2017 David Conran // Copyright 2017 David Conran
#define __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS
#include "ir_NEC.h"
#include <stdint.h> #include <stdint.h>
#include <algorithm> #include <algorithm>
#include "IRrecv.h" #include "IRrecv.h"
@@ -16,39 +17,13 @@
// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ // NEC originally added from https://github.com/shirriff/Arduino-IRremote/
// Constants #if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \
// Ref: SEND_PIONEER)
// http://www.sbprojects.com/knowledge/ir/nec.php
#define NEC_TICK 560U
#define NEC_HDR_MARK_TICKS 16U
#define NEC_HDR_MARK (NEC_HDR_MARK_TICKS * NEC_TICK)
#define NEC_HDR_SPACE_TICKS 8U
#define NEC_HDR_SPACE (NEC_HDR_SPACE_TICKS * NEC_TICK)
#define NEC_BIT_MARK_TICKS 1U
#define NEC_BIT_MARK (NEC_BIT_MARK_TICKS * NEC_TICK)
#define NEC_ONE_SPACE_TICKS 3U
#define NEC_ONE_SPACE (NEC_TICK * NEC_ONE_SPACE_TICKS)
#define NEC_ZERO_SPACE_TICKS 1U
#define NEC_ZERO_SPACE (NEC_TICK * NEC_ZERO_SPACE_TICKS)
#define NEC_RPT_SPACE_TICKS 4U
#define NEC_RPT_SPACE (NEC_RPT_SPACE_TICKS * NEC_TICK)
#define NEC_RPT_LENGTH 4U
#define NEC_MIN_COMMAND_LENGTH_TICKS 193U
#define NEC_MIN_COMMAND_LENGTH (NEC_MIN_COMMAND_LENGTH_TICKS * NEC_TICK)
#define NEC_MIN_GAP (NEC_MIN_COMMAND_LENGTH - \
(NEC_HDR_MARK + NEC_HDR_SPACE + NEC_BITS * (NEC_BIT_MARK + NEC_ONE_SPACE) \
+ NEC_BIT_MARK))
#define NEC_MIN_GAP_TICKS (NEC_MIN_COMMAND_LENGTH_TICKS - \
(NEC_HDR_MARK_TICKS + NEC_HDR_SPACE_TICKS + \
NEC_BITS * (NEC_BIT_MARK_TICKS + NEC_ONE_SPACE_TICKS) + \
NEC_BIT_MARK_TICKS))
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
// Send a raw NEC(Renesas) formatted message. // Send a raw NEC(Renesas) formatted message.
// //
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The number of bits of the message to be sent. Typically NEC_BITS. // nbits: The number of bits of the message to be sent. Typically kNECBits.
// repeat: The number of times the command is to be repeated. // repeat: The number of times the command is to be repeated.
// //
// Status: STABLE / Known working. // Status: STABLE / Known working.
@@ -56,18 +31,15 @@
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/nec.php // http://www.sbprojects.com/knowledge/ir/nec.php
void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(NEC_HDR_MARK, NEC_HDR_SPACE, sendGeneric(kNecHdrMark, kNecHdrSpace, kNecBitMark, kNecOneSpace, kNecBitMark,
NEC_BIT_MARK, NEC_ONE_SPACE, kNecZeroSpace, kNecBitMark, kNecMinGap, kNecMinCommandLength,
NEC_BIT_MARK, NEC_ZERO_SPACE,
NEC_BIT_MARK, NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH,
data, nbits, 38, true, 0, // Repeats are handled later. data, nbits, 38, true, 0, // Repeats are handled later.
33); 33);
// Optional command repeat sequence. // Optional command repeat sequence.
if (repeat) if (repeat)
sendGeneric(NEC_HDR_MARK, NEC_RPT_SPACE, sendGeneric(kNecHdrMark, kNecRptSpace, 0, 0, 0, 0, // No actual data sent.
0, 0, 0, 0, // No actual data sent. kNecBitMark, kNecMinGap, kNecMinCommandLength, 0,
NEC_BIT_MARK, NEC_MIN_GAP, NEC_MIN_COMMAND_LENGTH, 0, // No data to be sent.
0, 0, // No data to be sent.
38, true, repeat - 1, // We've already sent a one message. 38, true, repeat - 1, // We've already sent a one message.
33); 33);
} }
@@ -103,7 +75,7 @@ uint32_t IRsend::encodeNEC(uint16_t address, uint16_t command) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically NEC_BITS. // nbits: The number of data bits to expect. Typically kNECBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -112,34 +84,33 @@ uint32_t IRsend::encodeNEC(uint16_t address, uint16_t command) {
// //
// Notes: // Notes:
// NEC protocol has three varients/forms. // NEC protocol has three varients/forms.
// Normal: a 8 bit address & a 8 bit command in 32 bit data form. // Normal: an 8 bit address & an 8 bit command in 32 bit data form.
// i.e. address + inverted(address) + command + inverted(command) // i.e. address + inverted(address) + command + inverted(command)
// Extended: a 16 bit address & a 8 bit command in 32 bit data form. // Extended: a 16 bit address & an 8 bit command in 32 bit data form.
// i.e. address + command + inverted(command) // i.e. address + command + inverted(command)
// Repeat: a 0-bit code. i.e. No data bits. Just the header + footer. // Repeat: a 0-bit code. i.e. No data bits. Just the header + footer.
// //
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/nec.php // http://www.sbprojects.com/knowledge/ir/nec.php
bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1 && if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 &&
results->rawlen != NEC_RPT_LENGTH) results->rawlen != kNecRptLength)
return false; // Can't possibly be a valid NEC message. return false; // Can't possibly be a valid NEC message.
if (strict && nbits != NEC_BITS) if (strict && nbits != kNECBits)
return false; // Not strictly an NEC message. return false; // Not strictly an NEC message.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], NEC_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false;
// Calculate how long the lowest tick time is based on the header mark. // Calculate how long the lowest tick time is based on the header mark.
uint32_t mark_tick = results->rawbuf[offset++] * RAWTICK / uint32_t mark_tick = results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks;
NEC_HDR_MARK_TICKS;
// Check if it is a repeat code. // Check if it is a repeat code.
if (results->rawlen == NEC_RPT_LENGTH && if (results->rawlen == kNecRptLength &&
matchSpace(results->rawbuf[offset], NEC_RPT_SPACE) && matchSpace(results->rawbuf[offset], kNecRptSpace) &&
matchMark(results->rawbuf[offset + 1], NEC_BIT_MARK_TICKS * mark_tick)) { matchMark(results->rawbuf[offset + 1], kNecBitMarkTicks * mark_tick)) {
results->value = REPEAT; results->value = kRepeat;
results->decode_type = NEC; results->decode_type = NEC;
results->bits = 0; results->bits = 0;
results->address = 0; results->address = 0;
@@ -149,25 +120,24 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) {
} }
// Header (cont.) // Header (cont.)
if (!matchSpace(results->rawbuf[offset], NEC_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t space_tick = results->rawbuf[offset++] * RAWTICK / uint32_t space_tick =
NEC_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
NEC_BIT_MARK_TICKS * mark_tick, matchData(&(results->rawbuf[offset]), nbits, kNecBitMarkTicks * mark_tick,
NEC_ONE_SPACE_TICKS * space_tick, kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick,
NEC_BIT_MARK_TICKS * mark_tick, kNecZeroSpaceTicks * space_tick);
NEC_ZERO_SPACE_TICKS * space_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], NEC_BIT_MARK_TICKS * mark_tick)) if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], NEC_MIN_GAP_TICKS * space_tick)) !matchAtLeast(results->rawbuf[offset], kNecMinGapTicks * space_tick))
return false; return false;
// Compliance // Compliance
@@ -175,8 +145,7 @@ bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) {
uint8_t command = (data & 0xFF00) >> 8; uint8_t command = (data & 0xFF00) >> 8;
// Command is sent twice, once as plain and then inverted. // Command is sent twice, once as plain and then inverted.
if ((command ^ 0xFF) != (data & 0xFF)) { if ((command ^ 0xFF) != (data & 0xFF)) {
if (strict) if (strict) return false; // Command integrity failed.
return false; // Command integrity failed.
command = 0; // The command value isn't valid, so default to zero. command = 0; // The command value isn't valid, so default to zero.
} }
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017, 2018 David Conran
#ifndef IR_NEC_H_
#define IR_NEC_H_
#include <stdint.h>
#include "IRremoteESP8266.h"
// N N EEEEE CCCC
// NN N E C
// N N N EEE C
// N NN E C
// N N EEEEE CCCC
// NEC originally added from https://github.com/shirriff/Arduino-IRremote/
// Constants
// Ref:
// http://www.sbprojects.com/knowledge/ir/nec.php
const uint16_t kNecTick = 560;
const uint16_t kNecHdrMarkTicks = 16;
const uint16_t kNecHdrMark = kNecHdrMarkTicks * kNecTick;
const uint16_t kNecHdrSpaceTicks = 8;
const uint16_t kNecHdrSpace = kNecHdrSpaceTicks * kNecTick;
const uint16_t kNecBitMarkTicks = 1;
const uint16_t kNecBitMark = kNecBitMarkTicks * kNecTick;
const uint16_t kNecOneSpaceTicks = 3;
const uint16_t kNecOneSpace = kNecOneSpaceTicks * kNecTick;
const uint16_t kNecZeroSpaceTicks = 1;
const uint16_t kNecZeroSpace = kNecZeroSpaceTicks * kNecTick;
const uint16_t kNecRptSpaceTicks = 4;
const uint16_t kNecRptSpace = kNecRptSpaceTicks * kNecTick;
const uint16_t kNecRptLength = 4;
const uint16_t kNecMinCommandLengthTicks = 193;
const uint32_t kNecMinCommandLength = kNecMinCommandLengthTicks * kNecTick;
const uint32_t kNecMinGap =
kNecMinCommandLength -
(kNecHdrMark + kNecHdrSpace + kNECBits * (kNecBitMark + kNecOneSpace) +
kNecBitMark);
const uint16_t kNecMinGapTicks =
kNecMinCommandLengthTicks -
(kNecHdrMarkTicks + kNecHdrSpaceTicks +
kNECBits * (kNecBitMarkTicks + kNecOneSpaceTicks) + kNecBitMarkTicks);
#endif // IR_NEC_H_
+31 -37
View File
@@ -15,38 +15,35 @@
// Constants // Constants
// Ref: // Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/309 // https://github.com/markszabo/IRremoteESP8266/issues/309
#define NIKAI_TICK 500U const uint16_t kNikaiTick = 500;
#define NIKAI_HDR_MARK_TICKS 8U const uint16_t kNikaiHdrMarkTicks = 8;
#define NIKAI_HDR_MARK (NIKAI_HDR_MARK_TICKS * NIKAI_TICK) const uint16_t kNikaiHdrMark = kNikaiHdrMarkTicks * kNikaiTick;
#define NIKAI_HDR_SPACE_TICKS 8U const uint16_t kNikaiHdrSpaceTicks = 8;
#define NIKAI_HDR_SPACE (NIKAI_HDR_SPACE_TICKS * NIKAI_TICK) const uint16_t kNikaiHdrSpace = kNikaiHdrSpaceTicks * kNikaiTick;
#define NIKAI_BIT_MARK_TICKS 1U const uint16_t kNikaiBitMarkTicks = 1;
#define NIKAI_BIT_MARK (NIKAI_BIT_MARK_TICKS * NIKAI_TICK) const uint16_t kNikaiBitMark = kNikaiBitMarkTicks * kNikaiTick;
#define NIKAI_ONE_SPACE_TICKS 2U const uint16_t kNikaiOneSpaceTicks = 2;
#define NIKAI_ONE_SPACE (NIKAI_ONE_SPACE_TICKS * NIKAI_TICK) const uint16_t kNikaiOneSpace = kNikaiOneSpaceTicks * kNikaiTick;
#define NIKAI_ZERO_SPACE_TICKS 4U const uint16_t kNikaiZeroSpaceTicks = 4;
#define NIKAI_ZERO_SPACE (NIKAI_ZERO_SPACE_TICKS * NIKAI_TICK) const uint16_t kNikaiZeroSpace = kNikaiZeroSpaceTicks * kNikaiTick;
#define NIKAI_MIN_GAP_TICKS 17U const uint16_t kNikaiMinGapTicks = 17;
#define NIKAI_MIN_GAP (NIKAI_MIN_GAP_TICKS * NIKAI_TICK) const uint16_t kNikaiMinGap = kNikaiMinGapTicks * kNikaiTick;
#if SEND_NIKAI #if SEND_NIKAI
// Send a Nikai TV formatted message. // Send a Nikai TV formatted message.
// //
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The bit size of the message being sent. typically NIKAI_BITS. // nbits: The bit size of the message being sent. typically kNikaiBits.
// repeat: The number of times the message is to be repeated. // repeat: The number of times the message is to be repeated.
// //
// Status: STABLE / Working. // Status: STABLE / Working.
// //
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/309 // Ref: https://github.com/markszabo/IRremoteESP8266/issues/309
void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(NIKAI_HDR_MARK, NIKAI_HDR_SPACE, sendGeneric(kNikaiHdrMark, kNikaiHdrSpace, kNikaiBitMark, kNikaiOneSpace,
NIKAI_BIT_MARK, NIKAI_ONE_SPACE, kNikaiBitMark, kNikaiZeroSpace, kNikaiBitMark, kNikaiMinGap, data,
NIKAI_BIT_MARK, NIKAI_ZERO_SPACE, nbits, 38, true, repeat, 33);
NIKAI_BIT_MARK, NIKAI_MIN_GAP,
data, nbits, 38, true, repeat, 33);
} }
#endif #endif
@@ -56,7 +53,7 @@ void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion. // nbits: Nr. of bits to expect in the data portion.
// Typically NIKAI_BITS. // Typically kNikaiBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -64,37 +61,34 @@ void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Status: STABLE / Working. // Status: STABLE / Working.
// //
bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Nikai message. return false; // Can't possibly be a valid Nikai message.
if (strict && nbits != NIKAI_BITS) if (strict && nbits != kNikaiBits)
return false; // We expect Nikai to be a certain sized message. return false; // We expect Nikai to be a certain sized message.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], NIKAI_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kNikaiHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrMarkTicks;
NIKAI_HDR_MARK_TICKS; if (!matchSpace(results->rawbuf[offset], kNikaiHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset], NIKAI_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrSpaceTicks;
NIKAI_HDR_SPACE_TICKS;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
NIKAI_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits, kNikaiBitMarkTicks * m_tick,
NIKAI_ONE_SPACE_TICKS * s_tick, kNikaiOneSpaceTicks * s_tick, kNikaiBitMarkTicks * m_tick,
NIKAI_BIT_MARK_TICKS * m_tick, kNikaiZeroSpaceTicks * s_tick);
NIKAI_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], NIKAI_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kNikaiBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], NIKAI_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kNikaiMinGapTicks * s_tick))
return false; return false;
// Compliance // Compliance
+736 -56
View File
@@ -1,7 +1,11 @@
// Copyright 2015 Kristian Lauszus // Copyright 2015 Kristian Lauszus
// Copyright 2017 David Conran // Copyright 2017, 2018 David Conran
#include "ir_Panasonic.h"
#include <algorithm> #include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h" #include "IRrecv.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
@@ -15,36 +19,58 @@
// Panasonic protocol originally added by Kristian Lauszus from: // Panasonic protocol originally added by Kristian Lauszus from:
// https://github.com/z3t0/Arduino-IRremote // https://github.com/z3t0/Arduino-IRremote
// (Thanks to zenwheel and other people at the original blog post) // (Thanks to zenwheel and other people at the original blog post)
//
// Panasonic A/C support add by crankyoldgit but heavily influenced by:
// https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp
// Panasonic A/C Clock & Timer support:
// Reverse Engineering by MikkelTb
// Code by crankyoldgit
// Panasonic A/C models supported:
// A/C Series/models:
// JKE, LKE, DKE, CKP, & NKE series. (In theory)
// CS-YW9MKD (confirmed)
// CS-ME14CKPG / CS-ME12CKPG / CS-ME10CKPG
// A/C Remotes:
// A75C3747 (confirmed)
// A75C3704
// A75C2311 (CKP)
// Constants // Constants
// Ref: // Ref:
// http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152
#define PANASONIC_TICK 432U
#define PANASONIC_HDR_MARK_TICKS 8U const uint16_t kPanasonicTick = 432;
#define PANASONIC_HDR_MARK (PANASONIC_HDR_MARK_TICKS * PANASONIC_TICK) const uint16_t kPanasonicHdrMarkTicks = 8;
#define PANASONIC_HDR_SPACE_TICKS 4U const uint16_t kPanasonicHdrMark = kPanasonicHdrMarkTicks * kPanasonicTick;
#define PANASONIC_HDR_SPACE (PANASONIC_HDR_SPACE_TICKS * PANASONIC_TICK) const uint16_t kPanasonicHdrSpaceTicks = 4;
#define PANASONIC_BIT_MARK_TICKS 1U const uint16_t kPanasonicHdrSpace = kPanasonicHdrSpaceTicks * kPanasonicTick;
#define PANASONIC_BIT_MARK (PANASONIC_BIT_MARK_TICKS * PANASONIC_TICK) const uint16_t kPanasonicBitMarkTicks = 1;
#define PANASONIC_ONE_SPACE_TICKS 3U const uint16_t kPanasonicBitMark = kPanasonicBitMarkTicks * kPanasonicTick;
#define PANASONIC_ONE_SPACE (PANASONIC_ONE_SPACE_TICKS * PANASONIC_TICK) const uint16_t kPanasonicOneSpaceTicks = 3;
#define PANASONIC_ZERO_SPACE_TICKS 1U const uint16_t kPanasonicOneSpace = kPanasonicOneSpaceTicks * kPanasonicTick;
#define PANASONIC_ZERO_SPACE (PANASONIC_ZERO_SPACE_TICKS * PANASONIC_TICK) const uint16_t kPanasonicZeroSpaceTicks = 1;
#define PANASONIC_MIN_COMMAND_LENGTH_TICKS 378UL const uint16_t kPanasonicZeroSpace = kPanasonicZeroSpaceTicks * kPanasonicTick;
#define PANASONIC_MIN_COMMAND_LENGTH (PANASONIC_MIN_COMMAND_LENGTH_TICKS * \ const uint16_t kPanasonicMinCommandLengthTicks = 378;
PANASONIC_TICK) const uint32_t kPanasonicMinCommandLength =
#define PANASONIC_END_GAP 5000U // See issue #245 kPanasonicMinCommandLengthTicks * kPanasonicTick;
#define PANASONIC_MIN_GAP_TICKS (PANASONIC_MIN_COMMAND_LENGTH_TICKS - \ const uint16_t kPanasonicEndGap = 5000; // See issue #245
(PANASONIC_HDR_MARK_TICKS + PANASONIC_HDR_SPACE_TICKS + \ const uint16_t kPanasonicMinGapTicks =
PANASONIC_BITS * (PANASONIC_BIT_MARK_TICKS + PANASONIC_ONE_SPACE_TICKS) + \ kPanasonicMinCommandLengthTicks -
PANASONIC_BIT_MARK_TICKS)) (kPanasonicHdrMarkTicks + kPanasonicHdrSpaceTicks +
#define PANASONIC_MIN_GAP ((uint32_t)(PANASONIC_MIN_GAP_TICKS * PANASONIC_TICK)) kPanasonicBits * (kPanasonicBitMarkTicks + kPanasonicOneSpaceTicks) +
kPanasonicBitMarkTicks);
const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick;
const uint16_t kPanasonicAcSectionGap = 10000;
const uint16_t kPanasonicAcSection1Length = 8;
const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess.
#if (SEND_PANASONIC || SEND_DENON) #if (SEND_PANASONIC || SEND_DENON)
// Send a Panasonic formatted message. // Send a Panasonic formatted message.
// //
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The number of bits of the message to be sent. (PANASONIC_BITS). // nbits: The number of bits of the message to be sent. (kPanasonicBits).
// repeat: The number of times the command is to be repeated. // repeat: The number of times the command is to be repeated.
// //
// Status: BETA / Should be working. // Status: BETA / Should be working.
@@ -52,12 +78,10 @@
// Note: // Note:
// This protocol is a modified version of Kaseikyo. // This protocol is a modified version of Kaseikyo.
void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(PANASONIC_HDR_MARK, PANASONIC_HDR_SPACE, sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark,
PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE, kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace,
PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE, kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength,
PANASONIC_BIT_MARK, data, nbits, kPanasonicFreq, true, repeat, 50);
PANASONIC_MIN_GAP, PANASONIC_MIN_COMMAND_LENGTH,
data, nbits, 36700U, true, repeat, 50);
} }
// Send a Panasonic formatted message. // Send a Panasonic formatted message.
@@ -65,7 +89,7 @@ void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Args: // Args:
// address: The manufacturer code. // address: The manufacturer code.
// data: The data portion to be sent. // data: The data portion to be sent.
// nbits: The number of bits of the message to be sent. (PANASONIC_BITS). // nbits: The number of bits of the message to be sent. (kPanasonicBits).
// repeat: The number of times the command is to be repeated. // repeat: The number of times the command is to be repeated.
// //
// Status: STABLE. // Status: STABLE.
@@ -93,16 +117,11 @@ void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits,
// Panasonic 48-bit protocol is a modified version of Kaseikyo. // Panasonic 48-bit protocol is a modified version of Kaseikyo.
// Ref: // Ref:
// http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 // http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615
uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device,
uint8_t device, uint8_t subdevice, uint8_t function) {
uint8_t subdevice,
uint8_t function) {
uint8_t checksum = device ^ subdevice ^ function; uint8_t checksum = device ^ subdevice ^ function;
return (((uint64_t) manufacturer << 32) | return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) |
((uint64_t) device << 24) | ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum);
((uint64_t) subdevice << 16) |
((uint64_t) function << 8) |
checksum);
} }
#endif // (SEND_PANASONIC || SEND_DENON) #endif // (SEND_PANASONIC || SEND_DENON)
@@ -124,39 +143,38 @@ uint64_t IRsend::encodePanasonic(uint16_t manufacturer,
// http://www.hifi-remote.com/wiki/index.php?title=Panasonic // http://www.hifi-remote.com/wiki/index.php?title=Panasonic
bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits,
bool strict, uint32_t manufacturer) { bool strict, uint32_t manufacturer) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Not enough entries to be a Panasonic message. return false; // Not enough entries to be a Panasonic message.
if (strict && nbits != PANASONIC_BITS) if (strict && nbits != kPanasonicBits)
return false; // Request is out of spec. return false; // Request is out of spec.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], PANASONIC_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick =
PANASONIC_HDR_MARK_TICKS; results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], PANASONIC_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick =
PANASONIC_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result = matchData(
PANASONIC_BIT_MARK_TICKS * m_tick, &(results->rawbuf[offset]), nbits, kPanasonicBitMarkTicks * m_tick,
PANASONIC_ONE_SPACE_TICKS * s_tick, kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick,
PANASONIC_BIT_MARK_TICKS * m_tick, kPanasonicZeroSpaceTicks * s_tick);
PANASONIC_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!match(results->rawbuf[offset++], PANASONIC_BIT_MARK_TICKS * m_tick)) if (!match(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], PANASONIC_END_GAP)) !matchAtLeast(results->rawbuf[offset], kPanasonicEndGap))
return false; return false;
// Compliance // Compliance
@@ -168,8 +186,7 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits,
// Verify the checksum. // Verify the checksum.
uint8_t checksumOrig = data & 0xFF; uint8_t checksumOrig = data & 0xFF;
uint8_t checksumCalc = ((data >> 24) ^ (data >> 16) ^ (data >> 8)) & 0xFF; uint8_t checksumCalc = ((data >> 24) ^ (data >> 16) ^ (data >> 8)) & 0xFF;
if (checksumOrig != checksumCalc) if (checksumOrig != checksumCalc) return false;
return false;
} }
// Success // Success
@@ -181,3 +198,666 @@ bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits,
return true; return true;
} }
#endif // (DECODE_PANASONIC || DECODE_DENON) #endif // (DECODE_PANASONIC || DECODE_DENON)
#if SEND_PANASONIC_AC
// Send a Panasonic A/C message.
//
// Args:
// data: Contents of the message to be sent. (Guessing MSBF order)
// nbits: Nr. of bits of data to be sent. Typically kPanasonicAcBits.
// repeat: Nr. of additional times the message is to be sent.
//
// Status: Beta / Appears to work with real device(s).
//:
// Panasonic A/C models supported:
// A/C Series/models:
// JKE, LKE, DKE, & NKE series.
// CS-YW9MKD
// A/C Remotes:
// A75C3747
// A75C3704
//
void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
if (nbytes < kPanasonicAcSection1Length) return;
for (uint16_t r = 0; r <= repeat; r++) {
// First section. (8 bytes)
sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark,
kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace,
kPanasonicBitMark, kPanasonicAcSectionGap, data,
kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50);
// First section. (The rest of the data bytes)
sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark,
kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace,
kPanasonicBitMark, kPanasonicAcMessageGap,
data + kPanasonicAcSection1Length,
nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0,
50);
}
}
#endif // SEND_PANASONIC_AC
IRPanasonicAc::IRPanasonicAc(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRPanasonicAc::stateReset() {
for (uint8_t i = 0; i < kPanasonicAcStateLength; i++)
remote_state[i] = kPanasonicKnownGoodState[i];
_temp = 25; // An initial saved desired temp. Completely made up.
_swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing.
}
void IRPanasonicAc::begin() { _irsend.begin(); }
// 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 IRPanasonicAc::validChecksum(uint8_t state[], const uint16_t length) {
if (length < 2) return false; // 1 byte of data can't have a checksum.
return (state[length - 1] ==
sumBytes(state, length - 1, kPanasonicAcChecksumInit));
}
uint8_t IRPanasonicAc::calcChecksum(uint8_t state[], const uint16_t length) {
return sumBytes(state, length - 1, kPanasonicAcChecksumInit);
}
void IRPanasonicAc::fixChecksum(const uint16_t length) {
remote_state[length - 1] = calcChecksum(remote_state, length);
}
#if SEND_PANASONIC_AC
void IRPanasonicAc::send(const uint16_t repeat) {
fixChecksum();
_irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat);
}
#endif // SEND_PANASONIC_AC
void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) {
switch (model) {
case kPanasonicDke:
case kPanasonicJke:
case kPanasonicLke:
case kPanasonicNke:
case kPanasonicCkp:
break;
default: // Only proceed if we know what to do.
return;
}
// clear & set the various bits and bytes.
remote_state[13] &= 0xF0;
remote_state[17] = 0x00;
remote_state[21] &= 0b11101111;
remote_state[23] = 0x81;
remote_state[25] = 0x00;
switch (model) {
case kPanasonicLke:
remote_state[13] |= 0x02;
remote_state[17] = 0x06;
break;
case kPanasonicDke:
remote_state[23] = 0x01;
remote_state[25] = 0x06;
// Has to be done last as setSwingHorizontal has model check built-in
setSwingHorizontal(_swingh);
break;
case kPanasonicNke:
remote_state[17] = 0x06;
break;
case kPanasonicJke:
break;
case kPanasonicCkp:
remote_state[21] |= 0x10;
remote_state[23] = 0x01;
default:
break;
}
}
panasonic_ac_remote_model_t IRPanasonicAc::getModel() {
if (remote_state[17] == 0x00) {
if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01))
return kPanasonicCkp;
if (remote_state[23] & 0x80) return kPanasonicJke;
}
if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02)
return kPanasonicLke;
if (remote_state[23] == 0x01) return kPanasonicDke;
if (remote_state[17] == 0x06) return kPanasonicNke;
return kPanasonicUnknown;
}
uint8_t *IRPanasonicAc::getRaw() {
fixChecksum();
return remote_state;
}
void IRPanasonicAc::setRaw(const uint8_t state[]) {
for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) {
remote_state[i] = state[i];
}
}
// Control the power state of the A/C unit.
//
// For CKP models, the remote has no memory of the power state the A/C unit
// should be in. For those models setting this on/true will toggle the power
// state of the Panasonic A/C unit with the next meessage.
// e.g. If the A/C unit is already on, setPower(true) will turn it off.
// If the A/C unit is already off, setPower(true) will turn it on.
// setPower(false) will leave the A/C power state as it was.
//
// For all other models, setPower(true) should set the internal state to
// turn it on, and setPower(false) should turn it off.
void IRPanasonicAc::setPower(const bool state) {
if (state)
on();
else
off();
}
// Return the A/C power state of the remote.
// Except for CKP models, where it returns if the power state will be toggled
// on the A/C unit when the next message is sent.
bool IRPanasonicAc::getPower() {
return (remote_state[13] & kPanasonicAcPower) == kPanasonicAcPower;
}
void IRPanasonicAc::on() { remote_state[13] |= kPanasonicAcPower; }
void IRPanasonicAc::off() { remote_state[13] &= ~kPanasonicAcPower; }
uint8_t IRPanasonicAc::getMode() { return remote_state[13] >> 4; }
void IRPanasonicAc::setMode(const uint8_t desired) {
uint8_t mode = kPanasonicAcAuto; // Default to Auto mode.
switch (desired) {
case kPanasonicAcFan:
// Allegedly Fan mode has a temperature of 27.
setTemp(kPanasonicAcFanModeTemp, false);
mode = desired;
break;
case kPanasonicAcAuto:
case kPanasonicAcCool:
case kPanasonicAcHeat:
case kPanasonicAcDry:
mode = desired;
// Set the temp to the saved temp, just incase our previous mode was Fan.
setTemp(_temp);
break;
}
remote_state[13] &= 0x0F; // Clear the previous mode bits.
remote_state[13] |= mode << 4;
}
uint8_t IRPanasonicAc::getTemp() { return remote_state[14] >> 1; }
// Set the desitred temperature in Celcius.
// Args:
// celsius: The temperature to set the A/C unit to.
// remember: A boolean flag for the class to remember the temperature.
//
// Automatically safely limits the temp to the operating range supported.
void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) {
uint8_t temperature;
temperature = std::max(celsius, kPanasonicAcMinTemp);
temperature = std::min(temperature, kPanasonicAcMaxTemp);
remote_state[14] = temperature << 1;
if (remember) _temp = temperature;
}
uint8_t IRPanasonicAc::getSwingVertical() { return remote_state[16] & 0x0F; }
void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) {
uint8_t elevation = desired_elevation;
if (elevation != kPanasonicAcSwingVAuto) {
elevation = std::max(elevation, kPanasonicAcSwingVUp);
elevation = std::min(elevation, kPanasonicAcSwingVDown);
}
remote_state[16] &= 0xF0;
remote_state[16] |= elevation;
}
uint8_t IRPanasonicAc::getSwingHorizontal() { return remote_state[17]; }
void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) {
switch (desired_direction) {
case kPanasonicAcSwingHAuto:
case kPanasonicAcSwingHMiddle:
case kPanasonicAcSwingHFullLeft:
case kPanasonicAcSwingHLeft:
case kPanasonicAcSwingHRight:
case kPanasonicAcSwingHFullRight:
break;
default: // Ignore anything that isn't valid.
return;
}
_swingh = desired_direction; // Store the direction for later.
uint8_t direction = desired_direction;
switch (getModel()) {
case kPanasonicDke:
break;
case kPanasonicNke:
case kPanasonicLke:
direction = kPanasonicAcSwingHMiddle;
break;
default: // Ignore everything else.
return;
}
remote_state[17] = direction;
}
void IRPanasonicAc::setFan(const uint8_t speed) {
if (speed <= kPanasonicAcFanMax || speed == kPanasonicAcFanAuto)
remote_state[16] =
(remote_state[16] & 0x0F) | ((speed + kPanasonicAcFanOffset) << 4);
}
uint8_t IRPanasonicAc::getFan() {
return (remote_state[16] >> 4) - kPanasonicAcFanOffset;
}
bool IRPanasonicAc::getQuiet() {
if (getModel() == kPanasonicCkp)
return remote_state[21] & kPanasonicAcQuietCkp;
else
return remote_state[21] & kPanasonicAcQuiet;
}
void IRPanasonicAc::setQuiet(const bool state) {
uint8_t quiet;
if (getModel() == kPanasonicCkp)
quiet = kPanasonicAcQuietCkp;
else
quiet = kPanasonicAcQuiet;
if (state) {
setPowerful(false); // Powerful is mutually exclusive.
remote_state[21] |= quiet;
} else {
remote_state[21] &= ~quiet;
}
}
bool IRPanasonicAc::getPowerful() {
if (getModel() == kPanasonicCkp)
return remote_state[21] & kPanasonicAcPowerfulCkp;
else
return remote_state[21] & kPanasonicAcPowerful;
}
void IRPanasonicAc::setPowerful(const bool state) {
uint8_t powerful;
if (getModel() == kPanasonicCkp)
powerful = kPanasonicAcPowerfulCkp;
else
powerful = kPanasonicAcPowerful;
if (state) {
setQuiet(false); // Quiet is mutually exclusive.
remote_state[21] |= powerful;
} else {
remote_state[21] &= ~powerful;
}
}
uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) {
return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59);
}
uint16_t IRPanasonicAc::getClock() {
uint16_t result = ((remote_state[25] & 0b00000111) << 8) + remote_state[24];
if (result == kPanasonicAcTimeSpecial) return 0;
return result;
}
void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) {
uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax);
if (mins_since_midnight == kPanasonicAcTimeSpecial)
corrected = kPanasonicAcTimeSpecial;
remote_state[24] = corrected & 0xFF;
remote_state[25] &= 0b11111000;
remote_state[25] |= (corrected >> 8);
}
uint16_t IRPanasonicAc::getOnTimer() {
uint16_t result = ((remote_state[19] & 0b00000111) << 8) + remote_state[18];
if (result == kPanasonicAcTimeSpecial) return 0;
return result;
}
void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight,
const bool enable) {
// Ensure it's on a 10 minute boundary and no overflow.
uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax);
corrected -= corrected % 10;
if (mins_since_midnight == kPanasonicAcTimeSpecial)
corrected = kPanasonicAcTimeSpecial;
if (enable)
remote_state[13] |= kPanasonicAcOnTimer; // Set the Ontimer flag.
else
remote_state[13] &= ~kPanasonicAcOnTimer; // Clear the Ontimer flag.
// Store the time.
remote_state[18] = corrected & 0xFF;
remote_state[19] &= 0b11111000;
remote_state[19] |= (corrected >> 8);
}
void IRPanasonicAc::cancelOnTimer() { setOnTimer(0, false); }
bool IRPanasonicAc::isOnTimerEnabled() {
return remote_state[13] & kPanasonicAcOnTimer;
}
uint16_t IRPanasonicAc::getOffTimer() {
uint16_t result =
((remote_state[20] & 0b01111111) << 4) + (remote_state[19] >> 4);
if (result == kPanasonicAcTimeSpecial) return 0;
return result;
}
void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight,
const bool enable) {
// Ensure its on a 10 minute boundary and no overflow.
uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax);
corrected -= corrected % 10;
if (mins_since_midnight == kPanasonicAcTimeSpecial)
corrected = kPanasonicAcTimeSpecial;
if (enable)
remote_state[13] |= kPanasonicAcOffTimer; // Set the OffTimer flag.
else
remote_state[13] &= ~kPanasonicAcOffTimer; // Clear the OffTimer flag.
// Store the time.
remote_state[19] &= 0b00001111;
remote_state[19] |= (corrected & 0b00001111) << 4;
remote_state[20] &= 0b10000000;
remote_state[20] |= corrected >> 4;
}
void IRPanasonicAc::cancelOffTimer() { setOffTimer(0, false); }
bool IRPanasonicAc::isOffTimerEnabled() {
return remote_state[13] & kPanasonicAcOffTimer;
}
#ifdef ARDUINO
String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) {
String result = "";
#else
std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) {
std::string result = "";
#endif // ARDUINO
result += uint64ToString(mins_since_midnight / 60) + ":";
uint8_t mins = mins_since_midnight % 60;
if (mins < 10) result += "0"; // Zero pad the minutes.
return result + uint64ToString(mins);
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRPanasonicAc::toString() {
String result = "";
#else
std::string IRPanasonicAc::toString() {
std::string result = "";
#endif // ARDUINO
result += "Model: " + uint64ToString(getModel());
switch (getModel()) {
case kPanasonicDke:
result += " (DKE)";
break;
case kPanasonicJke:
result += " (JKE)";
break;
case kPanasonicNke:
result += " (NKE)";
break;
case kPanasonicLke:
result += " (LKE)";
break;
case kPanasonicCkp:
result += " (CKP)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Power: ";
if (getPower())
result += "On";
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kPanasonicAcAuto:
result += " (AUTO)";
break;
case kPanasonicAcCool:
result += " (COOL)";
break;
case kPanasonicAcHeat:
result += " (HEAT)";
break;
case kPanasonicAcDry:
result += " (DRY)";
break;
case kPanasonicAcFan:
result += " (FAN)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kPanasonicAcFanAuto:
result += " (AUTO)";
break;
case kPanasonicAcFanMax:
result += " (MAX)";
break;
case kPanasonicAcFanMin:
result += " (MIN)";
break;
default:
result += " (UNKNOWN)";
break;
}
result += ", Swing (Vertical): " + uint64ToString(getSwingVertical());
switch (getSwingVertical()) {
case kPanasonicAcSwingVAuto:
result += " (AUTO)";
break;
case kPanasonicAcSwingVUp:
result += " (Full Up)";
break;
case kPanasonicAcSwingVDown:
result += " (Full Down)";
break;
case 2:
case 3:
case 4:
break;
default:
result += " (UNKNOWN)";
break;
}
switch (getModel()) {
case kPanasonicJke:
case kPanasonicCkp:
break; // No Horizontal Swing support.
default:
result += ", Swing (Horizontal): " + uint64ToString(getSwingHorizontal());
switch (getSwingHorizontal()) {
case kPanasonicAcSwingHAuto:
result += " (AUTO)";
break;
case kPanasonicAcSwingHFullLeft:
result += " (Full Left)";
break;
case kPanasonicAcSwingHLeft:
result += " (Left)";
break;
case kPanasonicAcSwingHMiddle:
result += " (Middle)";
break;
case kPanasonicAcSwingHFullRight:
result += " (Full Right)";
break;
case kPanasonicAcSwingHRight:
result += " (Right)";
break;
default:
result += " (UNKNOWN)";
break;
}
}
result += ", Quiet: ";
if (getQuiet())
result += "On";
else
result += "Off";
result += ", Powerful: ";
if (getPowerful())
result += "On";
else
result += "Off";
result += ", Clock: " + timeToString(getClock());
result += ", On Timer: ";
if (isOnTimerEnabled())
result += timeToString(getOnTimer());
else
result += "Off";
result += ", Off Timer: ";
if (isOffTimerEnabled())
result += timeToString(getOffTimer());
else
result += "Off";
return result;
}
#if DECODE_PANASONIC_AC
// Decode the supplied Panasonic AC 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 kPanasonicAcBits.
// 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 work with real device(s).
//
// Panasonic A/C models supported:
// A/C Series/models:
// JKE, LKE, DKE, & NKE series.
// CS-YW9MKD
// A/C Remotes:
// A75C3747 (Confirmed)
// A75C3704
bool IRrecv::decodePanasonicAC(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;
uint8_t min_nr_of_messages = 1;
if (strict) {
if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits)
return false; // Not strictly a PANASONIC_AC message.
}
if (results->rawlen <
min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1)
return false; // Can't possibly be a valid PANASONIC_AC message.
uint16_t dataBitsSoFar = 0;
uint16_t offset = kStartOffset;
match_result_t data_result;
// Header
if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
// Calculate how long the common tick time is based on the header mark.
uint32_t m_tick =
results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
// Calculate how long the common tick time is based on the header space.
uint32_t s_tick =
results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks;
uint16_t i = 0;
// Data (Section #1)
// Keep reading bytes until we either run out of section or state to fill.
for (; offset <= results->rawlen - 16 && i < kPanasonicAcSection1Length;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(
&(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick,
kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick,
kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance,
kPanasonicAcExcess, false);
if (data_result.success == false) {
DPRINT("DEBUG: offset = ");
DPRINTLN(offset + data_result.used);
return false; // Fail
}
results->state[i] = data_result.data;
}
// Section footer.
if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
if (!matchSpace(results->rawbuf[offset++], kPanasonicAcSectionGap,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
// Header.
if (!matchMark(results->rawbuf[offset++], kPanasonicHdrMarkTicks * m_tick,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
if (!matchSpace(results->rawbuf[offset++], kPanasonicHdrSpaceTicks * s_tick,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
// Data (Section #2)
// Keep reading bytes until we either run out of data.
for (; offset <= results->rawlen - 16 && i < nbits / 8;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(
&(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick,
kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick,
kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance,
kPanasonicAcExcess, false);
if (data_result.success == false) {
DPRINT("DEBUG: offset = ");
DPRINTLN(offset + data_result.used);
return false; // Fail
}
results->state[i] = data_result.data;
}
// Message Footer.
if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick,
kPanasonicAcTolerance, kPanasonicAcExcess))
return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kPanasonicAcMessageGap))
return false;
// Compliance
if (strict) {
// Check the signatures of the section blocks. They start with 0x02& 0x20.
if (results->state[0] != 0x02 || results->state[1] != 0x20 ||
results->state[8] != 0x02 || results->state[9] != 0x20)
return false;
if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false;
}
// Success
results->decode_type = PANASONIC_AC;
results->bits = nbits;
return true;
}
#endif // DECODE_PANASONIC_AC
+145
View File
@@ -0,0 +1,145 @@
// Copyright 2018 David Conran
#ifndef IR_PANASONIC_H_
#define IR_PANASONIC_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC
// P P A A NN N A A S O O NN N I C
// PPPP AAAAA N N N AAAAA SSS O O N N N I C
// P A A N NN A A S O O N NN I C
// P A A N N A A SSSS OOO N N IIIII CCCC
// Panasonic A/C support heavily influenced by:
// https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp
// Constants
const uint16_t kPanasonicFreq = 36700;
const uint16_t kPanasonicAcExcess = 0;
// Much higher than usual. See issue #540.
const uint16_t kPanasonicAcTolerance = 40;
const uint8_t kPanasonicAcAuto = 0; // 0b0000
const uint8_t kPanasonicAcDry = 2; // 0b0010
const uint8_t kPanasonicAcCool = 3; // 0b0011
const uint8_t kPanasonicAcHeat = 4; // 0b0010
const uint8_t kPanasonicAcFan = 6; // 0b0110
const uint8_t kPanasonicAcFanMin = 0;
const uint8_t kPanasonicAcFanMax = 4;
const uint8_t kPanasonicAcFanAuto = 7;
const uint8_t kPanasonicAcFanOffset = 3;
const uint8_t kPanasonicAcPower = 1; // 0b1
const uint8_t kPanasonicAcMinTemp = 16; // Celsius
const uint8_t kPanasonicAcMaxTemp = 30; // Celsius
const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius
const uint8_t kPanasonicAcQuiet = 1; // 0b1
const uint8_t kPanasonicAcPowerful = 0x20; // 0b100000
// CKP models have Powerful and Quiet bits swapped.
const uint8_t kPanasonicAcQuietCkp = 0x20; // 0b100000
const uint8_t kPanasonicAcPowerfulCkp = 1; // 0b1
const uint8_t kPanasonicAcSwingVAuto = 0xF;
const uint8_t kPanasonicAcSwingVUp = 0x1;
const uint8_t kPanasonicAcSwingVDown = 0x5;
const uint8_t kPanasonicAcSwingHAuto = 0xD;
const uint8_t kPanasonicAcSwingHMiddle = 0x6;
const uint8_t kPanasonicAcSwingHFullLeft = 0x9;
const uint8_t kPanasonicAcSwingHLeft = 0xA;
const uint8_t kPanasonicAcSwingHRight = 0xB;
const uint8_t kPanasonicAcSwingHFullRight = 0xC;
const uint8_t kPanasonicAcChecksumInit = 0xF4;
const uint8_t kPanasonicAcOnTimer = 0b00000010;
const uint8_t kPanasonicAcOffTimer = 0b00000100;
const uint16_t kPanasonicAcTimeMax = 23 * 60 + 59; // Mins since midnight.
const uint16_t kPanasonicAcTimeSpecial = 0x600;
const uint8_t kPanasonicKnownGoodState[kPanasonicAcStateLength] = {
0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02,
0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
0x00, 0x0E, 0xE0, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00};
enum panasonic_ac_remote_model_t {
kPanasonicUnknown = 0,
kPanasonicLke = 1,
kPanasonicNke = 2,
kPanasonicDke = 3,
kPanasonicJke = 4,
kPanasonicCkp = 5,
};
class IRPanasonicAc {
public:
explicit IRPanasonicAc(uint16_t pin);
void stateReset();
#if SEND_PANASONIC
void send(const uint16_t repeat = kPanasonicAcDefaultRepeat);
#endif // SEND_PANASONIC
void begin();
void on();
void off();
void setPower(const bool state);
bool getPower();
void setTemp(const uint8_t temp, const bool remember = true);
uint8_t getTemp();
void setFan(const uint8_t fan);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
void setRaw(const uint8_t state[]);
uint8_t *getRaw();
static bool validChecksum(uint8_t *state,
const uint16_t length = kPanasonicAcStateLength);
static uint8_t calcChecksum(uint8_t *state,
const uint16_t length = kPanasonicAcStateLength);
void setQuiet(const bool state);
bool getQuiet();
void setPowerful(const bool state);
bool getPowerful();
void setModel(const panasonic_ac_remote_model_t model);
panasonic_ac_remote_model_t getModel();
void setSwingVertical(const uint8_t elevation);
uint8_t getSwingVertical();
void setSwingHorizontal(const uint8_t direction);
uint8_t getSwingHorizontal();
static uint16_t encodeTime(const uint8_t hours, const uint8_t mins);
uint16_t getClock();
void setClock(const uint16_t mins_since_midnight);
uint16_t getOnTimer();
void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true);
void cancelOnTimer();
bool isOnTimerEnabled();
uint16_t getOffTimer();
void setOffTimer(const uint16_t mins_since_midnight,
const bool enable = true);
void cancelOffTimer();
bool isOffTimerEnabled();
#ifdef ARDUINO
String toString();
static String timeToString(const uint16_t mins_since_midnight);
#else
std::string toString();
static std::string timeToString(const uint16_t mins_since_midnight);
#endif
#ifndef UNIT_TEST
private:
#endif
uint8_t remote_state[kPanasonicAcStateLength];
uint8_t _swingh;
uint8_t _temp;
void fixChecksum(const uint16_t length = kPanasonicAcStateLength);
static uint8_t calcChecksum(const uint8_t *state,
const uint16_t length = kPanasonicAcStateLength);
IRsend _irsend;
};
#endif // IR_PANASONIC_H_
+143
View File
@@ -0,0 +1,143 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017, 2018 David Conran
// Copyright 2018 Kamil Palczewski
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
#include "ir_NEC.h"
// PPPP III OOO N N EEEE EEEE RRRR
// P P I O O NN N E E R R
// PPPP I O O N N N EEE EEE RRRR
// P I O O N NN E E R R
// P III OOO N N EEEE EEEE R RR
// Ref:
// http://adrian-kingston.com/IRFormatPioneer.htm
#if SEND_PIONEER
// Send a raw Pioneer formatted message.
//
// Args:
// data: The message to be sent.
// nbits: The number of bits of the message to be sent.
// Typically kPioneerBits.
// repeat: The number of times the command is to be repeated.
//
// Status: BETA / Expected to be working.
//
// Ref:
// http://adrian-kingston.com/IRFormatPioneer.htm
void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
// If nbits is to big, or is odd, abort.
if (nbits > sizeof(data) * 8 || nbits % 2 == 1) return;
// send 1st part of the code
sendNEC(data >> (nbits / 2), nbits / 2, 0);
// send 2nd part of the code
sendNEC(data & (((uint64_t)1 << (nbits / 2)) - 1), nbits / 2, repeat);
}
// Calculate the raw Pioneer data code based on two NEC sub-codes
// Args:
// address A 16-bit "published" NEC value.
// command: A 16-bit "published" NEC value.
// Returns:
// A raw 64-bit Pioneer message code.
//
// Status: BETA / Expected to work.
//
// Note:
// Address & Command can be take from a decode result OR from the spreadsheets
// located at:
// https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers
// where the first part is considered the address,
// and the second the command.
// e.g.
// "A556+AF20" is an Address of 0xA556 & a Command of 0xAF20.
uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) {
return (((uint64_t)encodeNEC(address >> 8, address & 0xFF)) << 32) |
encodeNEC(command >> 8, command & 0xFF);
}
#endif // SEND_PIONEER
#if DECODE_PIONEER
// Decode the supplied Pioneer 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 kPioneerBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Should be working. (Self decodes & real examples)
//
bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1)
return false; // Can't possibly be a valid Pioneer message.
if (strict && nbits != kPioneerBits)
return false; // Not strictly an Pioneer message.
uint64_t data = 0;
uint16_t offset = kStartOffset;
for (uint16_t section = 0; section < 2; section++) {
// Header
if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false;
// Calculate how long the lowest tick time is based on the header mark.
uint32_t mark_tick =
results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space.
uint32_t space_tick =
results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks;
//
// Data
match_result_t data_result = matchData(
&(results->rawbuf[offset]), nbits / 2, kNecBitMarkTicks * mark_tick,
kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick,
kNecZeroSpaceTicks * space_tick);
if (data_result.success == false) return false;
uint8_t command = data_result.data >> 8;
uint8_t command_inverted = data_result.data;
uint8_t address = data_result.data >> 24;
uint8_t address_inverted = data_result.data >> 16;
// Compliance
if (strict) {
if (command != (command_inverted ^ 0xFF))
return false; // Command integrity failed.
if (address != (address_inverted ^ 0xFF))
return false; // Address integrity failed.
}
data = (data << (nbits / 2)) + data_result.data;
offset += data_result.used;
// NEC-like commands and addresses are technically in LSB first order so the
// final versions have to be reversed.
uint16_t code = reverseBits((command << 8) + address, 16);
if (section)
results->command = code;
else
results->address = code;
// Footer
if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick))
return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kNecMinGapTicks * space_tick))
return false;
}
// Success
results->bits = nbits;
results->value = data;
results->decode_type = PIONEER;
return true;
}
#endif // DECODE_PIONEER
+15 -15
View File
@@ -10,12 +10,12 @@
// PP rr oooo nn nn tttt oooo // PP rr oooo nn nn tttt oooo
// Constants // Constants
#define PRONTO_FREQ_FACTOR 0.241246 const float kProntoFreqFactor = 0.241246;
#define PRONTO_TYPE_OFFSET 0U const uint16_t kProntoTypeOffset = 0;
#define PRONTO_FREQ_OFFSET 1U const uint16_t kProntoFreqOffset = 1;
#define PRONTO_SEQ_1_LEN_OFFSET 2U const uint16_t kProntoSeq1LenOffset = 2;
#define PRONTO_SEQ_2_LEN_OFFSET 3U const uint16_t kProntoSeq2LenOffset = 3;
#define PRONTO_DATA_OFFSET 4U const uint16_t kProntoDataOffset = 4;
#if SEND_PRONTO #if SEND_PRONTO
// Send a Pronto Code formatted message. // Send a Pronto Code formatted message.
@@ -49,29 +49,29 @@
// 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, // 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018,
// 0x0018, 0x03f6}; // 0x0018, 0x03f6};
// // Send the Pronto(Sony) code. Repeat twice as Sony's require that. // // Send the Pronto(Sony) code. Repeat twice as Sony's require that.
// sendPronto(prontoCode, 46, SONY_MIN_REPEAT); // sendPronto(prontoCode, 46, kSonyMinRepeat);
// //
// Ref: // Ref:
// http://www.etcwiki.org/wiki/Pronto_Infrared_Format // http://www.etcwiki.org/wiki/Pronto_Infrared_Format
// http://www.remotecentral.com/features/irdisp2.htm // http://www.remotecentral.com/features/irdisp2.htm
void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) { void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) {
// Check we have enough data to work out what to send. // Check we have enough data to work out what to send.
if (len < PRONTO_MIN_LENGTH) return; if (len < kProntoMinLength) return;
// We only know how to deal with 'raw' pronto codes types. Reject all others. // We only know how to deal with 'raw' pronto codes types. Reject all others.
if (data[PRONTO_TYPE_OFFSET] != 0) return; if (data[kProntoTypeOffset] != 0) return;
// Pronto frequency is in Hz. // Pronto frequency is in Hz.
uint16_t hz = (uint16_t) (1000000U / (data[PRONTO_FREQ_OFFSET] * uint16_t hz =
PRONTO_FREQ_FACTOR)); (uint16_t)(1000000U / (data[kProntoFreqOffset] * kProntoFreqFactor));
enableIROut(hz); enableIROut(hz);
// Grab the length of the two sequences. // Grab the length of the two sequences.
uint16_t seq_1_len = data[PRONTO_SEQ_1_LEN_OFFSET] * 2; uint16_t seq_1_len = data[kProntoSeq1LenOffset] * 2;
uint16_t seq_2_len = data[PRONTO_SEQ_2_LEN_OFFSET] * 2; uint16_t seq_2_len = data[kProntoSeq2LenOffset] * 2;
// Calculate where each sequence starts in the buffer. // Calculate where each sequence starts in the buffer.
uint16_t seq_1_start = PRONTO_DATA_OFFSET; uint16_t seq_1_start = kProntoDataOffset;
uint16_t seq_2_start = PRONTO_DATA_OFFSET + seq_1_len; uint16_t seq_2_start = kProntoDataOffset + seq_1_len;
uint32_t periodic_time = calcUSecPeriod(hz, false); uint32_t periodic_time = calcUSecPeriod(hz, false);
+96 -99
View File
@@ -21,29 +21,31 @@
// Ref: // Ref:
// https://en.wikipedia.org/wiki/RC-5 // https://en.wikipedia.org/wiki/RC-5
// http://www.sbprojects.com/knowledge/ir/rc5.php // http://www.sbprojects.com/knowledge/ir/rc5.php
#define MIN_RC5_SAMPLES 11U
#define MIN_RC6_SAMPLES 1U const uint16_t kRc5T1 = 889;
#define RC5_T1 889U const uint32_t kRc5MinCommandLength = 113778;
#define RC5_MIN_COMMAND_LENGTH 113778UL const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1);
#define RC5_MIN_GAP (RC5_MIN_COMMAND_LENGTH - RC5_RAW_BITS * (2 * RC5_T1)) const uint16_t kRc5ToggleMask = 0x800; // The 12th bit.
#define RC5_TOGGLE_MASK 0x800U // (The 12th bit) const uint16_t kRc5SamplesMin = 11;
// RC-6 // RC-6
// Ref: // Ref:
// https://en.wikipedia.org/wiki/RC-6 // https://en.wikipedia.org/wiki/RC-6
// http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ // http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/
#define RC6_TICK 444U
#define RC6_HDR_MARK_TICKS 6U const uint16_t kRc6Tick = 444;
#define RC6_HDR_MARK (RC6_HDR_MARK_TICKS * RC6_TICK) const uint16_t kRc6HdrMarkTicks = 6;
#define RC6_HDR_SPACE_TICKS 2U const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick;
#define RC6_HDR_SPACE (RC6_HDR_SPACE_TICKS * RC6_TICK) const uint16_t kRc6HdrSpaceTicks = 2;
#define RC6_RPT_LENGTH_TICKS 187U const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick;
#define RC6_RPT_LENGTH (RC6_RPT_LENGTH_TICKS * RC6_TICK) const uint16_t kRc6RptLengthTicks = 187;
#define RC6_TOGGLE_MASK 0x10000UL // (The 17th bit) const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick;
#define RC6_36_TOGGLE_MASK 0x8000U // (The 16th bit) const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit.
const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit.
// Common (getRClevel()) // Common (getRClevel())
const int16_t kMARK = 0; const int16_t kMark = 0;
const int16_t kSPACE = 1; const int16_t kSpace = 1;
#if SEND_RC5 #if SEND_RC5
// Send a Philips RC-5/RC-5X packet. // Send a Philips RC-5/RC-5X packet.
@@ -67,14 +69,13 @@ const int16_t kSPACE = 1;
// TODO(anyone): // TODO(anyone):
// Testing of the RC-5X components. // Testing of the RC-5X components.
void IRsend::sendRC5(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendRC5(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (nbits > sizeof(data) * 8) if (nbits > sizeof(data) * 8) return; // We can't send something that big.
return; // We can't send something that big.
bool skipSpace = true; bool skipSpace = true;
bool field_bit = true; bool field_bit = true;
// Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle.
enableIROut(36, 25); enableIROut(36, 25);
if (nbits >= RC5X_BITS) { // Is this a RC-5X message? if (nbits >= kRC5XBits) { // Is this a RC-5X message?
// field bit is the inverted MSB of RC-5X data. // field bit is the inverted MSB of RC-5X data.
field_bit = ((data >> (nbits - 1)) ^ 1) & 1; field_bit = ((data >> (nbits - 1)) ^ 1) & 1;
nbits--; nbits--;
@@ -89,28 +90,28 @@ void IRsend::sendRC5(uint64_t data, uint16_t nbits, uint16_t repeat) {
if (skipSpace) if (skipSpace)
skipSpace = false; // First time through, we assume the leading space(). skipSpace = false; // First time through, we assume the leading space().
else else
space(RC5_T1); space(kRc5T1);
mark(RC5_T1); mark(kRc5T1);
// Field/Second start bit. // Field/Second start bit.
if (field_bit) { // Send a 1. Normal for RC-5. if (field_bit) { // Send a 1. Normal for RC-5.
space(RC5_T1); space(kRc5T1);
mark(RC5_T1); mark(kRc5T1);
} else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1.
mark(RC5_T1); mark(kRc5T1);
space(RC5_T1); space(kRc5T1);
} }
// Data // Data
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
if (data & mask) { // 1 if (data & mask) { // 1
space(RC5_T1); // 1 is space, then mark. space(kRc5T1); // 1 is space, then mark.
mark(RC5_T1); mark(kRc5T1);
} else { // 0 } else { // 0
mark(RC5_T1); // 0 is mark, then space. mark(kRc5T1); // 0 is mark, then space.
space(RC5_T1); space(kRc5T1);
} }
// Footer // Footer
space(std::max(RC5_MIN_GAP, RC5_MIN_COMMAND_LENGTH - usecTimer.elapsed())); space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed()));
} }
} }
@@ -131,8 +132,7 @@ void IRsend::sendRC5(uint64_t data, uint16_t nbits, uint16_t repeat) {
// https://en.wikipedia.org/wiki/RC-5 // https://en.wikipedia.org/wiki/RC-5
uint16_t IRsend::encodeRC5(uint8_t address, uint8_t command, uint16_t IRsend::encodeRC5(uint8_t address, uint8_t command,
bool key_released) { bool key_released) {
return (key_released << (RC5_BITS - 1)) | return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) |
((address & 0x1f) << 6) |
(command & 0x3F); (command & 0x3F);
} }
@@ -156,7 +156,7 @@ uint16_t IRsend::encodeRC5X(uint8_t address, uint8_t command,
// The 2nd start/field bit (MSB of the return value) is the value of the 7th // The 2nd start/field bit (MSB of the return value) is the value of the 7th
// command bit. // command bit.
bool s2 = (command >> 6) & 1; bool s2 = (command >> 6) & 1;
return ((uint16_t) s2 << (RC5X_BITS - 1)) | return ((uint16_t)s2 << (kRC5XBits - 1)) |
encodeRC5(address, command, key_released); encodeRC5(address, command, key_released);
} }
@@ -174,9 +174,7 @@ uint16_t IRsend::encodeRC5X(uint8_t address, uint8_t command,
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/rc5.php // http://www.sbprojects.com/knowledge/ir/rc5.php
// https://en.wikipedia.org/wiki/RC-5 // https://en.wikipedia.org/wiki/RC-5
uint64_t IRsend::toggleRC5(uint64_t data) { uint64_t IRsend::toggleRC5(uint64_t data) { return data ^ kRc5ToggleMask; }
return data ^ RC5_TOGGLE_MASK;
}
#endif // SEND_RC5 #endif // SEND_RC5
#if SEND_RC6 #if SEND_RC6
@@ -198,9 +196,8 @@ uint64_t IRsend::toggleRC5(uint64_t data) {
// http://www.sbprojects.com/knowledge/ir/rc6.php // http://www.sbprojects.com/knowledge/ir/rc6.php
// http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html // http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html
uint64_t IRsend::toggleRC6(uint64_t data, uint16_t nbits) { uint64_t IRsend::toggleRC6(uint64_t data, uint16_t nbits) {
if (nbits == RC6_36_BITS) if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask;
return data ^ RC6_36_TOGGLE_MASK; return data ^ kRc6ToggleMask;
return data ^ RC6_TOGGLE_MASK;
} }
// Encode a Philips RC-6 data message. // Encode a Philips RC-6 data message.
@@ -220,12 +217,11 @@ uint64_t IRsend::toggleRC6(uint64_t data, uint16_t nbits) {
// http://www.sbprojects.com/knowledge/ir/rc6.php // http://www.sbprojects.com/knowledge/ir/rc6.php
// http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html // http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html
// http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ // http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/
uint64_t IRsend::encodeRC6(uint32_t address, uint8_t command, uint64_t IRsend::encodeRC6(uint32_t address, uint8_t command, uint16_t mode) {
uint16_t mode) {
switch (mode) { switch (mode) {
case RC6_MODE0_BITS: case kRC6Mode0Bits:
return ((address & 0xFFF) << 8) | (command & 0xFF); return ((address & 0xFFF) << 8) | (command & 0xFF);
case RC6_36_BITS: case kRC6_36Bits:
return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF);
default: default:
return 0; return 0;
@@ -249,24 +245,23 @@ uint64_t IRsend::encodeRC6(uint32_t address, uint8_t command,
// https://en.wikipedia.org/wiki/Manchester_code // https://en.wikipedia.org/wiki/Manchester_code
void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Check we can send the number of bits requested. // Check we can send the number of bits requested.
if (nbits > sizeof(data) * 8) if (nbits > sizeof(data) * 8) return;
return;
// Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle.
enableIROut(36, 33); enableIROut(36, 33);
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
// Header // Header
mark(RC6_HDR_MARK); mark(kRc6HdrMark);
space(RC6_HDR_SPACE); space(kRc6HdrSpace);
// Start bit. // Start bit.
mark(RC6_TICK); // mark, then space == 0x1. mark(kRc6Tick); // mark, then space == 0x1.
space(RC6_TICK); space(kRc6Tick);
// Data // Data
uint16_t bitTime; uint16_t bitTime;
for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) {
if (i == 4) // The fourth bit we send is a "double width trailer bit". if (i == 4) // The fourth bit we send is a "double width trailer bit".
bitTime = 2 * RC6_TICK; // double-wide trailer bit bitTime = 2 * kRc6Tick; // double-wide trailer bit
else else
bitTime = RC6_TICK; // Normal bit bitTime = kRc6Tick; // Normal bit
if (data & mask) { // 1 if (data & mask) { // 1
mark(bitTime); mark(bitTime);
space(bitTime); space(bitTime);
@@ -276,7 +271,7 @@ void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) {
} }
} }
// Footer // Footer
space(RC6_RPT_LENGTH); space(kRc6RptLength);
} }
} }
#endif // SEND_RC6 #endif // SEND_RC6
@@ -293,42 +288,46 @@ void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) {
// offset: Ptr to the currect offset to the rawbuf. // offset: Ptr to the currect offset to the rawbuf.
// used: Ptr to the current used counter. // used: Ptr to the current used counter.
// bitTime: Time interval of single bit in microseconds. // bitTime: Time interval of single bit in microseconds.
// maxwidth: Maximum number of successive levels to find in a single level
// (default 3)
// Returns: // Returns:
// int: MARK, SPACE, or -1 for error (The measured time interval is not a // int: MARK, SPACE, or -1 for error (The measured time interval is not a
// multiple of t1.) // multiple of t1.)
// Ref: // Ref:
// https://en.wikipedia.org/wiki/Manchester_code // https://en.wikipedia.org/wiki/Manchester_code
int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset,
uint16_t *used, uint16_t bitTime, uint16_t *used, uint16_t bitTime, uint8_t tolerance,
uint8_t tolerance, int16_t excess, uint16_t delta) { int16_t excess, uint16_t delta, uint8_t maxwidth) {
DPRINT("DEBUG: getRClevel: offset = "); DPRINT("DEBUG: getRClevel: offset = ");
DPRINTLN(uint64ToString(*offset)); DPRINTLN(uint64ToString(*offset));
DPRINT("DEBUG: getRClevel: rawlen = ");
DPRINTLN(uint64ToString(results->rawlen));
if (*offset >= results->rawlen) { if (*offset >= results->rawlen) {
DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf");
return kSPACE; // After end of recorded buffer, assume SPACE. return kSpace; // After end of recorded buffer, assume SPACE.
} }
uint16_t width = results->rawbuf[*offset]; uint16_t width = results->rawbuf[*offset];
// If the value of offset is odd, it's a MARK. Even, it's a SPACE. // If the value of offset is odd, it's a MARK. Even, it's a SPACE.
uint16_t val = ((*offset) % 2) ? kMARK : kSPACE; uint16_t val = ((*offset) % 2) ? kMark : kSpace;
// Check to see if we have hit an inter-message gap (> 20ms). // Check to see if we have hit an inter-message gap (> 20ms).
if (val == kSPACE && width > 20000 - delta) { if (val == kSpace &&
(width > 20000 - delta || width > maxwidth * bitTime + delta)) {
DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap.");
return kSPACE; return kSpace;
} }
int16_t correction = (val == kMARK) ? excess : -excess; int16_t correction = (val == kMark) ? excess : -excess;
// Calculate the look-ahead for our current position in the buffer. // Calculate the look-ahead for our current position in the buffer.
uint16_t avail; uint16_t avail;
// Note: We want to match in greedy order as the other way leads to // Note: We want to match in greedy order as the other way leads to
// mismatches due to overlaps induced by the correction and tolerance // mismatches due to overlaps induced by the correction and tolerance
// values. // values.
if (match(width, 3 * bitTime + correction, tolerance, delta)) { for (avail = maxwidth; avail > 0; avail--) {
avail = 3; if (match(width, avail * bitTime + correction, tolerance, delta)) {
} else if (match(width, 2 * bitTime + correction, tolerance, delta)) { break;
avail = 2; }
} else if (match(width, bitTime + correction, tolerance, delta)) { }
avail = 1; if (!avail) {
} else {
DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting.");
return -1; // The width is not what we expected. return -1; // The width is not what we expected.
} }
@@ -339,7 +338,7 @@ int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset,
*used = 0; *used = 0;
(*offset)++; (*offset)++;
} }
if (val == kMARK) { if (val == kMark) {
DPRINTLN("DEBUG: getRClevel: MARK"); DPRINTLN("DEBUG: getRClevel: MARK");
} else { } else {
DPRINTLN("DEBUG: getRClevel: SPACE"); DPRINTLN("DEBUG: getRClevel: SPACE");
@@ -371,28 +370,28 @@ int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset,
// TODO(anyone): // TODO(anyone):
// Serious testing of the RC-5X and strict aspects needs to be done. // Serious testing of the RC-5X and strict aspects needs to be done.
bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < MIN_RC5_SAMPLES + HEADER - 1) return false; if (results->rawlen < kRc5SamplesMin + kHeader - 1) return false;
// Compliance // Compliance
if (strict && nbits != RC5_BITS && nbits != RC5X_BITS) if (strict && nbits != kRC5Bits && nbits != kRC5XBits)
return false; // It's neither RC-5 or RC-5X. return false; // It's neither RC-5 or RC-5X.
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t used = 0; uint16_t used = 0;
bool is_rc5x = false; bool is_rc5x = false;
uint64_t data = 0; uint64_t data = 0;
// Header // Header
// Get start bit #1. // Get start bit #1.
if (getRClevel(results, &offset, &used, RC5_T1) != kMARK) return false; if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false;
// Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol)
uint16_t actual_bits = 1; uint16_t actual_bits = 1;
int16_t levelA = getRClevel(results, &offset, &used, RC5_T1); int16_t levelA = getRClevel(results, &offset, &used, kRc5T1);
int16_t levelB = getRClevel(results, &offset, &used, RC5_T1); int16_t levelB = getRClevel(results, &offset, &used, kRc5T1);
if (levelA == kSPACE && levelB == kMARK) { // Matched a 1. if (levelA == kSpace && levelB == kMark) { // Matched a 1.
is_rc5x = false; is_rc5x = false;
} else if (levelA == kMARK && levelB == kSPACE) { // Matched a 0. } else if (levelA == kMark && levelB == kSpace) { // Matched a 0.
if (nbits <= RC5_BITS) return false; // Field bit must be '1' for RC5. if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5.
is_rc5x = true; is_rc5x = true;
data = 1; data = 1;
} else { } else {
@@ -401,11 +400,11 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) {
// Data // Data
for (; offset < results->rawlen; actual_bits++) { for (; offset < results->rawlen; actual_bits++) {
int16_t levelA = getRClevel(results, &offset, &used, RC5_T1); int16_t levelA = getRClevel(results, &offset, &used, kRc5T1);
int16_t levelB = getRClevel(results, &offset, &used, RC5_T1); int16_t levelB = getRClevel(results, &offset, &used, kRc5T1);
if (levelA == kSPACE && levelB == kMARK) if (levelA == kSpace && levelB == kMark)
data = (data << 1) | 1; // 1 data = (data << 1) | 1; // 1
else if (levelA == kMARK && levelB == kSPACE) else if (levelA == kMark && levelB == kSpace)
data <<= 1; // 0 data <<= 1; // 0
else else
break; break;
@@ -414,8 +413,8 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) {
// Compliance // Compliance
if (actual_bits < nbits) return false; // Less data than we expected. if (actual_bits < nbits) return false; // Less data than we expected.
if (strict && actual_bits != RC5_BITS && if (strict && actual_bits != kRC5Bits && actual_bits != kRC5XBits)
actual_bits != RC5X_BITS) return false; return false;
// Success // Success
results->value = data; results->value = data;
@@ -452,7 +451,7 @@ bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) {
// TODO(anyone): // TODO(anyone):
// Testing of the strict compliance aspects. // Testing of the strict compliance aspects.
bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < HEADER + 2 + 4) // Up to the double-wide T bit. if (results->rawlen < kHeader + 2 + 4) // Up to the double-wide T bit.
return false; // Smaller than absolute smallest possible RC6 message. return false; // Smaller than absolute smallest possible RC6 message.
if (strict) { // Compliance if (strict) { // Compliance
@@ -462,31 +461,31 @@ bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) {
// Also due to potential melding with the start bit, we can only count // Also due to potential melding with the start bit, we can only count
// the start bit as 1, instead of a more typical 2 value. The header still // the start bit as 1, instead of a more typical 2 value. The header still
// remains as normal. // remains as normal.
if (results->rawlen < nbits + HEADER + 1) if (results->rawlen < nbits + kHeader + 1)
return false; // Don't have enough entries/samples to be valid. return false; // Don't have enough entries/samples to be valid.
switch (nbits) { switch (nbits) {
case RC6_MODE0_BITS: case kRC6Mode0Bits:
case RC6_36_BITS: case kRC6_36Bits:
break; break;
default: default:
return false; // Asking for the wrong number of bits. return false; // Asking for the wrong number of bits.
} }
} }
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], RC6_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t tick = results->rawbuf[offset++] * RAWTICK / RC6_HDR_MARK_TICKS; uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks;
if (!matchSpace(results->rawbuf[offset++], RC6_HDR_SPACE_TICKS * tick)) if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick))
return false; return false;
uint16_t used = 0; uint16_t used = 0;
// Get the start bit. e.g. 1. // Get the start bit. e.g. 1.
if (getRClevel(results, &offset, &used, tick) != kMARK) return false; if (getRClevel(results, &offset, &used, tick) != kMark) return false;
if (getRClevel(results, &offset, &used, tick) != kSPACE) return false; if (getRClevel(results, &offset, &used, tick) != kSpace) return false;
uint16_t actual_bits; uint16_t actual_bits;
uint64_t data = 0; uint64_t data = 0;
@@ -496,17 +495,15 @@ bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) {
int16_t levelA, levelB; // Next two levels int16_t levelA, levelB; // Next two levels
levelA = getRClevel(results, &offset, &used, tick); levelA = getRClevel(results, &offset, &used, tick);
// T bit is double wide; make sure second half matches // T bit is double wide; make sure second half matches
if (actual_bits == 3 && if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick))
levelA != getRClevel(results, &offset, &used, tick))
return false; return false;
levelB = getRClevel(results, &offset, &used, tick); levelB = getRClevel(results, &offset, &used, tick);
// T bit is double wide; make sure second half matches // T bit is double wide; make sure second half matches
if (actual_bits == 3 && if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick))
levelB != getRClevel(results, &offset, &used, tick))
return false; return false;
if (levelA == kMARK && levelB == kSPACE) // reversed compared to RC5 if (levelA == kMark && levelB == kSpace) // reversed compared to RC5
data = (data << 1) | 1; // 1 data = (data << 1) | 1; // 1
else if (levelA == kSPACE && levelB == kMARK) else if (levelA == kSpace && levelB == kMark)
data <<= 1; // 0 data <<= 1; // 0
else else
break; break;
+61 -55
View File
@@ -17,28 +17,28 @@
// Constants // Constants
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/rcmm.php // http://www.sbprojects.com/knowledge/ir/rcmm.php
#define RCMM_TICK 28U // Technically it would be 27.777* const uint16_t kRcmmTick = 28; // Technically it would be 27.777*
#define RCMM_HDR_MARK_TICKS 15U const uint16_t kRcmmHdrMarkTicks = 15;
#define RCMM_HDR_MARK 416U const uint16_t kRcmmHdrMark = 416;
#define RCMM_HDR_SPACE_TICKS 10U const uint16_t kRcmmHdrSpaceTicks = 10;
#define RCMM_HDR_SPACE 277U const uint16_t kRcmmHdrSpace = 277;
#define RCMM_BIT_MARK_TICKS 6U const uint16_t kRcmmBitMarkTicks = 6;
#define RCMM_BIT_MARK 166U const uint16_t kRcmmBitMark = 166;
#define RCMM_BIT_SPACE_0_TICKS 10U const uint16_t kRcmmBitSpace0Ticks = 10;
#define RCMM_BIT_SPACE_0 277U const uint16_t kRcmmBitSpace0 = 277;
#define RCMM_BIT_SPACE_1_TICKS 16U const uint16_t kRcmmBitSpace1Ticks = 16;
#define RCMM_BIT_SPACE_1 444U const uint16_t kRcmmBitSpace1 = 444;
#define RCMM_BIT_SPACE_2_TICKS 22U const uint16_t kRcmmBitSpace2Ticks = 22;
#define RCMM_BIT_SPACE_2 611U const uint16_t kRcmmBitSpace2 = 611;
#define RCMM_BIT_SPACE_3_TICKS 28U const uint16_t kRcmmBitSpace3Ticks = 28;
#define RCMM_BIT_SPACE_3 777U const uint16_t kRcmmBitSpace3 = 777;
#define RCMM_RPT_LENGTH_TICKS 992U const uint16_t kRcmmRptLengthTicks = 992;
#define RCMM_RPT_LENGTH 27778U const uint32_t kRcmmRptLength = 27778;
#define RCMM_MIN_GAP_TICKS 120U const uint16_t kRcmmMinGapTicks = 120;
#define RCMM_MIN_GAP 3360U const uint32_t kRcmmMinGap = 3360;
// Use a tolerance of +/-10% when matching some data spaces. // Use a tolerance of +/-10% when matching some data spaces.
#define RCMM_TOLERANCE 10U const uint8_t kRcmmTolerance = 10;
#define RCMM_EXCESS 50U const uint16_t kRcmmExcess = 50;
#if SEND_RCMM #if SEND_RCMM
// Send a Philips RC-MM packet. // Send a Philips RC-MM packet.
@@ -60,27 +60,35 @@ void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) {
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
usecs.reset(); usecs.reset();
// Header // Header
mark(RCMM_HDR_MARK); mark(kRcmmHdrMark);
space(RCMM_HDR_SPACE); space(kRcmmHdrSpace);
// Data // Data
uint64_t mask = 0b11ULL << (nbits - 2); uint64_t mask = 0b11ULL << (nbits - 2);
// RC-MM sends data 2 bits at a time. // RC-MM sends data 2 bits at a time.
for (int32_t i = nbits; i > 0; i -= 2) { for (int32_t i = nbits; i > 0; i -= 2) {
mark(RCMM_BIT_MARK); mark(kRcmmBitMark);
// Grab the next Most Significant Bits to send. // Grab the next Most Significant Bits to send.
switch ((data & mask) >> (i - 2)) { switch ((data & mask) >> (i - 2)) {
case 0b00: space(RCMM_BIT_SPACE_0); break; case 0b00:
case 0b01: space(RCMM_BIT_SPACE_1); break; space(kRcmmBitSpace0);
case 0b10: space(RCMM_BIT_SPACE_2); break; break;
case 0b11: space(RCMM_BIT_SPACE_3); break; case 0b01:
space(kRcmmBitSpace1);
break;
case 0b10:
space(kRcmmBitSpace2);
break;
case 0b11:
space(kRcmmBitSpace3);
break;
} }
mask >>= 2; mask >>= 2;
} }
// Footer // Footer
mark(RCMM_BIT_MARK); mark(kRcmmBitMark);
// Protocol requires us to wait at least RCMM_RPT_LENGTH usecs from the // Protocol requires us to wait at least kRcmmRptLength usecs from the
// start or RCMM_MIN_GAP usecs. // start or kRcmmMinGap usecs.
space(std::max(RCMM_RPT_LENGTH - usecs.elapsed(), RCMM_MIN_GAP)); space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap));
} }
} }
#endif #endif
@@ -90,7 +98,7 @@ void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) {
// Places successful decode information in the results pointer. // Places successful decode information in the results pointer.
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // 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 RCMM_BITS. // nbits: Nr. of bits to expect in the data portion. Typically kRCMMBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -101,67 +109,65 @@ void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) {
// http://www.sbprojects.com/knowledge/ir/rcmm.php // http://www.sbprojects.com/knowledge/ir/rcmm.php
bool IRrecv::decodeRCMM(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeRCMM(decode_results *results, uint16_t nbits, bool strict) {
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
if (results->rawlen <= 4) if (results->rawlen <= 4)
return false; // Not enough entries to ever be RCMM. return false; // Not enough entries to ever be RCMM.
// Calc the maximum size in bits, the message can be, or that we can accept. // Calc the maximum size in bits, the message can be, or that we can accept.
int16_t maxBitSize = std::min((uint16_t) results->rawlen - 5, int16_t maxBitSize =
(uint16_t) sizeof(data) * 8); std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8);
// Compliance // Compliance
if (strict) { if (strict) {
// Technically the spec says bit sizes should be 12 xor 24. however // Technically the spec says bit sizes should be 12 xor 24. however
// 32 bits has been seen from a device. We are going to assume // 32 bits has been seen from a device. We are going to assume
// 12 <= bits <= 32 is the 'required' bit length for the spec. // 12 <= bits <= 32 is the 'required' bit length for the spec.
if (maxBitSize < 12 || maxBitSize > 32) if (maxBitSize < 12 || maxBitSize > 32) return false;
return false;
if (maxBitSize < nbits) if (maxBitSize < nbits)
return false; // Short cut, we can never reach the expected nr. of bits. return false; // Short cut, we can never reach the expected nr. of bits.
} }
// Header decode // Header decode
if (!matchMark(results->rawbuf[offset], RCMM_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / RCMM_HDR_MARK_TICKS; uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], RCMM_HDR_SPACE)) return false; if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / RCMM_HDR_SPACE_TICKS; uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks;
// Data decode // Data decode
// RC-MM has two bits of data per mark/space pair. // RC-MM has two bits of data per mark/space pair.
uint16_t actualBits; uint16_t actualBits;
for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) {
if (!match(results->rawbuf[offset++], RCMM_BIT_MARK_TICKS * m_tick)) if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick))
return false; return false;
data <<= 2; data <<= 2;
// Use non-default tolerance & excess for matching some of the spaces as the // Use non-default tolerance & excess for matching some of the spaces as the
// defaults are too generous and causes mis-matches in some cases. // defaults are too generous and causes mis-matches in some cases.
if (match(results->rawbuf[offset], RCMM_BIT_SPACE_0_TICKS * s_tick, if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick,
TOLERANCE)) kTolerance))
data += 0; data += 0;
else if (match(results->rawbuf[offset], RCMM_BIT_SPACE_1_TICKS * s_tick, else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick,
TOLERANCE)) kTolerance))
data += 1; data += 1;
else if (match(results->rawbuf[offset], RCMM_BIT_SPACE_2_TICKS * s_tick, else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick,
RCMM_TOLERANCE)) kRcmmTolerance))
data += 2; data += 2;
else if (match(results->rawbuf[offset], RCMM_BIT_SPACE_3_TICKS * s_tick, else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick,
RCMM_TOLERANCE)) kRcmmTolerance))
data += 3; data += 3;
else else
return false; return false;
} }
// Footer decode // Footer decode
if (!match(results->rawbuf[offset++], RCMM_BIT_MARK_TICKS * m_tick)) if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], RCMM_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick))
return false; return false;
// Compliance // Compliance
if (strict && actualBits != nbits) if (strict && actualBits != nbits) return false;
return false;
// Success // Success
results->value = data; results->value = data;
+491 -52
View File
@@ -1,7 +1,11 @@
// Copyright 2009 Ken Shirriff // Copyright 2009 Ken Shirriff
// Copyright 2017 David Conran // Copyright 2017 David Conran
#include "ir_Samsung.h"
#include <algorithm> #include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h" #include "IRrecv.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRutils.h" #include "IRutils.h"
@@ -17,29 +21,38 @@
// Constants // Constants
// Ref: // Ref:
// http://elektrolab.wz.cz/katalog/samsung_protocol.pdf // http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
#define SAMSUNG_TICK 560U const uint16_t kSamsungTick = 560;
#define SAMSUNG_HDR_MARK_TICKS 8U const uint16_t kSamsungHdrMarkTicks = 8;
#define SAMSUNG_HDR_MARK (SAMSUNG_HDR_MARK_TICKS * SAMSUNG_TICK) const uint16_t kSamsungHdrMark = kSamsungHdrMarkTicks * kSamsungTick;
#define SAMSUNG_HDR_SPACE_TICKS 8U const uint16_t kSamsungHdrSpaceTicks = 8;
#define SAMSUNG_HDR_SPACE (SAMSUNG_HDR_SPACE_TICKS * SAMSUNG_TICK) const uint16_t kSamsungHdrSpace = kSamsungHdrSpaceTicks * kSamsungTick;
#define SAMSUNG_BIT_MARK_TICKS 1U const uint16_t kSamsungBitMarkTicks = 1;
#define SAMSUNG_BIT_MARK (SAMSUNG_BIT_MARK_TICKS * SAMSUNG_TICK) const uint16_t kSamsungBitMark = kSamsungBitMarkTicks * kSamsungTick;
#define SAMSUNG_ONE_SPACE_TICKS 3U const uint16_t kSamsungOneSpaceTicks = 3;
#define SAMSUNG_ONE_SPACE (SAMSUNG_ONE_SPACE_TICKS * SAMSUNG_TICK) const uint16_t kSamsungOneSpace = kSamsungOneSpaceTicks * kSamsungTick;
#define SAMSUNG_ZERO_SPACE_TICKS 1U const uint16_t kSamsungZeroSpaceTicks = 1;
#define SAMSUNG_ZERO_SPACE (SAMSUNG_ZERO_SPACE_TICKS * SAMSUNG_TICK) const uint16_t kSamsungZeroSpace = kSamsungZeroSpaceTicks * kSamsungTick;
#define SAMSUNG_RPT_SPACE_TICKS 4U const uint16_t kSamsungRptSpaceTicks = 4;
#define SAMSUNG_RPT_SPACE (SAMSUNG_RPT_SPACE_TICKS * SAMSUNG_TICK) const uint16_t kSamsungRptSpace = kSamsungRptSpaceTicks * kSamsungTick;
#define SAMSUNG_MIN_MESSAGE_LENGTH_TICKS 193U const uint16_t kSamsungMinMessageLengthTicks = 193;
#define SAMSUNG_MIN_MESSAGE_LENGTH (SAMSUNG_MIN_MESSAGE_LENGTH_TICKS * \ const uint32_t kSamsungMinMessageLength =
SAMSUNG_TICK) kSamsungMinMessageLengthTicks * kSamsungTick;
#define SAMSUNG_MIN_GAP_TICKS (SAMSUNG_MIN_MESSAGE_LENGTH_TICKS - \ const uint16_t kSamsungMinGapTicks =
(SAMSUNG_HDR_MARK_TICKS + SAMSUNG_HDR_SPACE_TICKS + \ kSamsungMinMessageLengthTicks -
SAMSUNG_BITS * (SAMSUNG_BIT_MARK_TICKS + SAMSUNG_ONE_SPACE_TICKS) + \ (kSamsungHdrMarkTicks + kSamsungHdrSpaceTicks +
SAMSUNG_BIT_MARK_TICKS)) kSamsungBits * (kSamsungBitMarkTicks + kSamsungOneSpaceTicks) +
#define SAMSUNG_MIN_GAP (SAMSUNG_MIN_GAP_TICKS * SAMSUNG_TICK) kSamsungBitMarkTicks);
const uint32_t kSamsungMinGap = kSamsungMinGapTicks * kSamsungTick;
const uint16_t kSamsungAcHdrMark = 690;
const uint16_t kSamsungAcHdrSpace = 17844;
const uint8_t kSamsungAcSections = 2;
const uint16_t kSamsungAcSectionMark = 3086;
const uint16_t kSamsungAcSectionSpace = 8864;
const uint16_t kSamsungAcSectionGap = 2886;
const uint16_t kSamsungAcBitMark = 586;
const uint16_t kSamsungAcOneSpace = 1432;
const uint16_t kSamsungAcZeroSpace = 436;
#if SEND_SAMSUNG #if SEND_SAMSUNG
// Send a Samsung formatted message. // Send a Samsung formatted message.
@@ -49,19 +62,17 @@
// //
// Args: // Args:
// data: The message to be sent. // data: The message to be sent.
// nbits: The bit size of the message being sent. typically SAMSUNG_BITS. // nbits: The bit size of the message being sent. typically kSamsungBits.
// repeat: The number of times the message is to be repeated. // repeat: The number of times the message is to be repeated.
// //
// Status: BETA / Should be working. // Status: BETA / Should be working.
// //
// Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf // Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(SAMSUNG_HDR_MARK, SAMSUNG_HDR_SPACE, sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark,
SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace,
SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data,
SAMSUNG_BIT_MARK, nbits, 38, true, repeat, 33);
SAMSUNG_MIN_GAP, SAMSUNG_MIN_MESSAGE_LENGTH,
data, nbits, 38, true, repeat, 33);
} }
// Construct a raw Samsung message from the supplied customer(address) & // Construct a raw Samsung message from the supplied customer(address) &
@@ -77,8 +88,8 @@ void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) {
customer = reverseBits(customer, sizeof(customer) * 8); customer = reverseBits(customer, sizeof(customer) * 8);
command = reverseBits(command, sizeof(command) * 8); command = reverseBits(command, sizeof(command) * 8);
return((command ^ 0xFF) | (command << 8) | return ((command ^ 0xFF) | (command << 8) | (customer << 16) |
(customer << 16) | (customer << 24)); (customer << 24));
} }
#endif #endif
@@ -90,7 +101,7 @@ uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) {
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // 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 SAMSUNG_BITS. // nbits: Nr. of bits to expect in the data portion. Typically kSamsungBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -104,37 +115,35 @@ uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) {
// http://elektrolab.wz.cz/katalog/samsung_protocol.pdf // http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Samsung message. return false; // Can't possibly be a valid Samsung message.
if (strict && nbits != SAMSUNG_BITS) if (strict && nbits != kSamsungBits)
return false; // We expect Samsung to be 32 bits of message. return false; // We expect Samsung to be 32 bits of message.
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
if (!matchMark(results->rawbuf[offset], SAMSUNG_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks;
SAMSUNG_HDR_MARK_TICKS; if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset], SAMSUNG_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick =
SAMSUNG_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks;
// Data // Data
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
SAMSUNG_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits,
SAMSUNG_ONE_SPACE_TICKS * s_tick, kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick,
SAMSUNG_BIT_MARK_TICKS * m_tick, kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick);
SAMSUNG_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], SAMSUNG_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], SAMSUNG_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick))
return false; return false;
// Compliance // Compliance
@@ -142,13 +151,11 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits,
// According to the spec, the customer (address) code is the first 8 // According to the spec, the customer (address) code is the first 8
// transmitted bits. It's then repeated. Check for that. // transmitted bits. It's then repeated. Check for that.
uint8_t address = data >> 24; uint8_t address = data >> 24;
if (strict && address != ((data >> 16) & 0xFF)) if (strict && address != ((data >> 16) & 0xFF)) return false;
return false;
// Spec says the command code is the 3rd block of transmitted 8-bits, // Spec says the command code is the 3rd block of transmitted 8-bits,
// followed by the inverted command code. // followed by the inverted command code.
uint8_t command = (data & 0xFF00) >> 8; uint8_t command = (data & 0xFF00) >> 8;
if (strict && command != ((data & 0xFF) ^ 0xFF)) if (strict && command != ((data & 0xFF) ^ 0xFF)) return false;
return false;
// Success // Success
results->bits = nbits; results->bits = nbits;
@@ -160,3 +167,435 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits,
return true; return true;
} }
#endif #endif
#if SEND_SAMSUNG_AC
// Send a Samsung A/C message.
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=kSamsungAcStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: ALPHA / Untested.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/505
void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) {
if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength)
return; // Not an appropriate number of bytes to send a proper message.
enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) {
// Header
mark(kSamsungAcHdrMark);
space(kSamsungAcHdrSpace);
// Send in 7 byte sections.
for (uint16_t offset = 0; offset < nbytes;
offset += kSamsungACSectionLength) {
sendGeneric(kSamsungAcSectionMark, kSamsungAcSectionSpace,
kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark,
kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap,
data + offset, kSamsungACSectionLength, // 7 bytes == 56 bits
38000, false, 0, 50); // Send in LSBF order
}
// Complete made up guess at inter-message gap.
space(100000 - kSamsungAcSectionGap);
}
}
#endif // SEND_SAMSUNG_AC
IRSamsungAc::IRSamsungAc(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRSamsungAc::stateReset() {
for (uint8_t i = 0; i < kSamsungAcExtendedStateLength; i++)
remote_state[i] = 0x0;
remote_state[0] = 0x02;
remote_state[1] = 0x92;
remote_state[2] = 0x0F;
remote_state[6] = 0xF0;
remote_state[7] = 0x01;
remote_state[8] = 0x02;
remote_state[9] = 0xAE;
remote_state[10] = 0x71;
remote_state[12] = 0x15;
remote_state[13] = 0xF0;
}
void IRSamsungAc::begin() { _irsend.begin(); }
uint8_t IRSamsungAc::calcChecksum(const uint8_t state[],
const uint16_t length) {
uint8_t sum = 0;
// Safety check so we don't go outside the array.
if (length < 7) return 255;
// Shamelessly inspired by:
// https://github.com/adafruit/Raw-IR-decoder-for-Arduino/pull/3/files
// Count most of the '1' bits after the checksum location.
sum += countBits(state[length - 7], 8);
sum -= countBits(state[length - 6] & 0xF, 8);
sum += countBits(state[length - 5] & 0b11111110, 8);
sum += countBits(state + length - 4, 3);
return (28 - sum) & 0xF;
}
bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) {
if (length < kSamsungAcStateLength)
return true; // No checksum to compare with. Assume okay.
uint8_t offset = 0;
if (length >= kSamsungAcExtendedStateLength) offset = 7;
return ((state[length - 6] >> 4) == calcChecksum(state, length) &&
(state[length - (13 + offset)] >> 4) == calcChecksum(state, length -
(7 + offset)));
}
// Update the checksum for the internal state.
void IRSamsungAc::checksum(uint16_t length) {
if (length < 13) return;
remote_state[length - 6] &= 0x0F;
remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4);
remote_state[length - 13] &= 0x0F;
remote_state[length - 13] |= (calcChecksum(remote_state, length - 7) << 4);
}
#if SEND_SAMSUNG_AC
// Use for most function/mode/settings changes to the unit.
// i.e. When the device is already running.
void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) {
if (calcchecksum) checksum();
_irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat);
}
#endif // SEND_SAMSUNG_AC
#if SEND_SAMSUNG_AC
// Use this for when you need to power on/off the device.
// Samsung A/C requires an extended length message when you want to
// change the power operating mode of the A/C unit.
void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) {
if (calcchecksum) checksum();
uint8_t extended_state[kSamsungAcExtendedStateLength] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Copy/convert the internal state to an extended state.
for (uint16_t i = 0; i < kSamsungACSectionLength; i++)
extended_state[i] = remote_state[i];
for (uint16_t i = kSamsungACSectionLength; i < kSamsungAcStateLength; i++)
extended_state[i + kSamsungACSectionLength] = remote_state[i];
// extended_state[8] seems special. This is a guess on how to calculate it.
extended_state[8] = (extended_state[1] & 0x9F) | 0x40;
// Send it.
_irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat);
}
#endif // SEND_SAMSUNG_AC
uint8_t *IRSamsungAc::getRaw() {
checksum();
return remote_state;
}
void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) {
for (uint8_t i = 0; i < length && i < kSamsungAcExtendedStateLength; i++) {
remote_state[i] = new_code[i];
}
// Shrink the extended state into a normal state.
if (length > kSamsungAcStateLength) {
for (uint8_t i = kSamsungAcStateLength; i < length; i++)
remote_state[i - kSamsungACSectionLength] = remote_state[i];
}
}
void IRSamsungAc::on() {
remote_state[1] &= ~kSamsungAcPowerMask1;
remote_state[6] |= kSamsungAcPowerMask2;
}
void IRSamsungAc::off() {
remote_state[1] |= kSamsungAcPowerMask1;
remote_state[6] &= ~kSamsungAcPowerMask2;
}
void IRSamsungAc::setPower(const bool state) {
if (state)
on();
else
off();
}
bool IRSamsungAc::getPower() {
return ((remote_state[6] & kSamsungAcPowerMask2) != 0) &&
((remote_state[1] & kSamsungAcPowerMask1) == 0);
}
// Set the temp. in deg C
void IRSamsungAc::setTemp(const uint8_t temp) {
uint8_t newtemp = std::max(kSamsungAcMinTemp, temp);
newtemp = std::min(kSamsungAcMaxTemp, newtemp);
remote_state[11] = (remote_state[11] & ~kSamsungAcTempMask) |
((newtemp - kSamsungAcMinTemp) << 4);
}
// Return the set temp. in deg C
uint8_t IRSamsungAc::getTemp() {
return ((remote_state[11] & kSamsungAcTempMask) >> 4) + kSamsungAcMinTemp;
}
void IRSamsungAc::setMode(const uint8_t mode) {
// If we get an unexpected mode, default to AUTO.
uint8_t newmode = mode;
if (newmode > kSamsungAcHeat) newmode = kSamsungAcAuto;
remote_state[12] = (remote_state[12] & ~kSamsungAcModeMask) | (newmode << 4);
// Auto mode has a special fan setting valid only in auto mode.
if (newmode == kSamsungAcAuto) {
setFan(kSamsungAcFanAuto2);
} else {
if (getFan() == kSamsungAcFanAuto2) // Non-Auto can't have this fan setting
setFan(kSamsungAcFanAuto); // Default to something safe.
}
}
uint8_t IRSamsungAc::getMode() {
return (remote_state[12] & kSamsungAcModeMask) >> 4;
}
void IRSamsungAc::setFan(const uint8_t speed) {
switch (speed) {
case kSamsungAcFanAuto:
case kSamsungAcFanLow:
case kSamsungAcFanMed:
case kSamsungAcFanHigh:
case kSamsungAcFanTurbo:
if (getMode() == kSamsungAcAuto) return; // Not valid in Auto mode.
break;
case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode.
if (getMode() != kSamsungAcAuto) return;
break;
default:
return;
}
remote_state[12] = (remote_state[12] & ~kSamsungAcFanMask) | (speed << 1);
}
uint8_t IRSamsungAc::getFan() {
return ((remote_state[12] & kSamsungAcFanMask) >> 1);
}
bool IRSamsungAc::getSwing() {
// TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1.
// e.g. 0xAE or 0XAF for swing move.
return ((remote_state[9] & kSamsungAcSwingMask) >> 4) == kSamsungAcSwingMove;
}
void IRSamsungAc::setSwing(const bool state) {
// TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1.
// e.g. 0xAE or 0XAF for swing move.
remote_state[9] &= ~kSamsungAcSwingMask; // Clear the previous swing state.
if (state)
remote_state[9] |= (kSamsungAcSwingMove << 4);
else
remote_state[9] |= (kSamsungAcSwingStop << 4);
}
bool IRSamsungAc::getBeep() { return remote_state[13] & kSamsungAcBeepMask; }
void IRSamsungAc::setBeep(const bool state) {
if (state)
remote_state[13] |= kSamsungAcBeepMask;
else
remote_state[13] &= ~kSamsungAcBeepMask;
}
bool IRSamsungAc::getClean() {
return (remote_state[10] & kSamsungAcCleanMask10) &&
(remote_state[11] & kSamsungAcCleanMask11);
}
void IRSamsungAc::setClean(const bool state) {
if (state) {
remote_state[10] |= kSamsungAcCleanMask10;
remote_state[11] |= kSamsungAcCleanMask11;
} else {
remote_state[10] &= ~kSamsungAcCleanMask10;
remote_state[11] &= ~kSamsungAcCleanMask11;
}
}
// Very unsure this is correct.
bool IRSamsungAc::getQuiet() {
return remote_state[11] & kSamsungAcQuietMask11;
}
// Very unsure this is correct.
void IRSamsungAc::setQuiet(const bool state) {
if (state) {
remote_state[11] |= kSamsungAcQuietMask11;
setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto.
} else {
remote_state[11] &= ~kSamsungAcQuietMask11;
}
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRSamsungAc::toString() {
String result = "";
#else
std::string IRSamsungAc::toString() {
std::string result = "";
#endif // ARDUINO
result += "Power: ";
if (getPower())
result += "On";
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kSamsungAcAuto:
result += " (AUTO)";
break;
case kSamsungAcCool:
result += " (COOL)";
break;
case kSamsungAcHeat:
result += " (HEAT)";
break;
case kSamsungAcDry:
result += " (DRY)";
break;
case kSamsungAcFan:
result += " (FAN)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kSamsungAcFanAuto:
case kSamsungAcFanAuto2:
result += " (AUTO)";
break;
case kSamsungAcFanLow:
result += " (LOW)";
break;
case kSamsungAcFanMed:
result += " (MED)";
break;
case kSamsungAcFanHigh:
result += " (HIGH)";
break;
case kSamsungAcFanTurbo:
result += " (TURBO)";
break;
default:
result += " (UNKNOWN)";
break;
}
result += ", Swing: ";
if (getSwing())
result += "On";
else
result += "Off";
result += ", Beep: ";
if (getBeep())
result += "On";
else
result += "Off";
result += ", Clean: ";
if (getBeep())
result += "On";
else
result += "Off";
result += ", Quiet: ";
if (getQuiet())
result += "On";
else
result += "Off";
return result;
}
#if DECODE_SAMSUNG_AC
// Decode the supplied Samsung A/C 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 kSamsungAcBits
// 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/505
bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1)
return false; // Can't possibly be a valid Samsung A/C message.
if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false;
uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;
// Message Header
if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false;
// Section(s)
for (uint16_t pos = kSamsungACSectionLength, i = 0; pos <= nbits / 8;
pos += kSamsungACSectionLength) {
uint64_t sectiondata = 0;
// Section Header
if (!matchMark(results->rawbuf[offset++], kSamsungAcSectionMark))
return false;
if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionSpace))
return false;
// Section Data
// Keep reading bytes until we either run out of section or state to fill.
for (; offset <= results->rawlen - 16 && i < pos;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8, kSamsungAcBitMark,
kSamsungAcOneSpace, kSamsungAcBitMark,
kSamsungAcZeroSpace, 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;
sectiondata = (sectiondata << 8) + data_result.data;
}
DPRINTLN("DEBUG: sectiondata = 0x" + uint64ToString(sectiondata, 16));
// Section Footer
if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false;
if (pos < nbits / 8) { // Inter-section gap.
if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionGap))
return false;
} else { // Last section / End of message gap.
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kSamsungAcSectionGap))
return false;
}
}
// Compliance
// Re-check we got the correct size/length due to the way we read the data.
if (dataBitsSoFar != nbits) return false;
// Is the signature correct?
DPRINTLN("DEBUG: Checking signature.");
if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false;
if (strict) {
// Is the checksum valid?
if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) {
DPRINTLN("DEBUG: Checksum failed!");
return false;
}
}
// Success
results->decode_type = SAMSUNG_AC;
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_SAMSUNG_AC
+109
View File
@@ -0,0 +1,109 @@
// Samsung A/C
//
// Copyright 2018 David Conran
#ifndef IR_SAMSUNG_H_
#define IR_SAMSUNG_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// SSSS AAA MMM SSSS U U N N GGGG
// S A A M M M S U U NN N G
// SSS AAAAA M M M SSS U U N N N G GG
// S A A M M S U U N NN G G
// SSSS A A M M SSSS UUU N N GGG
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/505
// Constants
const uint8_t kSamsungAcAuto = 0;
const uint8_t kSamsungAcCool = 1;
const uint8_t kSamsungAcDry = 2;
const uint8_t kSamsungAcFan = 3;
const uint8_t kSamsungAcHeat = 4;
const uint8_t kSamsungAcModeMask = 0x70;
const uint8_t kSamsungAcFanAuto = 0;
const uint8_t kSamsungAcFanLow = 2;
const uint8_t kSamsungAcFanMed = 4;
const uint8_t kSamsungAcFanHigh = 5;
const uint8_t kSamsungAcFanAuto2 = 6;
const uint8_t kSamsungAcFanTurbo = 7;
const uint8_t kSamsungAcMinTemp = 16; // 16C
const uint8_t kSamsungAcMaxTemp = 30; // 30C
const uint8_t kSamsungAcAutoTemp = 25; // 25C
const uint8_t kSamsungAcTempMask = 0xF0;
const uint8_t kSamsungAcPowerMask1 = 0x20;
const uint8_t kSamsungAcPowerMask2 = 0x30;
const uint8_t kSamsungAcFanMask = 0x0E;
const uint8_t kSamsungAcSwingMask = 0x70;
const uint8_t kSamsungAcSwingMove = 0b010;
const uint8_t kSamsungAcSwingStop = 0b111;
const uint8_t kSamsungAcBeepMask = 0x02;
const uint8_t kSamsungAcCleanMask10 = 0x80;
const uint8_t kSamsungAcCleanMask11 = 0x02;
const uint8_t kSamsungAcQuietMask11 = 0x01;
const uint16_t kSamsungACSectionLength = 7;
const uint64_t kSamsungAcPowerSection = 0x1D20F00000000;
// Classes
class IRSamsungAc {
public:
explicit IRSamsungAc(uint16_t pin);
void stateReset();
#if SEND_SAMSUNG_AC
void send(const uint16_t repeat = kSamsungAcDefaultRepeat,
const bool calcchecksum = true);
void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat,
const bool calcchecksum = true);
#endif // SEND_SAMSUNG_AC
void begin();
void on();
void off();
void setPower(const bool state);
bool getPower();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t speed);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
void setSwing(const bool state);
bool getSwing();
void setBeep(const bool state);
bool getBeep();
void setClean(const bool state);
bool getClean();
void setQuiet(const bool state);
bool getQuiet();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[],
const uint16_t length = kSamsungAcStateLength);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kSamsungAcStateLength);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kSamsungAcStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
private:
// The state of the IR remote in IR code form.
uint8_t remote_state[kSamsungAcExtendedStateLength];
void checksum(const uint16_t length = kSamsungAcStateLength);
IRsend _irsend;
};
#endif // IR_SAMSUNG_H_
+51 -48
View File
@@ -20,31 +20,35 @@
// Sanyo SA 8650B // Sanyo SA 8650B
// Ref: // Ref:
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp
#define SANYO_SA8650B_HDR_MARK 3500U // seen range 3500
#define SANYO_SA8650B_HDR_SPACE 950U // seen 950 const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500
#define SANYO_SA8650B_ONE_MARK 2400U // seen 2400 const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950
#define SANYO_SA8650B_ZERO_MARK 700U // seen 700 const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400
const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700
// usually see 713 - not using ticks as get number wrapround // usually see 713 - not using ticks as get number wrapround
#define SANYO_SA8650B_DOUBLE_SPACE_USECS 800U const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800;
#define SANYO_SA8650B_RPT_LENGTH 45000U const uint16_t kSanyoSa8650bRptLength = 45000;
// Sanyo LC7461 // Sanyo LC7461
// Ref: // Ref:
// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp // https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp
// http://slydiman.narod.ru/scr/kb/sanyo.htm // http://slydiman.narod.ru/scr/kb/sanyo.htm
// http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf // http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf
#define SANYO_LC7461_ADDRESS_MASK ((1 << SANYO_LC7461_ADDRESS_BITS) - 1)
#define SANYO_LC7461_COMMAND_MASK ((1 << SANYO_LC7461_COMMAND_BITS) - 1) const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1;
#define SANYO_LC7461_HDR_MARK 9000U const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1;
#define SANYO_LC7461_HDR_SPACE 4500U const uint16_t kSanyoLc7461HdrMark = 9000;
#define SANYO_LC7461_BIT_MARK 560U // 1T const uint16_t kSanyoLc7461HdrSpace = 4500;
#define SANYO_LC7461_ONE_SPACE 1690U // 3T const uint16_t kSanyoLc7461BitMark = 560; // 1T
#define SANYO_LC7461_ZERO_SPACE 560U // 1T const uint16_t kSanyoLc7461OneSpace = 1690; // 3T
#define SANYO_LC7461_MIN_COMMAND_LENGTH 108000UL const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T
#define SANYO_LC7461_MIN_GAP SANYO_LC7461_MIN_COMMAND_LENGTH - \ const uint32_t kSanyoLc7461MinCommandLength = 108000;
(SANYO_LC7461_HDR_MARK + SANYO_LC7461_HDR_SPACE + SANYO_LC7461_BITS * \
(SANYO_LC7461_BIT_MARK + (SANYO_LC7461_ONE_SPACE + \ const uint16_t kSanyoLc7461MinGap =
SANYO_LC7461_ZERO_SPACE) / 2) \ kSanyoLc7461MinCommandLength -
+ SANYO_LC7461_BIT_MARK) (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace +
kSanyoLC7461Bits * (kSanyoLc7461BitMark +
(kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) +
kSanyoLc7461BitMark);
#if SEND_SANYO #if SEND_SANYO
// Construct a Sanyo LC7461 message. // Construct a Sanyo LC7461 message.
@@ -61,18 +65,18 @@
// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon // According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon
uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) {
// Mask our input values to ensure the correct bit sizes. // Mask our input values to ensure the correct bit sizes.
address &= SANYO_LC7461_ADDRESS_MASK; address &= kSanyoLc7461AddressMask;
command &= SANYO_LC7461_COMMAND_MASK; command &= kSanyoLc7461CommandMask;
uint64_t data = address; uint64_t data = address;
address ^= SANYO_LC7461_ADDRESS_MASK; // Invert the 13 LSBs. address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs.
// Append the now inverted address. // Append the now inverted address.
data = (data << SANYO_LC7461_ADDRESS_BITS) | address; data = (data << kSanyoLC7461AddressBits) | address;
// Append the command. // Append the command.
data = (data << SANYO_LC7461_COMMAND_BITS) | command; data = (data << kSanyoLC7461CommandBits) | command;
command ^= SANYO_LC7461_COMMAND_MASK; // Invert the command. command ^= kSanyoLc7461CommandMask; // Invert the command.
// Append the now inverted command. // Append the now inverted command.
data = (data << SANYO_LC7461_COMMAND_BITS) | command; data = (data << kSanyoLC7461CommandBits) | command;
return data; return data;
} }
@@ -127,31 +131,30 @@ void IRsend::sendSanyoLC7461(uint64_t data, uint16_t nbits, uint16_t repeat) {
// http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf // http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf
bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t nbits, bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (strict && nbits != SANYO_LC7461_BITS) if (strict && nbits != kSanyoLC7461Bits)
return false; // Not strictly in spec. return false; // Not strictly in spec.
// This protocol is basically a 42-bit variant of the NEC protocol. // This protocol is basically a 42-bit variant of the NEC protocol.
if (!decodeNEC(results, nbits, false)) if (!decodeNEC(results, nbits, false))
return false; // Didn't match a NEC format (without strict) return false; // Didn't match a NEC format (without strict)
// Bits 30 to 42+. // Bits 30 to 42+.
uint16_t address = results->value >> (SANYO_LC7461_BITS - uint16_t address =
SANYO_LC7461_ADDRESS_BITS); results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits);
// Bits 9 to 16. // Bits 9 to 16.
uint8_t command = (results->value >> SANYO_LC7461_COMMAND_BITS) & uint8_t command =
SANYO_LC7461_COMMAND_MASK; (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask;
// Compliance // Compliance
if (strict) { if (strict) {
if (results->bits != nbits) if (results->bits != nbits) return false;
return false;
// Bits 17 to 29. // Bits 17 to 29.
uint16_t inverted_address = uint16_t inverted_address =
(results->value >> (SANYO_LC7461_COMMAND_BITS * 2)) & (results->value >> (kSanyoLC7461CommandBits * 2)) &
SANYO_LC7461_ADDRESS_MASK; kSanyoLc7461AddressMask;
// Bits 1-8. // Bits 1-8.
uint8_t inverted_command = results->value & SANYO_LC7461_COMMAND_MASK; uint8_t inverted_command = results->value & kSanyoLc7461CommandMask;
if ((address ^ SANYO_LC7461_ADDRESS_MASK) != inverted_address) if ((address ^ kSanyoLc7461AddressMask) != inverted_address)
return false; // Address integrity check failed. return false; // Address integrity check failed.
if ((command ^ SANYO_LC7461_COMMAND_MASK) != inverted_command) if ((command ^ kSanyoLc7461CommandMask) != inverted_command)
return false; // Command integrity check failed. return false; // Command integrity check failed.
} }
@@ -182,9 +185,9 @@ bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t nbits,
// Ref: // Ref:
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp
bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * nbits + HEADER - 1) if (results->rawlen < 2 * nbits + kHeader - 1)
return false; // Shorter than shortest possible. return false; // Shorter than shortest possible.
if (strict && nbits != SANYO_SA8650B_BITS) if (strict && nbits != kSanyoSA8650BBits)
return false; // Doesn't match the spec. return false; // Doesn't match the spec.
uint16_t offset = 0; uint16_t offset = 0;
@@ -192,9 +195,9 @@ bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) {
// TODO(crankyoldgit): This repeat code looks like garbage, it should never // TODO(crankyoldgit): This repeat code looks like garbage, it should never
// match or if it does, it won't be reliable. We should probably just // match or if it does, it won't be reliable. We should probably just
// remove it. // remove it.
if (results->rawbuf[offset++] < SANYO_SA8650B_DOUBLE_SPACE_USECS) { if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) {
results->bits = 0; results->bits = 0;
results->value = REPEAT; results->value = kRepeat;
results->decode_type = SANYO; results->decode_type = SANYO;
results->address = 0; results->address = 0;
results->command = 0; results->command = 0;
@@ -203,27 +206,27 @@ bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) {
} }
// Header // Header
if (!matchMark(results->rawbuf[offset++], SANYO_SA8650B_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark))
return false; return false;
// NOTE: These next two lines look very wrong. Treat as suspect. // NOTE: These next two lines look very wrong. Treat as suspect.
if (!matchMark(results->rawbuf[offset++], SANYO_SA8650B_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark))
return false; return false;
// Data // Data
uint64_t data = 0; uint64_t data = 0;
while (offset + 1 < results->rawlen) { while (offset + 1 < results->rawlen) {
if (!matchSpace(results->rawbuf[offset], SANYO_SA8650B_HDR_SPACE)) if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace))
break; break;
offset++; offset++;
if (matchMark(results->rawbuf[offset], SANYO_SA8650B_ONE_MARK)) if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark))
data = (data << 1) | 1; // 1 data = (data << 1) | 1; // 1
else if (matchMark(results->rawbuf[offset], SANYO_SA8650B_ZERO_MARK)) else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark))
data <<= 1; // 0 data <<= 1; // 0
else else
return false; return false;
offset++; offset++;
} }
if (strict && SANYO_SA8650B_BITS > (offset - 1U) / 2U) if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U)
return false; return false;
// Success // Success
+50 -58
View File
@@ -22,27 +22,27 @@
// Ref: // Ref:
// GlobalCache's IR Control Tower data. // GlobalCache's IR Control Tower data.
// http://www.sbprojects.com/knowledge/ir/sharp.php // http://www.sbprojects.com/knowledge/ir/sharp.php
#define SHARP_TICK 26U const uint16_t kSharpTick = 26;
#define SHARP_BIT_MARK_TICKS 10U const uint16_t kSharpBitMarkTicks = 10;
#define SHARP_BIT_MARK (SHARP_BIT_MARK_TICKS * SHARP_TICK) const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick;
#define SHARP_ONE_SPACE_TICKS 70U const uint16_t kSharpOneSpaceTicks = 70;
#define SHARP_ONE_SPACE (SHARP_ONE_SPACE_TICKS * SHARP_TICK) const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick;
#define SHARP_ZERO_SPACE_TICKS 30U const uint16_t kSharpZeroSpaceTicks = 30;
#define SHARP_ZERO_SPACE (SHARP_ZERO_SPACE_TICKS * SHARP_TICK) const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick;
#define SHARP_GAP_TICKS 1677U const uint16_t kSharpGapTicks = 1677;
#define SHARP_GAP (SHARP_GAP_TICKS * SHARP_TICK) const uint16_t kSharpGap = kSharpGapTicks * kSharpTick;
// Address(5) + Command(8) + Expansion(1) + Check(1) // Address(5) + Command(8) + Expansion(1) + Check(1)
#define SHARP_TOGGLE_MASK ((1 << (SHARP_BITS - SHARP_ADDRESS_BITS)) - 1) const uint64_t kSharpToggleMask =
#define SHARP_ADDRESS_MASK ((1 << SHARP_ADDRESS_BITS) - 1) ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1;
#define SHARP_COMMAND_MASK ((1 << SHARP_COMMAND_BITS) - 1) const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1;
const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1;
#if (SEND_SHARP || SEND_DENON) #if (SEND_SHARP || SEND_DENON)
// Send a (raw) Sharp message // Send a (raw) Sharp message
// //
// Args: // Args:
// data: Contents of the message to be sent. // data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically SHARP_BITS. // nbits: Nr. of bits of data to be sent. Typically kSharpBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: BETA / Previously working fine. // Status: BETA / Previously working fine.
@@ -67,14 +67,13 @@ void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) {
// normal), however all data points to that being incorrect. // normal), however all data points to that being incorrect.
for (uint8_t n = 0; n < 2; n++) { for (uint8_t n = 0; n < 2; n++) {
sendGeneric(0, 0, // No Header sendGeneric(0, 0, // No Header
SHARP_BIT_MARK, SHARP_ONE_SPACE, kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace,
SHARP_BIT_MARK, SHARP_ZERO_SPACE, kSharpBitMark, kSharpGap, data, nbits, 38, true,
SHARP_BIT_MARK, SHARP_GAP, 0, // Repeats are handled already.
data, nbits, 38, true, 0, // Repeats are handled already.
33); 33);
// Invert the data per protocol. This is always called twice, so it's // Invert the data per protocol. This is always called twice, so it's
// retured to original upon exiting the inner loop. // retured to original upon exiting the inner loop.
data ^= SHARP_TOGGLE_MASK; data ^= kSharpToggleMask;
} }
} }
} }
@@ -107,17 +106,17 @@ uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command,
uint16_t expansion, uint16_t check, uint16_t expansion, uint16_t check,
bool MSBfirst) { bool MSBfirst) {
// Mask any unexpected bits. // Mask any unexpected bits.
address &= ((1 << SHARP_ADDRESS_BITS) - 1); address &= ((1 << kSharpAddressBits) - 1);
command &= ((1 << SHARP_COMMAND_BITS) - 1); command &= ((1 << kSharpCommandBits) - 1);
expansion &= 1; expansion &= 1;
check &= 1; check &= 1;
if (!MSBfirst) { // Correct bit order if needed. if (!MSBfirst) { // Correct bit order if needed.
address = reverseBits(address, SHARP_ADDRESS_BITS); address = reverseBits(address, kSharpAddressBits);
command = reverseBits(command, SHARP_COMMAND_BITS); command = reverseBits(command, kSharpCommandBits);
} }
// Concatinate all the bits. // Concatinate all the bits.
return (address << (SHARP_COMMAND_BITS + 2)) | (command << 2) | return (address << (kSharpCommandBits + 2)) | (command << 2) |
(expansion << 1) | check; (expansion << 1) | check;
} }
@@ -126,7 +125,7 @@ uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command,
// Args: // Args:
// address: Address value to be sent. // address: Address value to be sent.
// command: Command value to be sent. // command: Command value to be sent.
// nbits: Nr. of bits of data to be sent. Typically SHARP_BITS. // nbits: Nr. of bits of data to be sent. Typically kSharpBits.
// repeat: Nr. of additional times the message is to be sent. // repeat: Nr. of additional times the message is to be sent.
// //
// Status: DEPRICATED / Previously working fine. // Status: DEPRICATED / Previously working fine.
@@ -156,7 +155,7 @@ void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits,
// //
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of data bits to expect. Typically SHARP_BITS. // nbits: Nr. of data bits to expect. Typically kSharpBits.
// strict: Flag indicating if we should perform strict matching. // strict: Flag indicating if we should perform strict matching.
// expansion: Should we expect the expansion bit to be set. Default is true. // expansion: Should we expect the expansion bit to be set. Default is true.
// Returns: // Returns:
@@ -175,90 +174,83 @@ void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits,
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp // http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp
bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict, bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict,
bool expansion) { bool expansion) {
if (results->rawlen < 2 * nbits + FOOTER - 1) if (results->rawlen < 2 * nbits + kFooter - 1)
return false; // Not enough entries to be a Sharp message. return false; // Not enough entries to be a Sharp message.
// Compliance // Compliance
if (strict) { if (strict) {
if (nbits != SHARP_BITS) if (nbits != kSharpBits) return false; // Request is out of spec.
return false; // Request is out of spec.
// DISABLED - See TODO // DISABLED - See TODO
#ifdef UNIT_TEST #ifdef UNIT_TEST
// An in spec message has the data sent normally, then inverted. So we // An in spec message has the data sent normally, then inverted. So we
// expect twice as many entries than to just get the results. // expect twice as many entries than to just get the results.
if (results->rawlen < 2 * (2 * nbits + FOOTER)) if (results->rawlen < 2 * (2 * nbits + kFooter)) return false;
return false;
#endif #endif
} }
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// No header // No header
// But try to auto-calibrate off the initial mark signal. // But try to auto-calibrate off the initial mark signal.
if (!matchMark(results->rawbuf[offset], SHARP_BIT_MARK, 35)) return false; if (!matchMark(results->rawbuf[offset], kSharpBitMark, 35)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t tick = results->rawbuf[offset] * RAWTICK / SHARP_BIT_MARK_TICKS; uint32_t tick = results->rawbuf[offset] * kRawTick / kSharpBitMarkTicks;
// Data // Data
for (uint16_t i = 0; i < nbits; i++, offset++) { for (uint16_t i = 0; i < nbits; i++, offset++) {
// Use a higher tolerance value for SHARP_BIT_MARK as it is quite small. // Use a higher tolerance value for kSharpBitMark as it is quite small.
if (!matchMark(results->rawbuf[offset++], SHARP_BIT_MARK_TICKS * tick, 35)) if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35))
return false; return false;
if (matchSpace(results->rawbuf[offset], SHARP_ONE_SPACE_TICKS * tick)) if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick))
data = (data << 1) | 1; // 1 data = (data << 1) | 1; // 1
else if (matchSpace(results->rawbuf[offset], SHARP_ZERO_SPACE_TICKS * tick)) else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick))
data <<= 1; // 0 data <<= 1; // 0
else else
return false; return false;
} }
// Footer // Footer
if (!match(results->rawbuf[offset++], SHARP_BIT_MARK_TICKS * tick)) if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], SHARP_GAP_TICKS * tick)) !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick))
return false; return false;
// Compliance // Compliance
if (strict) { if (strict) {
// Check the state of the expansion bit is what we expect. // Check the state of the expansion bit is what we expect.
if ((data & 0b10) >> 1 != expansion) if ((data & 0b10) >> 1 != expansion) return false;
return false;
// The check bit should be cleared in a normal message. // The check bit should be cleared in a normal message.
if (data & 0b1) if (data & 0b1) return false;
return false;
// DISABLED - See TODO // DISABLED - See TODO
#ifdef UNIT_TEST #ifdef UNIT_TEST
// Grab the second copy of the data (i.e. inverted) // Grab the second copy of the data (i.e. inverted)
// Header // Header
// i.e. The inter-data/command repeat gap. // i.e. The inter-data/command repeat gap.
if (!matchSpace(results->rawbuf[offset++], SHARP_GAP_TICKS * tick)) if (!matchSpace(results->rawbuf[offset++], kSharpGapTicks * tick))
return false; return false;
// Data // Data
uint64_t second_data = 0; uint64_t second_data = 0;
for (uint16_t i = 0; i < nbits; i++, offset++) { for (uint16_t i = 0; i < nbits; i++, offset++) {
// Use a higher tolerance value for SHARP_BIT_MARK as it is quite small. // Use a higher tolerance value for kSharpBitMark as it is quite small.
if (!matchMark(results->rawbuf[offset++], SHARP_BIT_MARK_TICKS * tick, if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35))
35))
return false; return false;
if (matchSpace(results->rawbuf[offset], SHARP_ONE_SPACE_TICKS * tick)) if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick))
second_data = (second_data << 1) | 1; // 1 second_data = (second_data << 1) | 1; // 1
else if (matchSpace(results->rawbuf[offset], else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick))
SHARP_ZERO_SPACE_TICKS * tick))
second_data <<= 1; // 0 second_data <<= 1; // 0
else else
return false; return false;
} }
// Footer // Footer
if (!match(results->rawbuf[offset++], SHARP_BIT_MARK_TICKS * tick)) if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], SHARP_GAP_TICKS * tick)) !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick))
return false; return false;
// Check that second_data has been inverted correctly. // Check that second_data has been inverted correctly.
if (data != (second_data ^ SHARP_TOGGLE_MASK)) if (data != (second_data ^ kSharpToggleMask)) return false;
return false;
#endif // UNIT_TEST #endif // UNIT_TEST
} }
@@ -267,9 +259,9 @@ bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict,
results->bits = nbits; results->bits = nbits;
results->value = data; results->value = data;
// Address & command are actually transmitted in LSB first order. // Address & command are actually transmitted in LSB first order.
results->address = reverseBits(data, nbits) & SHARP_ADDRESS_MASK; results->address = reverseBits(data, nbits) & kSharpAddressMask;
results->command = reverseBits((data >> 2) & SHARP_COMMAND_MASK, results->command =
SHARP_COMMAND_BITS); reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits);
return true; return true;
} }
#endif // (DECODE_SHARP || DECODE_DENON) #endif // (DECODE_SHARP || DECODE_DENON)
+4 -5
View File
@@ -14,16 +14,15 @@
// //
// Args: // Args:
// data: The contents of the command you want to send. // data: The contents of the command you want to send.
// nbits: The bit size of the command being sent. (SHERWOOD_BITS) // nbits: The bit size of the command being sent. (kSherwoodBits)
// repeat: The nr. of times you want the command to be repeated. (Default: 1) // repeat: The nr. of times you want the command to be repeated. (Default: 1)
// //
// Status: STABLE / Known working. // Status: STABLE / Known working.
// //
// Note: // Note:
// Sherwood remote codes appear to be NEC codes with a manditory repeat code. // Sherwood remote codes appear to be NEC codes with a manditory repeat code.
// i.e. repeat should be >= SHERWOOD_MIN_REPEAT (1). // i.e. repeat should be >= kSherwoodMinRepeat (1).
void IRsend::sendSherwood(uint64_t data, uint16_t nbits, void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) {
uint16_t repeat) { sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat));
sendNEC(data, nbits, std::max((uint16_t) SHERWOOD_MIN_REPEAT, repeat));
} }
#endif #endif
+32 -35
View File
@@ -19,19 +19,19 @@
// Constants // Constants
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/sirc.php // http://www.sbprojects.com/knowledge/ir/sirc.php
#define SONY_TICK 200U const uint16_t kSonyTick = 200;
#define SONY_HDR_MARK_TICKS 12U const uint16_t kSonyHdrMarkTicks = 12;
#define SONY_HDR_MARK (SONY_HDR_MARK_TICKS * SONY_TICK) const uint16_t kSonyHdrMark = kSonyHdrMarkTicks * kSonyTick;
#define SONY_SPACE_TICKS 3U const uint16_t kSonySpaceTicks = 3;
#define SONY_SPACE (SONY_SPACE_TICKS * SONY_TICK) const uint16_t kSonySpace = kSonySpaceTicks * kSonyTick;
#define SONY_ONE_MARK_TICKS 6U const uint16_t kSonyOneMarkTicks = 6;
#define SONY_ONE_MARK (SONY_ONE_MARK_TICKS * SONY_TICK) const uint16_t kSonyOneMark = kSonyOneMarkTicks * kSonyTick;
#define SONY_ZERO_MARK_TICKS 3U const uint16_t kSonyZeroMarkTicks = 3;
#define SONY_ZERO_MARK (SONY_ZERO_MARK_TICKS * SONY_TICK) const uint16_t kSonyZeroMark = kSonyZeroMarkTicks * kSonyTick;
#define SONY_RPT_LENGTH_TICKS 225U const uint16_t kSonyRptLengthTicks = 225;
#define SONY_RPT_LENGTH (SONY_RPT_LENGTH_TICKS * SONY_TICK) const uint16_t kSonyRptLength = kSonyRptLengthTicks * kSonyTick;
#define SONY_MIN_GAP_TICKS 50U const uint16_t kSonyMinGapTicks = 50;
#define SONY_MIN_GAP (SONY_MIN_GAP_TICKS * SONY_TICK) const uint16_t kSonyMinGap = kSonyMinGapTicks * kSonyTick;
#if SEND_SONY #if SEND_SONY
// Send a Sony/SIRC(Serial Infra-Red Control) message. // Send a Sony/SIRC(Serial Infra-Red Control) message.
@@ -50,12 +50,10 @@
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/sirc.php // http://www.sbprojects.com/knowledge/ir/sirc.php
void IRsend::sendSony(uint64_t data, uint16_t nbits, uint16_t repeat) { void IRsend::sendSony(uint64_t data, uint16_t nbits, uint16_t repeat) {
sendGeneric(SONY_HDR_MARK, SONY_SPACE, sendGeneric(kSonyHdrMark, kSonySpace, kSonyOneMark, kSonySpace, kSonyZeroMark,
SONY_ONE_MARK, SONY_SPACE, kSonySpace,
SONY_ZERO_MARK, SONY_SPACE,
0, // No Footer mark. 0, // No Footer mark.
SONY_MIN_GAP, SONY_RPT_LENGTH, kSonyMinGap, kSonyRptLength, data, nbits, 40, true, repeat, 33);
data, nbits, 40, true, repeat, 33);
} }
// Convert Sony/SIRC command, address, & extended bits into sendSony format. // Convert Sony/SIRC command, address, & extended bits into sendSony format.
@@ -68,8 +66,8 @@ void IRsend::sendSony(uint64_t data, uint16_t nbits, uint16_t repeat) {
// A sendSony compatible data message. // A sendSony compatible data message.
// //
// Status: BETA / Should be working. // Status: BETA / Should be working.
uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command, uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command, uint16_t address,
uint16_t address, uint16_t extended) { uint16_t extended) {
uint32_t result = 0; uint32_t result = 0;
switch (nbits) { switch (nbits) {
case 12: // 5 address bits. case 12: // 5 address bits.
@@ -107,7 +105,7 @@ uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command,
// Ref: // Ref:
// http://www.sbprojects.com/knowledge/ir/sirc.php // http://www.sbprojects.com/knowledge/ir/sirc.php
bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) { bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) {
if (results->rawlen < 2 * nbits + HEADER - 1) if (results->rawlen < 2 * nbits + kHeader - 1)
return false; // Message is smaller than we expected. return false; // Message is smaller than we expected.
// Compliance // Compliance
@@ -123,31 +121,30 @@ bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) {
} }
uint64_t data = 0; uint64_t data = 0;
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t actualBits; uint16_t actualBits;
uint32_t timeSoFar = 0; // Time in uSecs of the message length. uint32_t timeSoFar = 0; // Time in uSecs of the message length.
// Header // Header
timeSoFar += results->rawbuf[offset] * RAWTICK; timeSoFar += results->rawbuf[offset] * kRawTick;
if (!matchMark(results->rawbuf[offset], SONY_HDR_MARK)) if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false;
return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t tick = results->rawbuf[offset++] * RAWTICK / SONY_HDR_MARK_TICKS; uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks;
// Data // Data
for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) {
// The gap after a Sony packet for a repeat should be SONY_MIN_GAP or // The gap after a Sony packet for a repeat should be kSonyMinGap or
// (SONY_RPT_LENGTH - timeSoFar) according to the spec. // (kSonyRptLength - timeSoFar) according to the spec.
if (matchSpace(results->rawbuf[offset], SONY_MIN_GAP_TICKS * tick) || if (matchSpace(results->rawbuf[offset], kSonyMinGapTicks * tick) ||
matchAtLeast(results->rawbuf[offset], SONY_RPT_LENGTH - timeSoFar)) matchAtLeast(results->rawbuf[offset], kSonyRptLength - timeSoFar))
break; // Found a repeat space. break; // Found a repeat space.
timeSoFar += results->rawbuf[offset] * RAWTICK; timeSoFar += results->rawbuf[offset] * kRawTick;
if (!matchSpace(results->rawbuf[offset++], SONY_SPACE_TICKS * tick)) if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick))
return false; return false;
timeSoFar += results->rawbuf[offset] * RAWTICK; timeSoFar += results->rawbuf[offset] * kRawTick;
if (matchMark(results->rawbuf[offset], SONY_ONE_MARK_TICKS * tick)) if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick))
data = (data << 1) | 1; data = (data << 1) | 1;
else if (matchMark(results->rawbuf[offset], SONY_ZERO_MARK_TICKS * tick)) else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick))
data <<= 1; data <<= 1;
else else
return false; return false;
+62 -71
View File
@@ -28,33 +28,32 @@
// Toshiba A/C // Toshiba A/C
// Ref: // Ref:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77
#define TOSHIBA_AC_HDR_MARK 4400U const uint16_t kToshibaAcHdrMark = 4400;
#define TOSHIBA_AC_HDR_SPACE 4300U const uint16_t kToshibaAcHdrSpace = 4300;
#define TOSHIBA_AC_BIT_MARK 543U const uint16_t kToshibaAcBitMark = 543;
#define TOSHIBA_AC_ONE_SPACE 1623U const uint16_t kToshibaAcOneSpace = 1623;
#define TOSHIBA_AC_ZERO_SPACE 472U const uint16_t kToshibaAcZeroSpace = 472;
#define TOSHIBA_AC_MIN_GAP 7048U const uint16_t kToshibaAcMinGap = 7048;
#if SEND_TOSHIBA_AC #if SEND_TOSHIBA_AC
// Send a Toshiba A/C message. // Send a Toshiba A/C message.
// //
// Args: // Args:
// data: An array of bytes containing the IR command. // data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=TOSHIBA_AC_STATE_LENGTH) // nbytes: Nr. of bytes of data in the array. (>=kToshibaACStateLength)
// repeat: Nr. of times the message is to be repeated. // repeat: Nr. of times the message is to be repeated.
// (Default = TOSHIBA_AC_MIN_REPEAT). // (Default = kToshibaACMinRepeat).
// //
// Status: StABLE / Working. // Status: StABLE / Working.
// //
void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < TOSHIBA_AC_STATE_LENGTH) if (nbytes < kToshibaACStateLength)
return; // Not enough bytes to send a proper message. return; // Not enough bytes to send a proper message.
sendGeneric(TOSHIBA_AC_HDR_MARK, TOSHIBA_AC_HDR_SPACE, sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark,
TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_ONE_SPACE, kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace,
TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_ZERO_SPACE, kToshibaAcBitMark, kToshibaAcMinGap, data, nbytes, 38, true,
TOSHIBA_AC_BIT_MARK, TOSHIBA_AC_MIN_GAP, repeat, 50);
data, nbytes, 38, true, repeat, 50);
} }
#endif // SEND_TOSHIBA_AC #endif // SEND_TOSHIBA_AC
@@ -65,9 +64,7 @@ void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes,
// Status: STABLE / Working. // Status: STABLE / Working.
// //
// Initialise the object. // Initialise the object.
IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
// Reset the state of the remote to a known good state/sequence. // Reset the state of the remote to a known good state/sequence.
void IRToshibaAC::stateReset() { void IRToshibaAC::stateReset() {
@@ -75,29 +72,26 @@ void IRToshibaAC::stateReset() {
// Known good state obtained from: // Known good state obtained from:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103
// Note: Can't use the following because it requires -std=c++11 // Note: Can't use the following because it requires -std=c++11
// uint8_t remote_state[TOSHIBA_AC_STATE_LENGTH] = { // uint8_t remote_state[kToshibaACStateLength] = {
// 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 }; // 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 };
remote_state[0] = 0xF2; remote_state[0] = 0xF2;
remote_state[1] = 0x0D; remote_state[1] = 0x0D;
remote_state[2] = 0x03; remote_state[2] = 0x03;
remote_state[3] = 0xFC; remote_state[3] = 0xFC;
remote_state[4] = 0x01; remote_state[4] = 0x01;
for (uint8_t i = 5; i < TOSHIBA_AC_STATE_LENGTH; i++) for (uint8_t i = 5; i < kToshibaACStateLength; i++) remote_state[i] = 0;
remote_state[i] = 0;
mode_state = remote_state[6] & 0b00000011; mode_state = remote_state[6] & 0b00000011;
checksum(); // Calculate the checksum checksum(); // Calculate the checksum
} }
// Configure the pin for output. // Configure the pin for output.
void IRToshibaAC::begin() { void IRToshibaAC::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_TOSHIBA_AC #if SEND_TOSHIBA_AC
// Send the current desired state to the IR LED. // Send the current desired state to the IR LED.
void IRToshibaAC::send() { void IRToshibaAC::send(const uint16_t repeat) {
checksum(); // Ensure correct checksum before sending. checksum(); // Ensure correct checksum before sending.
_irsend.sendToshibaAC(remote_state); _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat);
} }
#endif // SEND_TOSHIBA_AC #endif // SEND_TOSHIBA_AC
@@ -109,7 +103,7 @@ uint8_t* IRToshibaAC::getRaw() {
// Override the internal state with the new state. // Override the internal state with the new state.
void IRToshibaAC::setRaw(uint8_t newState[]) { void IRToshibaAC::setRaw(uint8_t newState[]) {
for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) { for (uint8_t i = 0; i < kToshibaACStateLength; i++) {
remote_state[i] = newState[i]; remote_state[i] = newState[i];
} }
mode_state = getMode(true); mode_state = getMode(true);
@@ -127,8 +121,7 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[],
// Only calculate it for valid lengths. // Only calculate it for valid lengths.
if (length > 1) { if (length > 1) {
// Checksum is simple XOR of all bytes except the last one. // Checksum is simple XOR of all bytes except the last one.
for (uint8_t i = 0; i < length - 1; i++) for (uint8_t i = 0; i < length - 1; i++) checksum ^= state[i];
checksum ^= state[i];
} }
return checksum; return checksum;
} }
@@ -139,29 +132,27 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[],
// length: The size of the state. // length: The size of the state.
// Returns: // Returns:
// A boolean. // A boolean.
bool IRToshibaAC::validChecksum(const uint8_t state[], bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) {
const uint16_t length) {
return (length > 1 && state[length - 1] == calcChecksum(state, length)); return (length > 1 && state[length - 1] == calcChecksum(state, length));
} }
// Calculate & set the checksum for the current internal state of the remote. // Calculate & set the checksum for the current internal state of the remote.
void IRToshibaAC::checksum(const uint16_t length) { void IRToshibaAC::checksum(const uint16_t length) {
// Stored the checksum value in the last byte. // Stored the checksum value in the last byte.
if (length > 1) if (length > 1) remote_state[length - 1] = calcChecksum(remote_state, length);
remote_state[length - 1] = calcChecksum(remote_state, length);
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRToshibaAC::on() { void IRToshibaAC::on() {
// state = ON; // state = ON;
remote_state[6] &= ~TOSHIBA_AC_POWER; remote_state[6] &= ~kToshibaAcPower;
setMode(mode_state); setMode(mode_state);
} }
// Set the requested power state of the A/C to off. // Set the requested power state of the A/C to off.
void IRToshibaAC::off() { void IRToshibaAC::off() {
// state = OFF; // state = OFF;
remote_state[6] |= (TOSHIBA_AC_POWER | 0b00000011); remote_state[6] |= (kToshibaAcPower | 0b00000011);
} }
// Set the requested power state of the A/C. // Set the requested power state of the A/C.
@@ -174,28 +165,28 @@ void IRToshibaAC::setPower(bool state) {
// Return the requested power state of the A/C. // Return the requested power state of the A/C.
bool IRToshibaAC::getPower() { bool IRToshibaAC::getPower() {
return((remote_state[6] & TOSHIBA_AC_POWER) == 0); return ((remote_state[6] & kToshibaAcPower) == 0);
} }
// Set the temp. in deg C // Set the temp. in deg C
void IRToshibaAC::setTemp(uint8_t temp) { void IRToshibaAC::setTemp(uint8_t temp) {
temp = std::max((uint8_t) TOSHIBA_AC_MIN_TEMP, temp); temp = std::max((uint8_t)kToshibaAcMinTemp, temp);
temp = std::min((uint8_t) TOSHIBA_AC_MAX_TEMP, temp); temp = std::min((uint8_t)kToshibaAcMaxTemp, temp);
remote_state[5] = (temp - TOSHIBA_AC_MIN_TEMP) << 4; remote_state[5] = (temp - kToshibaAcMinTemp) << 4;
} }
// Return the set temp. in deg C // Return the set temp. in deg C
uint8_t IRToshibaAC::getTemp() { uint8_t IRToshibaAC::getTemp() {
return((remote_state[5] >> 4) + TOSHIBA_AC_MIN_TEMP); return ((remote_state[5] >> 4) + kToshibaAcMinTemp);
} }
// Set the speed of the fan, 0-5. // Set the speed of the fan, 0-5.
// 0 is auto, 1-5 is the speed, 5 is Max. // 0 is auto, 1-5 is the speed, 5 is Max.
void IRToshibaAC::setFan(uint8_t fan) { void IRToshibaAC::setFan(uint8_t fan) {
// Bounds check // Bounds check
if (fan > TOSHIBA_AC_FAN_MAX) if (fan > kToshibaAcFanMax)
fan = TOSHIBA_AC_FAN_MAX; // Set the fan to maximum if out of range. fan = kToshibaAcFanMax; // Set the fan to maximum if out of range.
if (fan > TOSHIBA_AC_FAN_AUTO) fan++; if (fan > kToshibaAcFanAuto) fan++;
remote_state[6] &= 0b00011111; // Clear the previous fan state remote_state[6] &= 0b00011111; // Clear the previous fan state
remote_state[6] |= (fan << 5); remote_state[6] |= (fan << 5);
} }
@@ -203,7 +194,7 @@ void IRToshibaAC::setFan(uint8_t fan) {
// Return the requested state of the unit's fan. // Return the requested state of the unit's fan.
uint8_t IRToshibaAC::getFan() { uint8_t IRToshibaAC::getFan() {
uint8_t fan = remote_state[6] >> 5; uint8_t fan = remote_state[6] >> 5;
if (fan == TOSHIBA_AC_FAN_AUTO) return TOSHIBA_AC_FAN_AUTO; if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto;
return --fan; return --fan;
} }
@@ -223,11 +214,16 @@ uint8_t IRToshibaAC::getMode(bool useRaw) {
void IRToshibaAC::setMode(uint8_t mode) { void IRToshibaAC::setMode(uint8_t mode) {
// If we get an unexpected mode, default to AUTO. // If we get an unexpected mode, default to AUTO.
switch (mode) { switch (mode) {
case TOSHIBA_AC_AUTO: break; case kToshibaAcAuto:
case TOSHIBA_AC_COOL: break; break;
case TOSHIBA_AC_DRY: break; case kToshibaAcCool:
case TOSHIBA_AC_HEAT: break; break;
default: mode = TOSHIBA_AC_AUTO; case kToshibaAcDry:
break;
case kToshibaAcHeat:
break;
default:
mode = kToshibaAcAuto;
} }
mode_state = mode; mode_state = mode;
// Only adjust the remote_state if we have power set to on. // Only adjust the remote_state if we have power set to on.
@@ -252,16 +248,16 @@ std::string IRToshibaAC::toString() {
result += "Off"; result += "Off";
result += ", Mode: " + uint64ToString(getMode()); result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) { switch (getMode()) {
case TOSHIBA_AC_AUTO: case kToshibaAcAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case TOSHIBA_AC_COOL: case kToshibaAcCool:
result += " (COOL)"; result += " (COOL)";
break; break;
case TOSHIBA_AC_HEAT: case kToshibaAcHeat:
result += " (HEAT)"; result += " (HEAT)";
break; break;
case TOSHIBA_AC_DRY: case kToshibaAcDry:
result += " (DRY)"; result += " (DRY)";
break; break;
default: default:
@@ -270,10 +266,10 @@ std::string IRToshibaAC::toString() {
result += ", Temp: " + uint64ToString(getTemp()) + "C"; result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan()); result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) { switch (getFan()) {
case TOSHIBA_AC_FAN_AUTO: case kToshibaAcFanAuto:
result += " (AUTO)"; result += " (AUTO)";
break; break;
case TOSHIBA_AC_FAN_MAX: case kToshibaAcFanMax:
result += " (MAX)"; result += " (MAX)";
break; break;
} }
@@ -285,7 +281,7 @@ std::string IRToshibaAC::toString() {
// Places successful decode information in the results pointer. // Places successful decode information in the results pointer.
// Args: // Args:
// results: Ptr to the data to decode and where to store the decode result. // results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically TOSHIBA_AC_BITS. // nbits: The number of data bits to expect. Typically kToshibaACBits.
// strict: Flag to indicate if we strictly adhere to the specification. // strict: Flag to indicate if we strictly adhere to the specification.
// Returns: // Returns:
// boolean: True if it can decode it, false if it can't. // boolean: True if it can decode it, false if it can't.
@@ -296,32 +292,27 @@ std::string IRToshibaAC::toString() {
// //
bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits,
bool strict) { bool strict) {
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0; uint16_t dataBitsSoFar = 0;
// Have we got enough data to successfully decode? // Have we got enough data to successfully decode?
if (results->rawlen < TOSHIBA_AC_BITS + HEADER + FOOTER - 1) if (results->rawlen < kToshibaACBits + kHeader + kFooter - 1)
return false; // Can't possibly be a valid message. return false; // Can't possibly be a valid message.
// Compliance // Compliance
if (strict && nbits != TOSHIBA_AC_BITS) if (strict && nbits != kToshibaACBits)
return false; // Must be called with the correct nr. of bytes. return false; // Must be called with the correct nr. of bytes.
// Header // Header
if (!matchMark(results->rawbuf[offset++], TOSHIBA_AC_HDR_MARK)) if (!matchMark(results->rawbuf[offset++], kToshibaAcHdrMark)) return false;
return false; if (!matchSpace(results->rawbuf[offset++], kToshibaAcHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset++], TOSHIBA_AC_HDR_SPACE))
return false;
// Data // Data
for (uint8_t i = 0; i < TOSHIBA_AC_STATE_LENGTH; i++) { for (uint8_t i = 0; i < kToshibaACStateLength; i++) {
// Read a byte's worth of data. // Read a byte's worth of data.
match_result_t data_result = matchData(&(results->rawbuf[offset]), 8, match_result_t data_result =
TOSHIBA_AC_BIT_MARK, matchData(&(results->rawbuf[offset]), 8, kToshibaAcBitMark,
TOSHIBA_AC_ONE_SPACE, kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace);
TOSHIBA_AC_BIT_MARK,
TOSHIBA_AC_ZERO_SPACE);
if (data_result.success == false) return false; // Fail if (data_result.success == false) return false; // Fail
dataBitsSoFar += 8; dataBitsSoFar += 8;
results->state[i] = (uint8_t)data_result.data; results->state[i] = (uint8_t)data_result.data;
@@ -329,8 +320,8 @@ bool IRrecv::decodeToshibaAC(decode_results *results, uint16_t nbits,
} }
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], TOSHIBA_AC_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kToshibaAcBitMark)) return false;
if (!matchSpace(results->rawbuf[offset++], TOSHIBA_AC_MIN_GAP)) return false; if (!matchSpace(results->rawbuf[offset++], kToshibaAcMinGap)) return false;
// Compliance // Compliance
if (strict) { if (strict) {
+25 -14
View File
@@ -21,15 +21,26 @@
// Toshiba A/C support added by David Conran // Toshiba A/C support added by David Conran
// Constants // Constants
#define TOSHIBA_AC_AUTO 0U const uint8_t kToshibaAcAuto = 0;
#define TOSHIBA_AC_COOL 1U const uint8_t kToshibaAcCool = 1;
#define TOSHIBA_AC_DRY 2U const uint8_t kToshibaAcDry = 2;
#define TOSHIBA_AC_HEAT 3U const uint8_t kToshibaAcHeat = 3;
#define TOSHIBA_AC_POWER 4U const uint8_t kToshibaAcPower = 4;
#define TOSHIBA_AC_FAN_AUTO 0U const uint8_t kToshibaAcFanAuto = 0;
#define TOSHIBA_AC_FAN_MAX 5U const uint8_t kToshibaAcFanMax = 5;
#define TOSHIBA_AC_MIN_TEMP 17U // 17C const uint8_t kToshibaAcMinTemp = 17; // 17C
#define TOSHIBA_AC_MAX_TEMP 30U // 30C const uint8_t kToshibaAcMaxTemp = 30; // 30C
// Legacy defines. (Deperecated)
#define TOSHIBA_AC_AUTO kToshibaAcAuto
#define TOSHIBA_AC_COOL kToshibaAcCool
#define TOSHIBA_AC_DRY kToshibaAcDry
#define TOSHIBA_AC_HEAT kToshibaAcHeat
#define TOSHIBA_AC_POWER kToshibaAcPower
#define TOSHIBA_AC_FAN_AUTO kToshibaAcFanAuto
#define TOSHIBA_AC_FAN_MAX kToshibaAcFanMax
#define TOSHIBA_AC_MIN_TEMP kToshibaAcMinTemp
#define TOSHIBA_AC_MAX_TEMP kToshibaAcMaxTemp
class IRToshibaAC { class IRToshibaAC {
public: public:
@@ -37,7 +48,7 @@ class IRToshibaAC {
void stateReset(); void stateReset();
#if SEND_TOSHIBA_AC #if SEND_TOSHIBA_AC
void send(); void send(const uint16_t repeat = kToshibaACMinRepeat);
#endif // SEND_TOSHIBA_AC #endif // SEND_TOSHIBA_AC
void begin(); void begin();
void on(); void on();
@@ -53,7 +64,7 @@ class IRToshibaAC {
void setRaw(uint8_t newState[]); void setRaw(uint8_t newState[]);
uint8_t* getRaw(); uint8_t* getRaw();
static bool validChecksum(const uint8_t state[], static bool validChecksum(const uint8_t state[],
const uint16_t length = TOSHIBA_AC_STATE_LENGTH); const uint16_t length = kToshibaACStateLength);
#ifdef ARDUINO #ifdef ARDUINO
String toString(); String toString();
#else #else
@@ -63,10 +74,10 @@ class IRToshibaAC {
private: private:
#endif #endif
uint8_t remote_state[TOSHIBA_AC_STATE_LENGTH]; uint8_t remote_state[kToshibaACStateLength];
void checksum(const uint16_t length = TOSHIBA_AC_STATE_LENGTH); void checksum(const uint16_t length = kToshibaACStateLength);
static uint8_t calcChecksum(const uint8_t state[], static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = TOSHIBA_AC_STATE_LENGTH); const uint16_t length = kToshibaACStateLength);
uint8_t mode_state; uint8_t mode_state;
IRsend _irsend; IRsend _irsend;
}; };
+43 -62
View File
@@ -5,49 +5,43 @@
#include "IRutils.h" #include "IRutils.h"
// Constants // Constants
#define TROTEC_HDR_MARK 5952U const uint16_t kTrotecHdrMark = 5952;
#define TROTEC_HDR_SPACE 7364U const uint16_t kTrotecHdrSpace = 7364;
#define TROTEC_ONE_MARK 592U const uint16_t kTrotecOneMark = 592;
#define TROTEC_ONE_SPACE 1560U const uint16_t kTrotecOneSpace = 1560;
#define TROTEC_ZERO_MARK 592U const uint16_t kTrotecZeroMark = 592;
#define TROTEC_ZERO_SPACE 592U const uint16_t kTrotecZeroSpace = 592;
#define TROTEC_GAP 6184U const uint16_t kTrotecGap = 6184;
#define TROTEC_GAP_END 1500U // made up value const uint16_t kTrotecGapEnd = 1500; // made up value
#if SEND_TROTEC #if SEND_TROTEC
void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes,
uint16_t repeat) { uint16_t repeat) {
if (nbytes < TROTEC_COMMAND_LENGTH) if (nbytes < kTrotecStateLength) return;
return;
for (uint16_t r = 0; r <= repeat; r++) { for (uint16_t r = 0; r <= repeat; r++) {
sendGeneric(TROTEC_HDR_MARK, TROTEC_HDR_SPACE, sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark,
TROTEC_ONE_MARK, TROTEC_ONE_SPACE, kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace,
TROTEC_ZERO_MARK, TROTEC_ZERO_SPACE, kTrotecOneMark, kTrotecGap, data, nbytes, 36, false,
TROTEC_ONE_MARK, TROTEC_GAP, 0, // Repeats handled elsewhere
data, nbytes, 36, false, 0, // Repeats handled elsewhere
50); 50);
// More footer // More footer
enableIROut(36); enableIROut(36);
mark(TROTEC_ONE_MARK); mark(kTrotecOneMark);
space(TROTEC_GAP_END); space(kTrotecGapEnd);
} }
} }
#endif // SEND_TROTEC #endif // SEND_TROTEC
IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); }
stateReset();
}
void IRTrotecESP::begin() { void IRTrotecESP::begin() { _irsend.begin(); }
_irsend.begin();
}
#if SEND_TROTEC #if SEND_TROTEC
void IRTrotecESP::send() { void IRTrotecESP::send(const uint16_t repeat) {
checksum(); checksum();
_irsend.sendTrotec(trotec); _irsend.sendTrotec(trotec, kTrotecStateLength, repeat);
} }
#endif // SEND_TROTEC #endif // SEND_TROTEC
@@ -61,16 +55,15 @@ void IRTrotecESP::checksum() {
} }
void IRTrotecESP::stateReset() { void IRTrotecESP::stateReset() {
for (uint8_t i = 2; i < TROTEC_COMMAND_LENGTH; i++) for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0;
trotec[i] = 0x0;
trotec[0] = TROTEC_INTRO1; trotec[0] = kTrotecIntro1;
trotec[1] = TROTEC_INTRO2; trotec[1] = kTrotecIntro2;
setPower(false); setPower(false);
setTemp(TROTEC_DEF_TEMP); setTemp(kTrotecDefTemp);
setSpeed(TROTEC_FAN_MED); setSpeed(kTrotecFanMed);
setMode(TROTEC_AUTO); setMode(kTrotecAuto);
} }
uint8_t* IRTrotecESP::getRaw() { uint8_t* IRTrotecESP::getRaw() {
@@ -80,67 +73,55 @@ uint8_t* IRTrotecESP::getRaw() {
void IRTrotecESP::setPower(bool state) { void IRTrotecESP::setPower(bool state) {
if (state) if (state)
trotec[2] |= (TROTEC_ON << 3); trotec[2] |= (kTrotecOn << 3);
else else
trotec[2] &= ~(TROTEC_ON << 3); trotec[2] &= ~(kTrotecOn << 3);
} }
uint8_t IRTrotecESP::getPower() { uint8_t IRTrotecESP::getPower() { return trotec[2] & (kTrotecOn << 3); }
return trotec[2] & (TROTEC_ON << 3);
}
void IRTrotecESP::setSpeed(uint8_t speed) { void IRTrotecESP::setSpeed(uint8_t speed) {
trotec[2] = (trotec[2] & 0xcf) | (speed << 4); trotec[2] = (trotec[2] & 0xcf) | (speed << 4);
} }
uint8_t IRTrotecESP::getSpeed() { uint8_t IRTrotecESP::getSpeed() { return trotec[2] & 0x30; }
return trotec[2] & 0x30;
}
void IRTrotecESP::setMode(uint8_t mode) { void IRTrotecESP::setMode(uint8_t mode) {
trotec[2] = (trotec[2] & 0xfc) | mode; trotec[2] = (trotec[2] & 0xfc) | mode;
} }
uint8_t IRTrotecESP::getMode() { uint8_t IRTrotecESP::getMode() { return trotec[2] & 0x03; }
return trotec[2] & 0x03;
}
void IRTrotecESP::setTemp(uint8_t temp) { void IRTrotecESP::setTemp(uint8_t temp) {
if (temp < TROTEC_MIN_TEMP) if (temp < kTrotecMinTemp)
temp = TROTEC_MIN_TEMP; temp = kTrotecMinTemp;
else if (temp > TROTEC_MAX_TEMP) else if (temp > kTrotecMaxTemp)
temp = TROTEC_MAX_TEMP; temp = kTrotecMaxTemp;
trotec[3] = (trotec[3] & 0x80) | (temp - TROTEC_MIN_TEMP); trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp);
} }
uint8_t IRTrotecESP::getTemp() { uint8_t IRTrotecESP::getTemp() { return trotec[3] & 0x7f; }
return trotec[3] & 0x7f;
}
void IRTrotecESP::setSleep(bool sleep) { void IRTrotecESP::setSleep(bool sleep) {
if (sleep) if (sleep)
trotec[3] |= (TROTEC_SLEEP_ON << 7); trotec[3] |= (kTrotecSleepOn << 7);
else else
trotec[3] &= ~(TROTEC_SLEEP_ON << 7); trotec[3] &= ~(kTrotecSleepOn << 7);
} }
bool IRTrotecESP::getSleep(void) { bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); }
return trotec[3] & (TROTEC_SLEEP_ON << 7);
}
void IRTrotecESP::setTimer(uint8_t timer) { void IRTrotecESP::setTimer(uint8_t timer) {
if (timer > TROTEC_MAX_TIMER) timer = TROTEC_MAX_TIMER; if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer;
if (timer) { if (timer) {
trotec[5] |= (TROTEC_TIMER_ON << 6); trotec[5] |= (kTrotecTimerOn << 6);
trotec[6] = timer; trotec[6] = timer;
} else { } else {
trotec[5] &= ~(TROTEC_TIMER_ON << 6); trotec[5] &= ~(kTrotecTimerOn << 6);
trotec[6] = 0; trotec[6] = 0;
} }
} }
uint8_t IRTrotecESP::getTimer() { uint8_t IRTrotecESP::getTimer() { return trotec[6]; }
return trotec[6];
}
+32 -20
View File
@@ -8,45 +8,57 @@
// Constants // Constants
// Byte 0 // Byte 0
#define TROTEC_INTRO1 0x12 const uint8_t kTrotecIntro1 = 0x12;
// Byte 1 // Byte 1
#define TROTEC_INTRO2 0x34 const uint8_t kTrotecIntro2 = 0x34;
// Byte 2 // Byte 2
#define TROTEC_AUTO 0 const uint8_t kTrotecAuto = 0;
#define TROTEC_COOL 1 const uint8_t kTrotecCool = 1;
#define TROTEC_DRY 2 const uint8_t kTrotecDry = 2;
#define TROTEC_FAN 3 const uint8_t kTrotecFan = 3;
#define TROTEC_ON 1 const uint8_t kTrotecOn = 1;
#define TROTEC_OFF 0 const uint8_t kTrotecOff = 0;
#define TROTEC_FAN_LOW 1 const uint8_t kTrotecFanLow = 1;
#define TROTEC_FAN_MED 2 const uint8_t kTrotecFanMed = 2;
#define TROTEC_FAN_HIGH 3 const uint8_t kTrotecFanHigh = 3;
// Byte 3 // Byte 3
#define TROTEC_MIN_TEMP 18 const uint8_t kTrotecMinTemp = 18;
#define TROTEC_MAX_TEMP 32 const uint8_t kTrotecDefTemp = 25;
#define TROTEC_DEF_TEMP 25 const uint8_t kTrotecMaxTemp = 32;
#define TROTEC_SLEEP_ON 1 const uint8_t kTrotecSleepOn = 1;
// Byte 5 // Byte 5
#define TROTEC_TIMER_ON 1 const uint8_t kTrotecTimerOn = 1;
// Byte 6 // Byte 6
#define TROTEC_MIN_TIMER 0 const uint8_t kTrotecMinTimer = 0;
#define TROTEC_MAX_TIMER 23 const uint8_t kTrotecMaxTimer = 23;
// Legacy defines. (Deperecated)
#define TROTEC_AUTO kTrotecAuto
#define TROTEC_COOL kTrotecCool
#define TROTEC_DRY kTrotecDry
#define TROTEC_FAN kTrotecFan
#define TROTEC_FAN_LOW kTrotecFanLow
#define TROTEC_FAN_MED kTrotecFanMed
#define TROTEC_FAN_HIGH kTrotecFanHigh
#define TROTEC_MIN_TEMP kTrotecMinTemp
#define TROTEC_MAX_TEMP kTrotecMaxTemp
#define TROTEC_MIN_TIMER kTrotecMinTimer
#define TROTEC_MAX_TIMER kTrotecMaxTimer
class IRTrotecESP { class IRTrotecESP {
public: public:
explicit IRTrotecESP(uint16_t pin); explicit IRTrotecESP(uint16_t pin);
#if SEND_TROTEC #if SEND_TROTEC
void send(); void send(const uint16_t repeat = kTrotecDefaultRepeat);
#endif // SEND_TROTEC #endif // SEND_TROTEC
void begin(); void begin();
@@ -71,7 +83,7 @@ class IRTrotecESP {
uint8_t* getRaw(); uint8_t* getRaw();
private: private:
uint8_t trotec[TROTEC_COMMAND_LENGTH]; uint8_t trotec[kTrotecStateLength];
void stateReset(); void stateReset();
void checksum(); void checksum();
IRsend _irsend; IRsend _irsend;
+481
View File
@@ -0,0 +1,481 @@
// Copyright 2018 Erdem U. Altinyurt
#include "ir_Vestel.h"
#include <algorithm>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
#include "IRutils.h"
#include "ir_Haier.h"
// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL
// VV VV EE S TT EE LL
// VV VV EEEEE SSSS TT EEEEE LL
// VV VV EE S TT EE LL
// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL
// Vestel added by Erdem U. Altinyurt
// Equipment it seems compatible with:
// * Vestel AC Model BIOX CXP-9 (9K BTU)
// * <Add models (A/C & remotes) you've gotten it working with here>
// Ref:
// None. Totally reverse engineered.
#if SEND_VESTEL_AC
// Send a Vestel message
//
// Args:
// data: Contents of the message to be sent.
// nbits: Nr. of bits of data to be sent. Typically kVestelBits.
//
// Status: STABLE / Working.
//
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
data, nbits, 38, false, repeat, 50);
}
#endif
// Code to emulate Vestel A/C IR remote control unit.
// Initialise the object.
IRVestelAC::IRVestelAC(uint16_t pin) : _irsend(pin) { stateReset(); }
// Reset the state of the remote to a known good state/sequence.
void IRVestelAC::stateReset() {
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
remote_state.rawCode = 0x0F00D9001FEF201LL;
remote_time_state.rawCode = 0x00;
use_time_state = false;
}
// Configure the pin for output.
void IRVestelAC::begin() {
_irsend.begin();
stateReset();
}
#if SEND_VESTEL_AC
// Send the current desired state to the IR LED.
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;
else
code_to_send = remote_state.rawCode;
_irsend.sendVestelAC(code_to_send);
}
#endif // SEND_VESTEL_AC
// Return the internal state date of the remote.
uint64_t IRVestelAC::getRaw() {
checksum();
if (use_time_state) return remote_time_state.rawCode;
return remote_state.rawCode;
}
// Override the internal state with the new state.
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;
}
void IRVestelAC::setRaw(const uint64_t newState) {
remote_state.rawCode = newState;
remote_time_state.rawCode = newState;
}
// Set the requested power state of the A/C to on.
void IRVestelAC::on() { setPower(true); }
// Set the requested power state of the A/C to off.
void IRVestelAC::off() { setPower(false); }
// Set the requested power state of the A/C.
void IRVestelAC::setPower(const bool state) {
if (state)
remote_state.power = 0xF;
else
remote_state.power = 0xC;
use_time_state = false;
}
// Return the requested power state of the A/C.
bool IRVestelAC::getPower() { return (remote_state.power == 0xF); }
// Set the temperature in Celsius degrees.
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;
use_time_state = false;
}
// Return the set temperature.
uint8_t IRVestelAC::getTemp(void) { return remote_state.temp + 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) {
switch (fan) {
case kVestelACFanLow:
case kVestelACFanMed:
case kVestelACFanHigh:
case kVestelACFanAutoCool:
case kVestelACFanAutoHot:
remote_state.fan = fan;
break;
default:
remote_state.fan = kVestelACFanAuto;
}
use_time_state = false;
}
// Return the requested state of the unit's fan.
uint8_t IRVestelAC::getFan() { return remote_state.fan; }
// 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; }
// Set the requested climate operation mode of the a/c unit.
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;
break;
default:
remote_state.mode = kVestelACAuto;
}
use_time_state = false;
}
// Set Auto mode of AC.
void IRVestelAC::setAuto(const int8_t autoLevel) {
if (autoLevel < -2 || autoLevel > 2) return;
setMode(kVestelACAuto);
setFan((autoLevel < 0 ? kVestelACFanAutoCool : kVestelACFanAutoHot));
if (autoLevel == 2)
setTemp(30);
else if (autoLevel == 1)
setTemp(31);
else if (autoLevel == 0)
setTemp(25);
else if (autoLevel == -1)
setTemp(16);
else if (autoLevel == -2)
setTemp(17);
}
// 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;
}
use_time_state = true;
}
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;
use_time_state = true;
}
uint16_t IRVestelAC::getTime(void) {
return remote_time_state.t_hour * 60 + remote_time_state.t_minute;
}
// 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;
use_time_state = true;
}
uint16_t IRVestelAC::getOnTimer(void) {
return remote_time_state.t_turnOnHour * 60 +
remote_time_state.t_turnOnMinute * 10;
}
// 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;
use_time_state = true;
}
uint16_t IRVestelAC::getOffTimer(void) {
return remote_time_state.t_turnOffHour * 60 +
remote_time_state.t_turnOffMinute * 10;
}
// Set the Sleep state of the A/C.
void IRVestelAC::setSleep(const bool state) {
remote_state.turbo_sleep_normal = state ? kVestelACSleep : kVestelACNormal;
use_time_state = false;
}
// Return the Sleep state of the A/C.
bool IRVestelAC::getSleep() {
return remote_state.turbo_sleep_normal == kVestelACSleep;
}
// Set the Turbo state of the A/C.
void IRVestelAC::setTurbo(const bool state) {
remote_state.turbo_sleep_normal = state ? kVestelACTurbo : kVestelACNormal;
use_time_state = false;
}
// Return the Turbo state of the A/C.
bool IRVestelAC::getTurbo() {
return remote_state.turbo_sleep_normal == kVestelACTurbo;
}
// Set the Ion state of the A/C.
void IRVestelAC::setIon(const bool state) {
remote_state.ion = state ? kVestelACIon : 0;
use_time_state = false;
}
// Return the Ion state of the A/C.
bool IRVestelAC::getIon() { return remote_state.ion == kVestelACIon; }
// Set the Swing Roaming state of the A/C.
void IRVestelAC::setSwing(const bool state) {
remote_state.swing = state ? kVestelACSwing : 0xF;
use_time_state = false;
}
// Return the Swing Roaming state of the A/C.
bool IRVestelAC::getSwing() { return remote_state.swing == 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) {
// Just counts the set bits +1 on stream and take inverse after mask
uint8_t sum = 0;
uint64_t temp_state = state & kVestelACCRCMask;
for (; temp_state; temp_state >>= 1)
if (temp_state & 1) sum++;
sum += 2;
sum = 0xff - sum;
return sum;
}
// Verify the checksum is valid for a given state.
// Args:
// state: The state to verify the checksum of.
// Returns:
// A boolean.
bool IRVestelAC::validChecksum(const uint64_t state) {
return (((state >> 12) & 0xFF) == calcChecksum(state));
}
// Calculate & set the checksum for the current internal state of the remote.
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);
}
bool IRVestelAC::isTimeCommand() {
return (remote_state.power == 0x00 || use_time_state);
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRVestelAC::toString() {
String result = "";
#else
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 += ", On Timer: ";
result += (remote_time_state.t_on_active && !remote_time_state.t_timer_mode)
? IRHaierAC::timeToString(getOnTimer())
: "Off";
result += ", Off Timer: ";
result += remote_time_state.t_off_active
? IRHaierAC::timeToString(getOffTimer())
: "Off";
return result;
}
// Not a time command, it's a normal command.
result += "Power: ";
result += (getPower() ? "On" : "Off");
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kVestelACAuto:
result += " (AUTO)";
break;
case kVestelACCool:
result += " (COOL)";
break;
case kVestelACHeat:
result += " (HEAT)";
break;
case kVestelACDry:
result += " (DRY)";
break;
case kVestelACFan:
result += " (FAN)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kVestelACFanAuto:
result += " (AUTO)";
break;
case kVestelACFanLow:
result += " (LOW)";
break;
case kVestelACFanMed:
result += " (MEDIUM)";
break;
case kVestelACFanHigh:
result += " (HIGH)";
break;
case kVestelACFanAutoCool:
result += " (AUTO COOL)";
break;
case kVestelACFanAutoHot:
result += " (AUTO HOT)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Sleep: ";
result += (getSleep() ? "On" : "Off");
result += ", Turbo: ";
result += (getTurbo() ? "On" : "Off");
result += ", Ion: ";
result += (getIon() ? "On" : "Off");
result += ", Swing: ";
result += (getSwing() ? "On" : "Off");
return result;
}
#if DECODE_VESTEL_AC
// Decode the supplied Vestel 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 kVestelBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: Alpha / Needs testing against a real device.
//
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)
return false; // Not strictly a Vestel AC message.
uint64_t data = 0;
uint16_t offset = kStartOffset;
if (nbits > sizeof(data) * 8)
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;
// Data (Normal)
match_result_t data_result =
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;
// Compliance
if (strict)
if (!IRVestelAC::validChecksum(data_result.data)) return false;
// Success
results->decode_type = VESTEL_AC;
results->bits = nbits;
results->value = data;
results->address = 0;
results->command = 0;
return true;
}
#endif // DECODE_VESTEL_AC
+160
View File
@@ -0,0 +1,160 @@
// Copyright 2018 Erdem U. Altinyurt
// (Used Midea.h as template)
#ifndef IR_VESTEL_H_
#define IR_VESTEL_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifdef ARDUINO
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// VV VV EEEEEEE SSSSS TTTTTTTT EEEEEEE LL
// VV VV EE S TT EE LL
// VV VV EEEEE SSSS TT EEEEE LL
// VV VV EE S TT EE LL
// VVV EEEEEEE SSSSS TT EEEEEEE LLLLLLL
// Vestel added by Erdem U. Altinyurt
// 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 uint8_t kVestelACMinTempH = 16;
const uint8_t kVestelACMinTempC = 18;
const uint8_t kVestelACMaxTemp = 30;
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 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;
#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.
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 {
public:
explicit IRVestelAC(uint16_t pin);
void stateReset();
#if SEND_VESTEL_AC
void send();
#endif // SEND_VESTEL_AC
void begin(void);
void on(void);
void off(void);
void setPower(const bool state);
bool getPower();
void setAuto(const int8_t autoLevel);
void setTimer(const uint16_t minutes);
uint16_t getTimer(void);
void setTime(const uint16_t minutes);
uint16_t getTime(void);
void setOnTimer(const uint16_t minutes);
uint16_t getOnTimer(void);
void setOffTimer(const uint16_t minutes);
uint16_t getOffTimer(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 setRaw(uint8_t* newState);
void setRaw(const uint64_t newState);
uint64_t getRaw(void);
static bool validChecksum(const uint64_t state);
void setSwing(const bool state);
bool getSwing(void);
void setSleep(const bool state);
bool getSleep(void);
void setTurbo(const bool state);
bool getTurbo(void);
void setIon(const bool state);
bool getIon(void);
bool isTimeCommand(void);
#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;
void checksum();
static uint8_t calcChecksum(const uint64_t state);
IRsend _irsend;
};
#endif // IR_VESTEL_H_
+634
View File
@@ -0,0 +1,634 @@
// Copyright 2018 David Conran
//
// Code to emulate Whirlpool protocol compatible devices.
// Should be compatible with:
// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L
// Remotes:
// * DG11J1-3A / DG11J1-04
// * DG11J1-91
//
// Note: Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported.
// Advanced 6thSense, Dehumidify, & Sleep modes are not supported.
// FYI:
// Dim == !Light
// Jet == Super == Turbo
//
#include "ir_Whirlpool.h"
#include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h"
#include "IRutils.h"
// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL
// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL
// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL
// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL
// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL
// Constants
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509
const uint16_t kWhirlpoolAcHdrMark = 8950;
const uint16_t kWhirlpoolAcHdrSpace = 4484;
const uint16_t kWhirlpoolAcBitMark = 597;
const uint16_t kWhirlpoolAcOneSpace = 1649;
const uint16_t kWhirlpoolAcZeroSpace = 533;
const uint16_t kWhirlpoolAcGap = 7920;
const uint32_t kWhirlpoolAcMinGap = 100000; // Completely made up value.
const uint8_t kWhirlpoolAcSections = 3;
#if SEND_WHIRLPOOL_AC
// Send a Whirlpool A/C message.
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: ALPHA / Untested.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/509
void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
if (nbytes < kWhirlpoolAcStateLength)
return; // Not enough bytes to send a proper message.
for (uint16_t r = 0; r <= repeat; r++) {
// Section 1
sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark,
kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark,
kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap,
data, 6, // 6 bytes == 48 bits
38000, // Complete guess of the modulation frequency.
false, 0, 50);
// Section 2
sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace,
kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark,
kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits
38000, // Complete guess of the modulation frequency.
false, 0, 50);
// Section 3
sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace,
kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark,
kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits
38000, // Complete guess of the modulation frequency.
false, 0, 50);
}
}
#endif // SEND_WHIRLPOOL_AC
// Class for emulating a Whirlpool A/C remote.
// Decoding help from:
// @redmusicxd, @josh929800, @raducostea
IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); }
void IRWhirlpoolAc::stateReset() {
for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0;
remote_state[0] = 0x83;
remote_state[1] = 0x06;
remote_state[6] = 0x80;
_setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value.
}
void IRWhirlpoolAc::begin() { _irsend.begin(); }
bool IRWhirlpoolAc::validChecksum(uint8_t state[], const uint16_t length) {
if (length > kWhirlpoolAcChecksumByte1 &&
state[kWhirlpoolAcChecksumByte1] !=
xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) {
DPRINTLN("DEBUG: First Whirlpool AC checksum failed.");
return false;
}
if (length > kWhirlpoolAcChecksumByte2 &&
state[kWhirlpoolAcChecksumByte2] !=
xorBytes(state + kWhirlpoolAcChecksumByte1 + 1,
kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) {
DPRINTLN("DEBUG: Second Whirlpool AC checksum failed.");
return false;
}
// State is too short to have a checksum or everything checked out.
return true;
}
// Update the checksum for the internal state.
void IRWhirlpoolAc::checksum(uint16_t length) {
if (length >= kWhirlpoolAcChecksumByte1)
remote_state[kWhirlpoolAcChecksumByte1] =
xorBytes(remote_state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2);
if (length >= kWhirlpoolAcChecksumByte2)
remote_state[kWhirlpoolAcChecksumByte2] =
xorBytes(remote_state + kWhirlpoolAcChecksumByte1 + 1,
kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1);
}
#if SEND_WHIRLPOOL_AC
void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) {
if (calcchecksum) checksum();
_irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat);
}
#endif // SEND_WHIRLPOOL_AC
uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) {
if (calcchecksum) checksum();
return remote_state;
}
void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) {
for (uint8_t i = 0; i < length && i < kWhirlpoolAcStateLength; i++)
remote_state[i] = new_code[i];
}
whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() {
if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask)
return DG11J191;
else
return DG11J13A;
}
void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) {
switch (model) {
case DG11J191:
remote_state[kWhirlpoolAcAltTempPos] |= kWhirlpoolAcAltTempMask;
break;
case DG11J13A:
// FALL THRU
default:
remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask;
}
_setTemp(_desiredtemp); // Different models have different temp values.
}
// Return the temp. offset in deg C for the current model.
int8_t IRWhirlpoolAc::getTempOffset() {
switch (getModel()) {
case DG11J191:
return -2;
break;
default:
return 0;
}
}
// Set the temp. in deg C
void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) {
if (remember) _desiredtemp = temp;
int8_t offset = getTempOffset(); // Cache the min temp for the model.
uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp);
newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp);
remote_state[kWhirlpoolAcTempPos] =
(remote_state[kWhirlpoolAcTempPos] & ~kWhirlpoolAcTempMask) |
((newtemp - (kWhirlpoolAcMinTemp + offset)) << 4);
}
// Set the temp. in deg C
void IRWhirlpoolAc::setTemp(const uint8_t temp) {
_setTemp(temp);
setSuper(false); // Changing temp cancels Super/Jet mode.
setCommand(kWhirlpoolAcCommandTemp);
}
// Return the set temp. in deg C
uint8_t IRWhirlpoolAc::getTemp() {
return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) +
+ kWhirlpoolAcMinTemp + getTempOffset();
}
void IRWhirlpoolAc::_setMode(const uint8_t mode) {
switch (mode) {
case kWhirlpoolAcAuto:
setFan(kWhirlpoolAcFanAuto);
_setTemp(kWhirlpoolAcAutoTemp, false);
setSleep(false); // Cancel sleep mode when in auto/6thsense mode.
// FALL THRU
case kWhirlpoolAcHeat:
case kWhirlpoolAcCool:
case kWhirlpoolAcDry:
case kWhirlpoolAcFan:
remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask;
remote_state[kWhirlpoolAcModePos] |= mode;
setCommand(kWhirlpoolAcCommandMode);
break;
default:
return;
}
if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense);
}
void IRWhirlpoolAc::setMode(const uint8_t mode) {
setSuper(false); // Changing mode cancels Super/Jet mode.
_setMode(mode);
}
uint8_t IRWhirlpoolAc::getMode() {
return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask;
}
void IRWhirlpoolAc::setFan(const uint8_t speed) {
switch (speed) {
case kWhirlpoolAcFanAuto:
case kWhirlpoolAcFanLow:
case kWhirlpoolAcFanMedium:
case kWhirlpoolAcFanHigh:
remote_state[kWhirlpoolAcFanPos] =
(remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed;
setSuper(false); // Changing fan speed cancels Super/Jet mode.
setCommand(kWhirlpoolAcCommandFanSpeed);
break;
}
}
uint8_t IRWhirlpoolAc::getFan() {
return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask;
}
void IRWhirlpoolAc::setSwing(const bool on) {
if (on) {
remote_state[kWhirlpoolAcFanPos] |= kWhirlpoolAcSwing1Mask;
remote_state[kWhirlpoolAcOffTimerPos] |= kWhirlpoolAcSwing2Mask;
} else {
remote_state[kWhirlpoolAcFanPos] &= ~kWhirlpoolAcSwing1Mask;
remote_state[kWhirlpoolAcOffTimerPos] &= ~kWhirlpoolAcSwing2Mask;
}
setCommand(kWhirlpoolAcCommandSwing);
}
bool IRWhirlpoolAc::getSwing() {
return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) &&
(remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask);
}
void IRWhirlpoolAc::setLight(const bool on) {
if (on)
remote_state[kWhirlpoolAcClockPos] &= ~kWhirlpoolAcLightMask;
else
remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask;
}
bool IRWhirlpoolAc::getLight() {
return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask);
}
void IRWhirlpoolAc::setTime(const uint16_t pos,
const uint16_t minspastmidnight) {
// Hours
remote_state[pos] &= ~kWhirlpoolAcHourMask;
remote_state[pos] |= (minspastmidnight / 60) % 24;
// Minutes
remote_state[pos + 1] &= ~kWhirlpoolAcMinuteMask;
remote_state[pos + 1] |= minspastmidnight % 60;
}
uint16_t IRWhirlpoolAc::getTime(const uint16_t pos) {
return (remote_state[pos] & kWhirlpoolAcHourMask) * 60 +
(remote_state[pos + 1] & kWhirlpoolAcMinuteMask);
}
bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) {
return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask;
}
void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) {
if (state)
remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask;
else
remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask;
}
void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) {
setTime(kWhirlpoolAcClockPos, minspastmidnight);
}
uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); }
void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) {
setTime(kWhirlpoolAcOffTimerPos, minspastmidnight);
}
uint16_t IRWhirlpoolAc::getOffTimer() {
return getTime(kWhirlpoolAcOffTimerPos);
}
bool IRWhirlpoolAc::isOffTimerEnabled() {
return isTimerEnabled(kWhirlpoolAcOffTimerPos);
}
void IRWhirlpoolAc::enableOffTimer(const bool state) {
enableTimer(kWhirlpoolAcOffTimerPos, state);
setCommand(kWhirlpoolAcCommandOffTimer);
}
void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) {
setTime(kWhirlpoolAcOnTimerPos, minspastmidnight);
}
uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); }
bool IRWhirlpoolAc::isOnTimerEnabled() {
return isTimerEnabled(kWhirlpoolAcOnTimerPos);
}
void IRWhirlpoolAc::enableOnTimer(const bool state) {
enableTimer(kWhirlpoolAcOnTimerPos, state);
setCommand(kWhirlpoolAcCommandOnTimer);
}
void IRWhirlpoolAc::setPowerToggle(const bool on) {
if (on)
remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask;
else
remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask;
setSuper(false); // Changing power cancels Super/Jet mode.
setCommand(kWhirlpoolAcCommandPower);
}
bool IRWhirlpoolAc::getPowerToggle() {
return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask;
}
uint8_t IRWhirlpoolAc::getCommand() {
return remote_state[kWhirlpoolAcCommandPos];
}
void IRWhirlpoolAc::setSleep(const bool on) {
if (on) {
remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask;
setFan(kWhirlpoolAcFanLow);
} else {
remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask;
}
setCommand(kWhirlpoolAcCommandSleep);
}
bool IRWhirlpoolAc::getSleep() {
return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask;
}
// AKA Jet/Turbo mode.
void IRWhirlpoolAc::setSuper(const bool on) {
if (on) {
setFan(kWhirlpoolAcFanHigh);
switch (getMode()) {
case kWhirlpoolAcHeat:
setTemp(kWhirlpoolAcMaxTemp + getTempOffset());
break;
case kWhirlpoolAcCool:
default:
setTemp(kWhirlpoolAcMinTemp + getTempOffset());
setMode(kWhirlpoolAcCool);
break;
}
remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask;
} else {
remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask;
}
setCommand(kWhirlpoolAcCommandSuper);
}
bool IRWhirlpoolAc::getSuper() {
return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask;
}
void IRWhirlpoolAc::setCommand(const uint8_t code) {
remote_state[kWhirlpoolAcCommandPos] = code;
}
#ifdef ARDUINO
String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) {
String result = "";
#else
std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) {
std::string result = "";
#endif // ARDUINO
uint8_t hours = minspastmidnight / 60;
if (hours < 10) result += "0";
result += uint64ToString(hours);
result += ":";
uint8_t mins = minspastmidnight % 60;
if (mins < 10) result += "0";
result += uint64ToString(mins);
return result;
}
// Convert the internal state into a human readable string.
#ifdef ARDUINO
String IRWhirlpoolAc::toString() {
String result = "";
#else
std::string IRWhirlpoolAc::toString() {
std::string result = "";
#endif // ARDUINO
result += "Model: " + uint64ToString(getModel());
switch (getModel()) {
case DG11J191:
result += " (DG11J191)";
break;
case DG11J13A:
result += " (DG11J13A)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Power toggle: ";
if (getPowerToggle())
result += "On";
else
result += "Off";
result += ", Mode: " + uint64ToString(getMode());
switch (getMode()) {
case kWhirlpoolAcHeat:
result += " (HEAT)";
break;
case kWhirlpoolAcAuto:
result += " (AUTO)";
break;
case kWhirlpoolAcCool:
result += " (COOL)";
break;
case kWhirlpoolAcDry:
result += " (DRY)";
break;
case kWhirlpoolAcFan:
result += " (FAN)";
break;
default:
result += " (UNKNOWN)";
}
result += ", Temp: " + uint64ToString(getTemp()) + "C";
result += ", Fan: " + uint64ToString(getFan());
switch (getFan()) {
case kWhirlpoolAcFanAuto:
result += " (AUTO)";
break;
case kWhirlpoolAcFanHigh:
result += " (HIGH)";
break;
case kWhirlpoolAcFanMedium:
result += " (MEDIUM)";
break;
case kWhirlpoolAcFanLow:
result += " (LOW)";
break;
default:
result += " (UNKNOWN)";
break;
}
result += ", Swing: ";
if (getSwing())
result += "On";
else
result += "Off";
result += ", Light: ";
if (getLight())
result += "On";
else
result += "Off";
result += ", Clock: ";
result += timeToString(getClock());
result += ", On Timer: ";
if (isOnTimerEnabled())
result += timeToString(getOnTimer());
else
result += "Off";
result += ", Off Timer: ";
if (isOffTimerEnabled())
result += timeToString(getOffTimer());
else
result += "Off";
result += ", Sleep: ";
if (getSleep())
result += "On";
else
result += "Off";
result += ", Super: ";
if (getSuper())
result += "On";
else
result += "Off";
result += ", Command: " + uint64ToString(getCommand());
switch (getCommand()) {
case kWhirlpoolAcCommandLight:
result += " (LIGHT)";
break;
case kWhirlpoolAcCommandPower:
result += " (POWER)";
break;
case kWhirlpoolAcCommandTemp:
result += " (TEMP)";
break;
case kWhirlpoolAcCommandSleep:
result += " (SLEEP)";
break;
case kWhirlpoolAcCommandSuper:
result += " (SUPER)";
break;
case kWhirlpoolAcCommandOnTimer:
result += " (ONTIMER)";
break;
case kWhirlpoolAcCommandMode:
result += " (MODE)";
break;
case kWhirlpoolAcCommandSwing:
result += " (SWING)";
break;
case kWhirlpoolAcCommandIFeel:
result += " (IFEEL)";
break;
case kWhirlpoolAcCommandFanSpeed:
result += " (FANSPEED)";
break;
case kWhirlpoolAcCommand6thSense:
result += " (6THSENSE)";
break;
case kWhirlpoolAcCommandOffTimer:
result += " (OFFTIMER)";
break;
default:
result += " (UNKNOWN)";
break;
}
return result;
}
#if DECODE_WHIRLPOOL_AC
// Decode the supplied Whirlpool A/C 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 kWhirlpoolAcBits
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: STABLE / Working as intended.
//
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/509
bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1)
return false; // Can't possibly be a valid Whirlpool A/C message.
if (strict) {
if (nbits != kWhirlpoolAcBits) return false;
}
uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0;
uint16_t i = 0;
match_result_t data_result;
uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7};
// Header
if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace))
return false;
// Data Section
// Keep reading bytes until we either run out of section or state to fill.
for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections;
section++) {
pos += sectionSize[section];
for (; offset <= results->rawlen - 16 && i < pos;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result =
matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark,
kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark,
kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false);
if (data_result.success == false) break; // Fail
// Data is in LSB order. We need to reverse it.
results->state[i] = (uint8_t)data_result.data;
}
// Section Footer
if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark))
return false;
if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps.
if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false;
} else { // Last section / End of message gap.
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap))
return false;
}
}
// Compliance
if (strict) {
// Re-check we got the correct size/length due to the way we read the data.
if (dataBitsSoFar != kWhirlpoolAcBits) return false;
if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8))
return false;
}
// Success
results->decode_type = WHIRLPOOL_AC;
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 // WHIRLPOOL_AC
+161
View File
@@ -0,0 +1,161 @@
// Whirlpool A/C
//
// Copyright 2018 David Conran
#ifndef IR_WHIRLPOOL_H_
#define IR_WHIRLPOOL_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#else
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL
// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL
// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL
// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL
// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/509
// Constants
const uint8_t kWhirlpoolAcChecksumByte1 = 13;
const uint8_t kWhirlpoolAcChecksumByte2 = kWhirlpoolAcStateLength - 1;
const uint8_t kWhirlpoolAcHeat = 0;
const uint8_t kWhirlpoolAcAuto = 1;
const uint8_t kWhirlpoolAcCool = 2;
const uint8_t kWhirlpoolAcDry = 3;
const uint8_t kWhirlpoolAcFan = 4;
const uint8_t kWhirlpoolAcModeMask = 0b00000111;
const uint8_t kWhirlpoolAcModePos = 3;
const uint8_t kWhirlpoolAcFanAuto = 0;
const uint8_t kWhirlpoolAcFanHigh = 1;
const uint8_t kWhirlpoolAcFanMedium = 2;
const uint8_t kWhirlpoolAcFanLow = 3;
const uint8_t kWhirlpoolAcFanMask = 0b00000011;
const uint8_t kWhirlpoolAcFanPos = 2;
const uint8_t kWhirlpoolAcMinTemp = 18; // 18C (DG11J1-3A), 16C (DG11J1-91)
const uint8_t kWhirlpoolAcMaxTemp = 32; // 32C (DG11J1-3A), 30C (DG11J1-91)
const uint8_t kWhirlpoolAcAutoTemp = 23; // 23C
const uint8_t kWhirlpoolAcTempMask = 0b11110000;
const uint8_t kWhirlpoolAcTempPos = 3;
const uint8_t kWhirlpoolAcSwing1Mask = 0b10000000;
const uint8_t kWhirlpoolAcSwing2Mask = 0b01000000;
const uint8_t kWhirlpoolAcLightMask = 0b00100000;
const uint8_t kWhirlpoolAcPowerToggleMask = 0b00000100;
const uint8_t kWhirlpoolAcPowerTogglePos = 2;
const uint8_t kWhirlpoolAcSleepMask = 0b00001000;
const uint8_t kWhirlpoolAcSleepPos = 2;
const uint8_t kWhirlpoolAcSuperMask = 0b10010000;
const uint8_t kWhirlpoolAcSuperPos = 5;
const uint8_t kWhirlpoolAcHourMask = 0b00011111;
const uint8_t kWhirlpoolAcMinuteMask = 0b00111111;
const uint8_t kWhirlpoolAcTimerEnableMask = 0b10000000;
const uint8_t kWhirlpoolAcClockPos = 6;
const uint8_t kWhirlpoolAcOffTimerPos = 8;
const uint8_t kWhirlpoolAcOnTimerPos = 10;
const uint8_t kWhirlpoolAcCommandPos = 15;
const uint8_t kWhirlpoolAcCommandLight = 0x00;
const uint8_t kWhirlpoolAcCommandPower = 0x01;
const uint8_t kWhirlpoolAcCommandTemp = 0x02;
const uint8_t kWhirlpoolAcCommandSleep = 0x03;
const uint8_t kWhirlpoolAcCommandSuper = 0x04;
const uint8_t kWhirlpoolAcCommandOnTimer = 0x05;
const uint8_t kWhirlpoolAcCommandMode = 0x06;
const uint8_t kWhirlpoolAcCommandSwing = 0x07;
const uint8_t kWhirlpoolAcCommandIFeel = 0x0D;
const uint8_t kWhirlpoolAcCommandFanSpeed = 0x11;
const uint8_t kWhirlpoolAcCommand6thSense = 0x17;
const uint8_t kWhirlpoolAcCommandOffTimer = 0x1D;
const uint8_t kWhirlpoolAcAltTempMask = 0b00001000;
const uint8_t kWhirlpoolAcAltTempPos = 18;
enum whirlpool_ac_remote_model_t {
DG11J13A = 1, // DG11J1-04 too
DG11J191,
};
// Classes
class IRWhirlpoolAc {
public:
explicit IRWhirlpoolAc(uint16_t pin);
void stateReset();
#if SEND_WHIRLPOOL_AC
void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat,
const bool calcchecksum = true);
#endif // SEND_WHIRLPOOL_AC
void begin();
void on();
void off();
void setPowerToggle(const bool on);
bool getPowerToggle();
void setSleep(const bool on);
bool getSleep();
void setSuper(const bool on);
bool getSuper();
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t speed);
uint8_t getFan();
void setMode(const uint8_t mode);
uint8_t getMode();
void setSwing(const bool on);
bool getSwing();
void setLight(const bool on);
bool getLight();
uint16_t getClock();
void setClock(const uint16_t minspastmidnight);
uint16_t getOnTimer();
void setOnTimer(const uint16_t minspastmidnight);
void enableOnTimer(const bool state);
bool isOnTimerEnabled();
uint16_t getOffTimer();
void setOffTimer(const uint16_t minspastmidnight);
void enableOffTimer(const bool state);
bool isOffTimerEnabled();
void setCommand(const uint8_t code);
uint8_t getCommand();
whirlpool_ac_remote_model_t getModel();
void setModel(const whirlpool_ac_remote_model_t model);
uint8_t* getRaw(const bool calcchecksum = true);
void setRaw(const uint8_t new_code[],
const uint16_t length = kWhirlpoolAcStateLength);
static bool validChecksum(uint8_t state[],
const uint16_t length = kWhirlpoolAcStateLength);
#ifdef ARDUINO
String toString();
#else
std::string toString();
#endif
#ifndef UNIT_TEST
private:
#endif
// The state of the IR remote in IR code form.
uint8_t remote_state[kWhirlpoolAcStateLength];
IRsend _irsend;
uint8_t _desiredtemp;
void checksum(const uint16_t length = kWhirlpoolAcStateLength);
uint16_t getTime(const uint16_t pos);
void setTime(const uint16_t pos, const uint16_t minspastmidnight);
bool isTimerEnabled(const uint16_t pos);
void enableTimer(const uint16_t pos, const bool state);
void _setTemp(const uint8_t temp, const bool remember = true);
void _setMode(const uint8_t mode);
int8_t getTempOffset();
#ifdef ARDUINO
String timeToString(uint16_t minspastmidnight);
#else
std::string timeToString(uint16_t minspastmidnight);
#endif
};
#endif // IR_WHIRLPOOL_H_
+43 -48
View File
@@ -16,27 +16,26 @@
// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ // Whynter originally added from https://github.com/shirriff/Arduino-IRremote/
// Constants // Constants
#define WHYNTER_TICK 50U
#define WHYNTER_HDR_MARK_TICKS 57U const uint16_t kWhynterTick = 50;
#define WHYNTER_HDR_MARK (WHYNTER_HDR_MARK_TICKS * WHYNTER_TICK) const uint16_t kWhynterHdrMarkTicks = 57;
#define WHYNTER_HDR_SPACE_TICKS 57U const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick;
#define WHYNTER_HDR_SPACE (WHYNTER_HDR_SPACE_TICKS * \ const uint16_t kWhynterHdrSpaceTicks = 57;
WHYNTER_TICK) const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick;
#define WHYNTER_BIT_MARK_TICKS 15U const uint16_t kWhynterBitMarkTicks = 15;
#define WHYNTER_BIT_MARK (WHYNTER_BIT_MARK_TICKS * WHYNTER_TICK) const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick;
#define WHYNTER_ONE_SPACE_TICKS 43U const uint16_t kWhynterOneSpaceTicks = 43;
#define WHYNTER_ONE_SPACE (WHYNTER_ONE_SPACE_TICKS * \ const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick;
WHYNTER_TICK) const uint16_t kWhynterZeroSpaceTicks = 15;
#define WHYNTER_ZERO_SPACE_TICKS 15U const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick;
#define WHYNTER_ZERO_SPACE (WHYNTER_ZERO_SPACE_TICKS * \ const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value.
WHYNTER_TICK) const uint32_t kWhynterMinCommandLength =
#define WHYNTER_MIN_COMMAND_LENGTH_TICKS 2160U // Completely made up value. kWhynterMinCommandLengthTicks * kWhynterTick;
#define WHYNTER_MIN_COMMAND_LENGTH (WHYNTER_MIN_COMMAND_LENGTH_TICKS * \ const uint16_t kWhynterMinGapTicks =
WHYNTER_TICK) kWhynterMinCommandLengthTicks -
#define WHYNTER_MIN_GAP_TICKS (WHYNTER_MIN_COMMAND_LENGTH_TICKS - \ (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) +
(2 * (WHYNTER_BIT_MARK_TICKS + WHYNTER_ZERO_SPACE_TICKS) + \ kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks));
WHYNTER_BITS * (WHYNTER_BIT_MARK_TICKS + WHYNTER_ONE_SPACE_TICKS))) const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick;
#define WHYNTER_MIN_GAP (WHYNTER_MIN_GAP_TICKS * WHYNTER_TICK)
#if SEND_WHYNTER #if SEND_WHYNTER
// Send a Whynter message. // Send a Whynter message.
@@ -56,15 +55,13 @@ void IRsend::sendWhynter(uint64_t data, uint16_t nbits, uint16_t repeat) {
for (uint16_t i = 0; i <= repeat; i++) { for (uint16_t i = 0; i <= repeat; i++) {
// (Pre-)Header // (Pre-)Header
mark(WHYNTER_BIT_MARK); mark(kWhynterBitMark);
space(WHYNTER_ZERO_SPACE); space(kWhynterZeroSpace);
sendGeneric(WHYNTER_HDR_MARK, WHYNTER_HDR_SPACE, sendGeneric(
WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace,
WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap,
WHYNTER_BIT_MARK, WHYNTER_MIN_GAP, kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data,
WHYNTER_MIN_COMMAND_LENGTH - (WHYNTER_BIT_MARK + nbits, 38, true, 0, // Repeats are already handled.
WHYNTER_ZERO_SPACE),
data, nbits, 38, true, 0, // Repeats are already handled.
50); 50);
} }
} }
@@ -86,47 +83,45 @@ void IRsend::sendWhynter(uint64_t data, uint16_t nbits, uint16_t repeat) {
// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp // https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp
bool IRrecv::decodeWhynter(decode_results *results, uint16_t nbits, bool IRrecv::decodeWhynter(decode_results *results, uint16_t nbits,
bool strict) { bool strict) {
if (results->rawlen < 2 * nbits + 2 * HEADER + FOOTER - 1) if (results->rawlen < 2 * nbits + 2 * kHeader + kFooter - 1)
return false; // We don't have enough entries to possibly match. return false; // We don't have enough entries to possibly match.
// Compliance // Compliance
if (strict && nbits != WHYNTER_BITS) if (strict && nbits != kWhynterBits)
return false; // Incorrect nr. of bits per spec. return false; // Incorrect nr. of bits per spec.
uint16_t offset = OFFSET_START; uint16_t offset = kStartOffset;
// Header // Header
// Sequence begins with a bit mark and a zero space. // Sequence begins with a bit mark and a zero space.
// These are typically small, so we'll prefer to do the calibration // These are typically small, so we'll prefer to do the calibration
// on the much larger header mark & space that are next. // on the much larger header mark & space that are next.
if (!matchMark(results->rawbuf[offset++], WHYNTER_BIT_MARK)) return false; if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false;
if (!matchSpace(results->rawbuf[offset++], WHYNTER_ZERO_SPACE)) return false; if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false;
// Main header mark and space // Main header mark and space
if (!matchMark(results->rawbuf[offset], WHYNTER_HDR_MARK)) return false; if (!matchMark(results->rawbuf[offset], kWhynterHdrMark)) return false;
// Calculate how long the common tick time is based on the header mark. // Calculate how long the common tick time is based on the header mark.
uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kWhynterHdrMarkTicks;
WHYNTER_HDR_MARK_TICKS; if (!matchSpace(results->rawbuf[offset], kWhynterHdrSpace)) return false;
if (!matchSpace(results->rawbuf[offset], WHYNTER_HDR_SPACE)) return false;
// Calculate how long the common tick time is based on the header space. // Calculate how long the common tick time is based on the header space.
uint32_t s_tick = results->rawbuf[offset++] * RAWTICK / uint32_t s_tick =
WHYNTER_HDR_SPACE_TICKS; results->rawbuf[offset++] * kRawTick / kWhynterHdrSpaceTicks;
// Data // Data
uint64_t data = 0; uint64_t data = 0;
match_result_t data_result = matchData(&(results->rawbuf[offset]), nbits, match_result_t data_result =
WHYNTER_BIT_MARK_TICKS * m_tick, matchData(&(results->rawbuf[offset]), nbits,
WHYNTER_ONE_SPACE_TICKS * s_tick, kWhynterBitMarkTicks * m_tick, kWhynterOneSpaceTicks * s_tick,
WHYNTER_BIT_MARK_TICKS * m_tick, kWhynterBitMarkTicks * m_tick, kWhynterZeroSpaceTicks * s_tick);
WHYNTER_ZERO_SPACE_TICKS * s_tick);
if (data_result.success == false) return false; if (data_result.success == false) return false;
data = data_result.data; data = data_result.data;
offset += data_result.used; offset += data_result.used;
// Footer // Footer
if (!matchMark(results->rawbuf[offset++], WHYNTER_BIT_MARK_TICKS * m_tick)) if (!matchMark(results->rawbuf[offset++], kWhynterBitMarkTicks * m_tick))
return false; return false;
if (offset < results->rawlen && if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset], WHYNTER_MIN_GAP_TICKS * s_tick)) !matchAtLeast(results->rawbuf[offset], kWhynterMinGapTicks * s_tick))
return false; return false;
// Success // Success
+55 -68
View File
@@ -1,8 +1,8 @@
// Copyright 2017 David Conran // Copyright 2017 David Conran
#include "IRrecv_test.h" #include "IRrecv_test.h"
#include "IRremoteESP8266.h"
#include "IRrecv.h" #include "IRrecv.h"
#include "IRremoteESP8266.h"
#include "IRsend.h" #include "IRsend.h"
#include "IRsend_test.h" #include "IRsend_test.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@@ -10,7 +10,7 @@
// Tests for the IRrecv object. // Tests for the IRrecv object.
TEST(TestIRrecv, DefaultBufferSize) { TEST(TestIRrecv, DefaultBufferSize) {
IRrecv irrecv_default(1); IRrecv irrecv_default(1);
EXPECT_EQ(RAWBUF, irrecv_default.getBufSize()); EXPECT_EQ(kRawBuf, irrecv_default.getBufSize());
} }
TEST(TestIRrecv, LargeBufferSize) { TEST(TestIRrecv, LargeBufferSize) {
@@ -30,7 +30,7 @@ TEST(TestIRrecv, MediumBufferSize) {
TEST(TestIRrecv, IRrecvDestructor) { TEST(TestIRrecv, IRrecvDestructor) {
IRrecv *irrecv_ptr = new IRrecv(1); IRrecv *irrecv_ptr = new IRrecv(1);
EXPECT_EQ(RAWBUF, irrecv_ptr->getBufSize()); EXPECT_EQ(kRawBuf, irrecv_ptr->getBufSize());
delete irrecv_ptr; delete irrecv_ptr;
irrecv_ptr = new IRrecv(1, 1234); irrecv_ptr = new IRrecv(1, 1234);
@@ -120,7 +120,7 @@ TEST(TestDecode, DecodeNEC) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC, irsend.capture.decode_type);
EXPECT_EQ(NEC_BITS, irsend.capture.bits); EXPECT_EQ(kNECBits, irsend.capture.bits);
EXPECT_EQ(0x807F40BF, irsend.capture.value); EXPECT_EQ(0x807F40BF, irsend.capture.value);
} }
@@ -134,7 +134,7 @@ TEST(TestDecode, DecodeJVC) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(JVC, irsend.capture.decode_type); EXPECT_EQ(JVC, irsend.capture.decode_type);
EXPECT_EQ(JVC_BITS, irsend.capture.bits); EXPECT_EQ(kJvcBits, irsend.capture.bits);
EXPECT_EQ(0xC2B8, irsend.capture.value); EXPECT_EQ(0xC2B8, irsend.capture.value);
} }
@@ -148,15 +148,15 @@ TEST(TestDecode, DecodeLG) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(LG, irsend.capture.decode_type); EXPECT_EQ(LG, irsend.capture.decode_type);
EXPECT_EQ(LG_BITS, irsend.capture.bits); EXPECT_EQ(kLgBits, irsend.capture.bits);
EXPECT_EQ(0x4B4AE51, irsend.capture.value); EXPECT_EQ(0x4B4AE51, irsend.capture.value);
irsend.reset(); irsend.reset();
irsend.sendLG(0xB4B4AE51, LG32_BITS); irsend.sendLG(0xB4B4AE51, kLg32Bits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(LG, irsend.capture.decode_type); EXPECT_EQ(LG, irsend.capture.decode_type);
EXPECT_EQ(LG32_BITS, irsend.capture.bits); EXPECT_EQ(kLg32Bits, irsend.capture.bits);
EXPECT_EQ(0xB4B4AE51, irsend.capture.value); EXPECT_EQ(0xB4B4AE51, irsend.capture.value);
} }
@@ -168,9 +168,9 @@ TEST(TestDecode, DecodePanasonic) {
irsend.reset(); irsend.reset();
irsend.sendPanasonic64(0x40040190ED7C); irsend.sendPanasonic64(0x40040190ED7C);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, PANASONIC_BITS, true)); ASSERT_TRUE(irrecv.decodePanasonic(&irsend.capture, kPanasonicBits, true));
EXPECT_EQ(PANASONIC, irsend.capture.decode_type); EXPECT_EQ(PANASONIC, irsend.capture.decode_type);
EXPECT_EQ(PANASONIC_BITS, irsend.capture.bits); EXPECT_EQ(kPanasonicBits, irsend.capture.bits);
EXPECT_EQ(0x40040190ED7C, irsend.capture.value); EXPECT_EQ(0x40040190ED7C, irsend.capture.value);
} }
@@ -184,7 +184,7 @@ TEST(TestDecode, DecodeSamsung) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SAMSUNG, irsend.capture.decode_type); EXPECT_EQ(SAMSUNG, irsend.capture.decode_type);
EXPECT_EQ(SAMSUNG_BITS, irsend.capture.bits); EXPECT_EQ(kSamsungBits, irsend.capture.bits);
EXPECT_EQ(0xE0E09966, irsend.capture.value); EXPECT_EQ(0xE0E09966, irsend.capture.value);
} }
@@ -199,7 +199,7 @@ TEST(TestDecode, DecodeSherwood) {
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
// Sherwood codes are really NEC codes. // Sherwood codes are really NEC codes.
EXPECT_EQ(NEC, irsend.capture.decode_type); EXPECT_EQ(NEC, irsend.capture.decode_type);
EXPECT_EQ(NEC_BITS, irsend.capture.bits); EXPECT_EQ(kNECBits, irsend.capture.bits);
EXPECT_EQ(0x807F40BF, irsend.capture.value); EXPECT_EQ(0x807F40BF, irsend.capture.value);
} }
@@ -213,7 +213,7 @@ TEST(TestDecode, DecodeWhynter) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(WHYNTER, irsend.capture.decode_type); EXPECT_EQ(WHYNTER, irsend.capture.decode_type);
EXPECT_EQ(WHYNTER_BITS, irsend.capture.bits); EXPECT_EQ(kWhynterBits, irsend.capture.bits);
EXPECT_EQ(0x87654321, irsend.capture.value); EXPECT_EQ(0x87654321, irsend.capture.value);
} }
@@ -225,30 +225,29 @@ TEST(TestDecode, DecodeSony) {
// Synthesised Normal Sony 20-bit message. // Synthesised Normal Sony 20-bit message.
irsend.reset(); irsend.reset();
irsend.sendSony(irsend.encodeSony(SONY_20_BITS, 0x1, 0x1, 0x1)); irsend.sendSony(irsend.encodeSony(kSony20Bits, 0x1, 0x1, 0x1));
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(SONY, irsend.capture.decode_type);
EXPECT_EQ(SONY_20_BITS, irsend.capture.bits); EXPECT_EQ(kSony20Bits, irsend.capture.bits);
EXPECT_EQ(0x81080, irsend.capture.value); EXPECT_EQ(0x81080, irsend.capture.value);
// Synthesised Normal Sony 15-bit message. // Synthesised Normal Sony 15-bit message.
irsend.reset(); irsend.reset();
irsend.sendSony(irsend.encodeSony(SONY_15_BITS, 21, 1), SONY_15_BITS); irsend.sendSony(irsend.encodeSony(kSony15Bits, 21, 1), kSony15Bits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(SONY, irsend.capture.decode_type);
EXPECT_EQ(SONY_15_BITS, irsend.capture.bits); EXPECT_EQ(kSony15Bits, irsend.capture.bits);
EXPECT_EQ(0x5480, irsend.capture.value); EXPECT_EQ(0x5480, irsend.capture.value);
// Synthesised Normal Sony 12-bit message. // Synthesised Normal Sony 12-bit message.
irsend.reset(); irsend.reset();
irsend.sendSony(irsend.encodeSony(SONY_12_BITS, 21, 1), SONY_12_BITS); irsend.sendSony(irsend.encodeSony(kSony12Bits, 21, 1), kSony12Bits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SONY, irsend.capture.decode_type); EXPECT_EQ(SONY, irsend.capture.decode_type);
EXPECT_EQ(SONY_12_BITS, irsend.capture.bits); EXPECT_EQ(kSony12Bits, irsend.capture.bits);
EXPECT_EQ(0xA90, irsend.capture.value); EXPECT_EQ(0xA90, irsend.capture.value);
} }
@@ -262,7 +261,7 @@ TEST(TestDecode, DecodeSharp) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SHARP, irsend.capture.decode_type); EXPECT_EQ(SHARP, irsend.capture.decode_type);
EXPECT_EQ(SHARP_BITS, irsend.capture.bits); EXPECT_EQ(kSharpBits, irsend.capture.bits);
EXPECT_EQ(0x454A, irsend.capture.value); EXPECT_EQ(0x454A, irsend.capture.value);
} }
@@ -276,7 +275,7 @@ TEST(TestDecode, DecodeSanyo) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type); EXPECT_EQ(SANYO_LC7461, irsend.capture.decode_type);
EXPECT_EQ(SANYO_LC7461_BITS, irsend.capture.bits); EXPECT_EQ(kSanyoLC7461Bits, irsend.capture.bits);
EXPECT_EQ(0x2468DCB56A9, irsend.capture.value); EXPECT_EQ(0x2468DCB56A9, irsend.capture.value);
} }
@@ -292,7 +291,7 @@ TEST(TestDecode, DecodeRCMM) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RCMM, irsend.capture.decode_type); EXPECT_EQ(RCMM, irsend.capture.decode_type);
EXPECT_EQ(RCMM_BITS, irsend.capture.bits); EXPECT_EQ(kRCMMBits, irsend.capture.bits);
EXPECT_EQ(0xe0a600, irsend.capture.value); EXPECT_EQ(0xe0a600, irsend.capture.value);
// Normal RCMM 12-bit message. // Normal RCMM 12-bit message.
@@ -324,7 +323,7 @@ TEST(TestDecode, DecodeMitsubishi) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type); EXPECT_EQ(MITSUBISHI, irsend.capture.decode_type);
EXPECT_EQ(MITSUBISHI_BITS, irsend.capture.bits); EXPECT_EQ(kMitsubishiBits, irsend.capture.bits);
EXPECT_EQ(0xC2B8, irsend.capture.value); EXPECT_EQ(0xC2B8, irsend.capture.value);
} }
@@ -339,15 +338,15 @@ TEST(TestDecode, DecodeRC5) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RC5, irsend.capture.decode_type); EXPECT_EQ(RC5, irsend.capture.decode_type);
EXPECT_EQ(RC5_BITS, irsend.capture.bits); EXPECT_EQ(kRC5Bits, irsend.capture.bits);
EXPECT_EQ(0x175, irsend.capture.value); EXPECT_EQ(0x175, irsend.capture.value);
// Synthesised Normal RC-5X 13-bit message. // Synthesised Normal RC-5X 13-bit message.
irsend.reset(); irsend.reset();
irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), RC5X_BITS); irsend.sendRC5(irsend.encodeRC5X(0x02, 0x41, true), kRC5XBits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RC5X, irsend.capture.decode_type); EXPECT_EQ(RC5X, irsend.capture.decode_type);
EXPECT_EQ(RC5X_BITS, irsend.capture.bits); EXPECT_EQ(kRC5XBits, irsend.capture.bits);
EXPECT_EQ(0x1881, irsend.capture.value); EXPECT_EQ(0x1881, irsend.capture.value);
} }
@@ -362,16 +361,16 @@ TEST(TestDecode, DecodeRC6) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RC6, irsend.capture.decode_type); EXPECT_EQ(RC6, irsend.capture.decode_type);
EXPECT_EQ(RC6_MODE0_BITS, irsend.capture.bits); EXPECT_EQ(kRC6Mode0Bits, irsend.capture.bits);
EXPECT_EQ(0x175, irsend.capture.value); EXPECT_EQ(0x175, irsend.capture.value);
// Normal RC-6 36-bit message. // Normal RC-6 36-bit message.
irsend.reset(); irsend.reset();
irsend.sendRC6(0xC800F742A, RC6_36_BITS); irsend.sendRC6(0xC800F742A, kRC6_36Bits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(RC6, irsend.capture.decode_type); EXPECT_EQ(RC6, irsend.capture.decode_type);
EXPECT_EQ(RC6_36_BITS, irsend.capture.bits); EXPECT_EQ(kRC6_36Bits, irsend.capture.bits);
EXPECT_EQ(0xC800F742A, irsend.capture.value); EXPECT_EQ(0xC800F742A, irsend.capture.value);
} }
@@ -385,7 +384,7 @@ TEST(TestDecode, DecodeDish) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(DISH, irsend.capture.decode_type); EXPECT_EQ(DISH, irsend.capture.decode_type);
EXPECT_EQ(DISH_BITS, irsend.capture.bits); EXPECT_EQ(kDishBits, irsend.capture.bits);
EXPECT_EQ(0x9C00, irsend.capture.value); EXPECT_EQ(0x9C00, irsend.capture.value);
} }
@@ -404,7 +403,7 @@ TEST(TestDecode, DecodeDenon) {
EXPECT_EQ(0x2278, irsend.capture.value); EXPECT_EQ(0x2278, irsend.capture.value);
// Legacy Denon 14-bit message. // Legacy Denon 14-bit message.
irsend.reset(); irsend.reset();
irsend.sendDenon(0x1278, DENON_LEGACY_BITS); irsend.sendDenon(0x1278, kDenonLegacyBits);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(DENON, irsend.capture.decode_type); EXPECT_EQ(DENON, irsend.capture.decode_type);
@@ -430,7 +429,7 @@ TEST(TestDecode, DecodeCoolix) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(COOLIX, irsend.capture.decode_type); EXPECT_EQ(COOLIX, irsend.capture.decode_type);
EXPECT_EQ(COOLIX_BITS, irsend.capture.bits); EXPECT_EQ(kCoolixBits, irsend.capture.bits);
EXPECT_EQ(0x123456, irsend.capture.value); EXPECT_EQ(0x123456, irsend.capture.value);
} }
@@ -444,7 +443,7 @@ TEST(TestDecode, DecodeAiwa) {
irsend.makeDecodeResult(); irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture)); ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type); EXPECT_EQ(AIWA_RC_T501, irsend.capture.decode_type);
EXPECT_EQ(AIWA_RC_T501_BITS, irsend.capture.bits); EXPECT_EQ(kAiwaRcT501Bits, irsend.capture.bits);
EXPECT_EQ(0x7F, irsend.capture.value); EXPECT_EQ(0x7F, irsend.capture.value);
} }
@@ -454,14 +453,8 @@ TEST(TestMatchData, SpaceEncoded) {
IRrecv irrecv(1); IRrecv irrecv(1);
irsend.begin(); irsend.begin();
uint16_t space_encoded_raw[11] = { uint16_t space_encoded_raw[11] = {500, 500, 500, 1500, 499, 499,
500, 500, 501, 1501, 499, 1490, 500};
500, 1500,
499, 499,
501, 1501,
499, 1490,
500
};
match_result_t result; match_result_t result;
irsend.reset(); irsend.reset();
@@ -485,29 +478,35 @@ TEST(TestMatchData, MarkEncoded) {
IRrecv irrecv(1); IRrecv irrecv(1);
irsend.begin(); irsend.begin();
uint16_t mark_encoded_raw[11] = { uint16_t mark_encoded_raw[11] = {500, 500, 1500, 500, 499, 499,
500, 500, 1501, 501, 1499, 490, 500};
1500, 500,
499, 499,
1501, 501,
1499, 490,
500
};
match_result_t result; match_result_t result;
irsend.reset(); irsend.reset();
irsend.sendRaw(mark_encoded_raw, 11, 38000); irsend.sendRaw(mark_encoded_raw, 11, 38000);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
// MSBF order.
result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500); result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500);
ASSERT_TRUE(result.success); ASSERT_TRUE(result.success);
EXPECT_EQ(0b01011, result.data); EXPECT_EQ(0b01011, result.data);
EXPECT_EQ(10, result.used); EXPECT_EQ(10, result.used);
// LSBF order.
result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1500, 500, 500, 500,
kTolerance, kMarkExcess, false);
ASSERT_TRUE(result.success);
EXPECT_EQ(0b11010, result.data); // Bits reversed of the previous test.
EXPECT_EQ(10, result.used);
irsend.reset(); irsend.reset();
irsend.sendRaw(mark_encoded_raw, 11, 38000); irsend.sendRaw(mark_encoded_raw, 11, 38000);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
// MSBF order.
result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500); result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500);
ASSERT_FALSE(result.success); ASSERT_FALSE(result.success);
// LSBF order.
result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, 1000, 500, 500, 500,
kTolerance, kMarkExcess, false);
ASSERT_FALSE(result.success);
} }
// Test matchData() on "equal total bit time" encoded data. // Test matchData() on "equal total bit time" encoded data.
@@ -516,14 +515,8 @@ TEST(TestMatchData, EqualTotalBitTimeEncoded) {
IRrecv irrecv(1); IRrecv irrecv(1);
irsend.begin(); irsend.begin();
uint16_t equal_encoded_raw[11] = { uint16_t equal_encoded_raw[11] = {500, 1500, 1500, 500, 499, 1499,
500, 1500, 1501, 501, 1499, 490, 500};
1500, 500,
499, 1499,
1501, 501,
1499, 490,
500
};
match_result_t result; match_result_t result;
irsend.reset(); irsend.reset();
@@ -547,21 +540,15 @@ TEST(TestMatchData, ArbitraryEncoded) {
IRrecv irrecv(1); IRrecv irrecv(1);
irsend.begin(); irsend.begin();
uint16_t arbitrary_encoded_raw[11] = { uint16_t arbitrary_encoded_raw[11] = {500, 1500, 3000, 1000, 499, 1499,
500, 1500, 3001, 1001, 2999, 990, 500};
3000, 1000,
499, 1499,
3001, 1001,
2999, 990,
500
};
match_result_t result; match_result_t result;
irsend.reset(); irsend.reset();
irsend.sendRaw(arbitrary_encoded_raw, 11, 38000); irsend.sendRaw(arbitrary_encoded_raw, 11, 38000);
irsend.makeDecodeResult(); irsend.makeDecodeResult();
result = irrecv.matchData(irsend.capture.rawbuf + 1, 5, result =
3000, 1000, 500, 1500); irrecv.matchData(irsend.capture.rawbuf + 1, 5, 3000, 1000, 500, 1500);
ASSERT_TRUE(result.success); ASSERT_TRUE(result.success);
EXPECT_EQ(0b01011, result.data); EXPECT_EQ(0b01011, result.data);
EXPECT_EQ(10, result.used); EXPECT_EQ(10, result.used);
Loaded 100 of 149 files, more files were not shown because too many files have changed in this diff. Show more