Add IR Raw data control

* Add command SetOption58 0/1 to enable IR raw data info in JSON message (#2116)
 * Add command IRSend <frequency>|0,<rawdata1>,<rawdata2>,.. to allow raw data transmission (#2116)
This commit is contained in:
Theo Arends 2018-11-21 16:36:10 +01:00
parent 9ad155436a
commit 070843200f
6 changed files with 101 additions and 26 deletions

View File

@ -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 <frequency>|0,<rawdata1>,<rawdata2>,.. 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

View File

@ -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"

View File

@ -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)

View File

@ -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;

View File

@ -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 <IRrecv.h>
#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": "<Toshiba|Mitsubishi>", "Power": <0|1>, "Mode": "<Hot|Cold|Dry|Auto>", "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<uint16_t*>(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);

View File

@ -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",
"","","",
"","","","",
"","","","",
"","","","",