diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index cd44e7dbd..081164712 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -2,6 +2,8 @@ * Add delays removed in 6.3.0.9 (#4233) * Allow user definition of defines WIFI_RSSI_THRESHOLD (default 10) and WIFI_RESCAN_MINUTES (default 44) * Add support for Fujitsu HVac and IrRemote (#4387) + * Add command SetOption58 0/1 to enable IR raw data info in JSON message (#2116) + * Add command IRSend |0,,,.. to allow raw data transmission (#2116) * * 6.3.0.10 20181118 * Add command SetOption36 0..255 milliseconds (50 default) to tune main loop dynamic delay diff --git a/sonoff/i18n.h b/sonoff/i18n.h index f53433553..317e78712 100644 --- a/sonoff/i18n.h +++ b/sonoff/i18n.h @@ -335,10 +335,13 @@ // Commands xdrv_05_irremote.ino #define D_CMND_IRSEND "IRSend" #define D_JSON_INVALID_JSON "Invalid JSON" + #define D_JSON_INVALID_RAWDATA "Invalid RawData" + #define D_JSON_NO_BUFFER_SPACE "No buffer space" #define D_JSON_PROTOCOL_NOT_SUPPORTED "Protocol not supported" #define D_JSON_IR_PROTOCOL "Protocol" #define D_JSON_IR_BITS "Bits" #define D_JSON_IR_DATA "Data" + #define D_JSON_IR_RAWDATA "RawData" #define D_CMND_IRHVAC "IRHVAC" #define D_JSON_IRHVAC_VENDOR "VENDOR" #define D_JSON_IRHVAC_POWER "POWER" diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 004e93a09..86384fdbc 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -367,7 +367,10 @@ // -- Low level interface devices ----------------- #define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k3 code, 0k3 mem, 48 iram) // #define USE_IR_HVAC // Support for HVAC (Toshiba, Mitsubishi and LG) system using IR (+3k5 code) - #define USE_IR_RECEIVE // Support for IR receiver (+6k5 code, 264 iram) + #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)) + #define IR_RCV_TIMEOUT 15 // Number of milli-Seconds of no-more-data before we consider a message ended (default 15) + #define IR_RCV_MIN_UNKNOWN_SIZE 6 // Set the smallest sized "UNKNOWN" message packets we actually care about (default 6) #define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // #define USE_WS2812_CTYPE NEO_GRB // WS2812 Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) diff --git a/sonoff/settings.h b/sonoff/settings.h index 14ae43faf..553f9eba3 100644 --- a/sonoff/settings.h +++ b/sonoff/settings.h @@ -71,7 +71,7 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu uint32_t hass_short_discovery_msg : 1; // bit 5 (v6.3.0.7) uint32_t use_wifi_scan : 1; // bit 6 (v6.3.0.10) uint32_t use_wifi_rescan : 1; // bit 7 (v6.3.0.10) - uint32_t spare08 : 1; + uint32_t receive_raw : 1; // bit 8 (v6.3.0.11) uint32_t spare09 : 1; uint32_t spare10 : 1; uint32_t spare11 : 1; diff --git a/sonoff/xdrv_05_irremote.ino b/sonoff/xdrv_05_irremote.ino index 8836c4ddf..abf303fb1 100644 --- a/sonoff/xdrv_05_irremote.ino +++ b/sonoff/xdrv_05_irremote.ino @@ -77,16 +77,21 @@ void IrSendInit(void) * IR Receive \*********************************************************************************************/ +#define IR_RCV_SAVE_BUFFER 0 // 0 = do not use buffer, 1 = use buffer for decoding + +#define IR_TIME_AVOID_DUPLICATE 500 // Milliseconds + #include -#define IR_TIME_AVOID_DUPLICATE 500 // Milliseconds - IRrecv *irrecv = NULL; + unsigned long ir_lasttime = 0; void IrReceiveInit(void) { - irrecv = new IRrecv(pin[GPIO_IRRECV]); // an IR led is at GPIO_IRRECV + // an IR led is at GPIO_IRRECV + irrecv = new IRrecv(pin[GPIO_IRRECV], IR_RCV_BUFFER_SIZE, IR_RCV_TIMEOUT, IR_RCV_SAVE_BUFFER); + irrecv->setUnknownThreshold(IR_RCV_MIN_UNKNOWN_SIZE); irrecv->enableIRIn(); // Start the receiver // AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized")); @@ -102,33 +107,58 @@ void IrReceiveCheck(void) if (irrecv->decode(&results)) { - snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_IRR "RawLen %d, Bits %d, Value %08X, Decode %d"), - results.rawlen, results.bits, results.value, results.decode_type); + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_IRR "RawLen %d, Overflow %d, Bits %d, Value %08X, Decode %d"), + results.rawlen, results.overflow, results.bits, results.value, results.decode_type); AddLog(LOG_LEVEL_DEBUG); unsigned long now = millis(); - if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) { +// if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) { + if (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) { ir_lasttime = now; iridx = results.decode_type; if ((iridx < 0) || (iridx > 14)) { - iridx = 0; + iridx = 0; // UNKNOWN } - if (Settings.flag.ir_receive_decimal) { snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)results.value); } else { snprintf_P(stemp, sizeof(stemp), PSTR("\"%lX\""), (uint32_t)results.value); } - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s}}"), + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"), GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, stemp); + if (Settings.flag3.receive_raw) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_JSON_IR_RAWDATA "\":["), mqtt_data); + uint16_t i; + for (i = 1; i < results.rawlen; i++) { + if (i > 1) { snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,"), mqtt_data); } + uint32_t usecs; + for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d,0,"), mqtt_data, UINT16_MAX); + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%d"), mqtt_data, usecs); + if (strlen(mqtt_data) > sizeof(mqtt_data) - 40) { break; } // Quit if char string becomes too long + } + uint16_t extended_length = results.rawlen - 1; + for (uint16_t j = 0; j < results.rawlen - 1; j++) { + uint32_t usecs = results.rawbuf[j] * kRawTick; + // Add two extra entries for multiple larger than UINT16_MAX it is. + extended_length += (usecs / (UINT16_MAX + 1)) * 2; + } + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), mqtt_data, extended_length, i -1, results.overflow); + } + + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}}"), mqtt_data); MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED)); - XdrvRulesProcess(); + + if (iridx) { + XdrvRulesProcess(); #ifdef USE_DOMOTICZ - unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28] - DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value -#endif // USE_DOMOTICZ + unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28] + DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value +#endif // USE_DOMOTICZ + } } irrecv->resume(); @@ -276,9 +306,9 @@ boolean IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, boolea mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); mitsubir->send(); - // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), - // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); - // AddLog(LOG_LEVEL_DEBUG); +// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), +// mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); +// AddLog(LOG_LEVEL_DEBUG); return false; } @@ -469,28 +499,64 @@ boolean IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, boolean H { "Vendor": "", "Power": <0|1>, "Mode": "", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> } */ -//boolean IrSendCommand(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload) boolean IrSendCommand(void) { boolean serviced = true; boolean error = false; - char dataBufUc[XdrvMailbox.data_len]; char protocol_text[20]; const char *protocol; uint32_t bits = 0; uint32_t data = 0; + char dataBufUc[XdrvMailbox.data_len]; UpperCase(dataBufUc, XdrvMailbox.data); if (!strcasecmp_P(XdrvMailbox.topic, PSTR(D_CMND_IRSEND))) { if (XdrvMailbox.data_len) { StaticJsonBuffer<128> jsonBuf; JsonObject &root = jsonBuf.parseObject(dataBufUc); + + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_DONE "\"}")); if (!root.success()) { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_INVALID_JSON "\"}")); // JSON decode failed + // IRSend frequency, rawdata, rawdata ... + char *p; + char *str = strtok_r(XdrvMailbox.data, ", ", &p); + uint16_t freq = atoi(str); + if (!freq) { freq = 38000; } // Default to 38kHz + uint16_t count = 0; + char *q = p; + for (; *q; count += (*q++ == ',')); + if (count) { // At least two raw data values + count++; + uint16_t* raw_array = NULL; + raw_array = reinterpret_cast(malloc(count * sizeof(uint16_t))); + if (raw_array != NULL) { + byte i = 0; + for (str = strtok_r(NULL, ", ", &p); str && i < count; str = strtok_r(NULL, ", ", &p)) { + raw_array[i++] = strtoul(str, NULL, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input + } + +// snprintf_P(log_data, sizeof(log_data), PSTR("IRS: Count %d, Freq %d, Arr[0] %d, Arr[count -1] %d"), +// count, freq, raw_array[0], raw_array[count -1]); +// AddLog(LOG_LEVEL_DEBUG); + + irsend->sendRaw(raw_array, count, freq); + free(raw_array); + if (!count) { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_FAILED "\"}")); // JSON decode failed and invalid RawData + } + } + else { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO_BUFFER_SPACE "\"}")); // JSON decode failed and invalid RawData + } + } + else { + snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_INVALID_RAWDATA "\"}")); // JSON decode failed and invalid RawData + } } else { - snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_DONE "\"}")); + // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } char parm_uc[10]; + protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))]; bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))]; data = strtoul(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], NULL, 0); @@ -556,9 +622,9 @@ boolean IrSendCommand(void) HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED]; HVAC_Temp = root[D_JSON_IRHVAC_TEMP]; - // snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), - // HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); - // AddLog(LOG_LEVEL_DEBUG); +// snprintf_P(log_data, sizeof(log_data), PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), +// HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp); +// AddLog(LOG_LEVEL_DEBUG); if (HVAC_Vendor == NULL || !strcasecmp_P(HVAC_Vendor, PSTR("TOSHIBA"))) { error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); diff --git a/tools/decode-status.py b/tools/decode-status.py index cca379ca6..b1881865f 100644 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -92,7 +92,8 @@ a_setoption = [[ "Use short Hass discovery messages", "Use wifi network scan at restart", "Use wifi network rescan regularly", - "","","","", + "Add IR raw data to JSON message", + "","","", "","","","", "","","","", "","","","",