From ce13c0cf8ffbcd34407fdbd24e31e6bf778001d4 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 21 Jul 2019 18:06:13 +0200 Subject: [PATCH] Add support for optional IRHVAC Midea/Komeco protocol Add support for optional IRHVAC Midea/Komeco protocol (#3227) --- sonoff/_changelog.ino | 1 + sonoff/my_user_config.h | 1 + sonoff/xdrv_05_irremote.ino | 163 ++++++++++++++++++++++++++++++++---- 3 files changed, 150 insertions(+), 15 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 59831ab66..b93a6ee81 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -4,6 +4,7 @@ * Add support for a buzzer * Add command SetOption67 0/1 to disable or enable a buzzer as used in iFan03 * Add support IRSend long press ('repeat' feature from IRRemoteESP8266) (#6074) + * Add support for optional IRHVAC Midea/Komeco protocol (#3227) * * 6.6.0.1 20190708 * Fix Domoticz battery level set to 100 if define USE_ADC_VCC is not used (#6033) diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index a100f29ce..ac2c7e7e0 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -449,6 +449,7 @@ #define USE_IR_HVAC_MITSUBISHI // Support IRhvac Mitsubischi protocol #define USE_IR_HVAC_LG // Support IRhvac LG protocol #define USE_IR_HVAC_FUJITSU // Support IRhvac Fujitsu protocol +// #define USE_IR_HVAC_MIDEA // Support IRhvac Midea/Komeco protocol #define USE_IR_RECEIVE // Support for IR receiver (+7k2 code, 264 iram) #define IR_RCV_BUFFER_SIZE 100 // Max number of packets allowed in capture buffer (default 100 (*2 bytes ram)) diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 176f87157..208d4b097 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -40,21 +40,10 @@ const char kIrRemoteProtocols[] PROGMEM = #include #include -enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU }; -const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu" ; - -// HVAC TOSHIBA_ -const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; -const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; -const uint16_t HVAC_TOSHIBA_BIT_MARK = 543; -const uint16_t HVAC_TOSHIBA_ONE_SPACE = 1623; -const uint16_t HVAC_MISTUBISHI_ZERO_SPACE = 472; -const uint16_t HVAC_TOSHIBA_RPT_MARK = 440; -const uint16_t HVAC_TOSHIBA_RPT_SPACE = 7048; // Above original iremote limit -const uint8_t HVAC_TOSHIBA_DATALEN = 9; - -// HVAC LG -const uint8_t HVAC_LG_DATALEN = 7; +enum IrHvacVendors { + VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA }; +const char kIrHvacVendors[] PROGMEM = + "Toshiba|Mitsubishi|LG|Fujitsu|Midea" ; IRMitsubishiAC *mitsubir = nullptr; @@ -212,6 +201,15 @@ void IrReceiveCheck(void) TOSHIBA ********************/ +const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; +const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; +const uint16_t HVAC_TOSHIBA_BIT_MARK = 543; +const uint16_t HVAC_TOSHIBA_ONE_SPACE = 1623; +const uint16_t HVAC_MISTUBISHI_ZERO_SPACE = 472; +const uint16_t HVAC_TOSHIBA_RPT_MARK = 440; +const uint16_t HVAC_TOSHIBA_RPT_SPACE = 7048; // Above original iremote limit +const uint8_t HVAC_TOSHIBA_DATALEN = 9; + uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { uint16_t rawdata[2 + 2 * 8 * HVAC_TOSHIBA_DATALEN + 2]; @@ -303,6 +301,135 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC } #endif // USE_IR_HVAC_TOSHIBA +#ifdef USE_IR_HVAC_MIDEA +/******************* + MIDEA / KOMECO +********************/ + +// http://veillard.com/embedded/midea.html +// https://github.com/sheinz/esp-midea-ir/blob/master/midea-ir.c + +const uint16_t HVAC_MIDEA_HDR_MARK = 4420; // 8T high +const uint16_t HVAC_MIDEA_HDR_SPACE = 4420; // 8T low +const uint16_t HVAC_MIDEA_BIT_MARK = 553; // 1T +const uint16_t HVAC_MIDEA_ONE_SPACE = 1660; // 3T low +const uint16_t HVAC_MIDEA_ZERO_SPACE = 553; // 1T high +const uint16_t HVAC_MIDEA_RPT_MARK = 553; // 1T +const uint16_t HVAC_MIDEA_RPT_SPACE = 5530; // 10T +const uint8_t HVAC_MIDEA_DATALEN = 3; + +uint8_t IrHvacMidea(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) +{ + uint16_t rawdata[2 + 2 * 2 * 8 * HVAC_MIDEA_DATALEN + 2]; // START + 2* (2 * 3 BYTES) + STOP + uint8_t data[HVAC_MIDEA_DATALEN] = {0xB2, 0x00, 0x00}; + + char *p; + uint8_t mode; + + if (!HVAC_Power) { // Turn OFF HVAC + data[1] = 0x7B; + data[2] = 0xE0; + } else { + // FAN + if (HVAC_FanMode == nullptr) { + p = (char*)kFanSpeedOptions; // default auto + } + else { + p = (char*)HVAC_FanMode; + } + + switch(p[0]) { + case '1': data[1] = 0xBF; break; // off + case '2': data[1] = 0x9F; break; // low + case '3': data[1] = 0x5F; break; // med + case '4': data[1] = 0x3F; break; // high + case '5': data[1] = 0x1F; break; // auto + case 'A': data[1] = 0x1F; break; // auto + default: return IE_SYNTAX_IRHVAC; + } + + // TEMPERATURE + uint8_t Temp; + if (HVAC_Temp > 30) { + Temp = 30; + } + else if (HVAC_Temp < 17) { + Temp = 17; + } + else { + Temp = HVAC_Temp-17; + } + if (10 == Temp) { // Temp is encoded as gray code; except 27 and 28. Go figure... + data[2] = 0x90; + } else if (11 == Temp) { + data[2] = 0x80; + } else { + Temp = (Temp >> 1) ^Temp; + data[2] = (Temp << 4); + } + + // MODE + if (HVAC_Mode == nullptr) { + p = (char*)kHvacModeOptions + 3; // default to auto + } + else { + p = (char*)HVAC_Mode; + } + switch(toupper(p[0])) { + case 'D': data[2] = 0xE4; break; // for fan Temp must be 0XE + case 'C': data[2] = 0x0 | data[2]; break; + case 'A': data[2] = 0x8 | data[2]; data[1] = 0x1F; break; // for auto Fan must be 0x1 + case 'H': data[2] = 0xC | data[2]; break; + default: return IE_SYNTAX_IRHVAC; + } + } + + int i = 0; + uint8_t mask = 1; + + //header + rawdata[i++] = HVAC_MIDEA_HDR_MARK; + rawdata[i++] = HVAC_MIDEA_HDR_SPACE; + + //data + for (int b = 0; b < HVAC_MIDEA_DATALEN; b++) { // Send value + for (mask = B10000000; mask > 0; mask >>= 1) { + if (data[b] & mask) { // Bit ONE + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ONE_SPACE; + } + else { // Bit ZERO + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ZERO_SPACE; + } + } + for (mask = B10000000; mask > 0; mask >>= 1) { // Send complement + if (data[b] & mask) { // Bit ONE + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ZERO_SPACE; + } + else { // Bit ZERO + rawdata[i++] = HVAC_MIDEA_BIT_MARK; + rawdata[i++] = HVAC_MIDEA_ONE_SPACE; + } + } + + } + + //trailer + rawdata[i++] = HVAC_MIDEA_RPT_MARK; + rawdata[i++] = HVAC_MIDEA_RPT_SPACE; + +// noInterrupts(); + // this takes ~180 ms : + irsend->sendRaw(rawdata, i, 38); + irsend->sendRaw(rawdata, i, 38); +// interrupts(); + + return IE_NO_ERROR; +} +#endif // USE_IR_HVAC_MIDEA + #ifdef USE_IR_HVAC_MITSUBISHI /******************* MITSUBISHI @@ -357,6 +484,8 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H LG ********************/ +const uint8_t HVAC_LG_DATALEN = 7; + uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) { uint32_t LG_Code; @@ -822,6 +951,10 @@ bool IrSendCommand(void) #ifdef USE_IR_HVAC_FUJITSU case VNDR_FUJITSU: error = IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; +#endif +#ifdef USE_IR_HVAC_MIDEA + case VNDR_MIDEA: + error = IrHvacMidea(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break; #endif default: error = IE_SYNTAX_IRHVAC;