Refactor IrRemote commands

Refactor IrRemote commands
This commit is contained in:
Theo Arends 2019-08-02 14:23:41 +02:00
parent bea3174ec8
commit 8875787935
1 changed files with 360 additions and 355 deletions

View File

@ -28,8 +28,17 @@
enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC }; enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC };
enum IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC }; const char kIrRemoteCommands[] PROGMEM =
const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ; #ifdef USE_IR_HVAC
D_CMND_IRHVAC "|"
#endif
D_CMND_IRSEND ;
void (* const IrRemoteCommand[])(void) PROGMEM = {
#ifdef USE_IR_HVAC
&CmndIrHvac,
#endif
&CmndIrSend };
// Based on IRremoteESP8266.h enum decode_type_t // Based on IRremoteESP8266.h enum decode_type_t
static const uint8_t MAX_STANDARD_IR = SHARP; // this is the last code mapped to decode_type_t static const uint8_t MAX_STANDARD_IR = SHARP; // this is the last code mapped to decode_type_t
@ -46,22 +55,6 @@ const char kIrRemoteProtocols[] PROGMEM =
#endif // USE_IR_SEND_PIONEER #endif // USE_IR_SEND_PIONEER
; ;
#ifdef USE_IR_HVAC
#include <ir_Mitsubishi.h>
#include <ir_Fujitsu.h>
enum IrHvacVendors {
VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA };
const char kIrHvacVendors[] PROGMEM =
"Toshiba|Mitsubishi|LG|Fujitsu|Midea" ;
IRMitsubishiAC *mitsubir = nullptr;
const char kFanSpeedOptions[] = "A12345S";
const char kHvacModeOptions[] = "HDCA";
#endif // USE_IR_HVAC
/*********************************************************************************************\ /*********************************************************************************************\
* IR Send * IR Send
\*********************************************************************************************/ \*********************************************************************************************/
@ -75,10 +68,6 @@ void IrSendInit(void)
{ {
irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND
irsend->begin(); irsend->begin();
#ifdef USE_IR_HVAC
mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]);
#endif //USE_IR_HVAC
} }
char* IrUint64toHex(uint64_t value, char *str, uint16_t bits) char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
@ -102,6 +91,7 @@ char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
return str; return str;
} }
#ifdef USE_IR_RECEIVE #ifdef USE_IR_RECEIVE
/*********************************************************************************************\ /*********************************************************************************************\
* IR Receive * IR Receive
@ -202,15 +192,22 @@ void IrReceiveCheck(void)
} }
#endif // USE_IR_RECEIVE #endif // USE_IR_RECEIVE
#ifdef USE_IR_HVAC #ifdef USE_IR_HVAC
/********************************************************************************************* \ /*********************************************************************************************\
* IR Heating, Ventilation and Air Conditioning using IRMitsubishiAC library * IR Heating, Ventilation and Air Conditioning
\*********************************************************************************************/ \*********************************************************************************************/
enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU, VNDR_MIDEA };
const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu|Midea" ;
const char kFanSpeedOptions[] = "A12345S";
const char kHvacModeOptions[] = "HDCA";
#ifdef USE_IR_HVAC_TOSHIBA #ifdef USE_IR_HVAC_TOSHIBA
/******************* /*-------------------------------------------------------------------------------------------*\
TOSHIBA * Toshiba
********************/ \*-------------------------------------------------------------------------------------------*/
const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400; const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400;
const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300; const uint16_t HVAC_TOSHIBA_HDR_SPACE = 4300;
@ -303,7 +300,6 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE; rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE;
// noInterrupts(); // noInterrupts();
irsend_active = true;
irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38);
irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38);
// interrupts(); // interrupts();
@ -313,9 +309,9 @@ uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
#endif // USE_IR_HVAC_TOSHIBA #endif // USE_IR_HVAC_TOSHIBA
#ifdef USE_IR_HVAC_MIDEA #ifdef USE_IR_HVAC_MIDEA
/******************* /*-------------------------------------------------------------------------------------------*\
MIDEA / KOMECO * Midea / Komeco
********************/ \*-------------------------------------------------------------------------------------------*/
// http://veillard.com/embedded/midea.html // http://veillard.com/embedded/midea.html
// https://github.com/sheinz/esp-midea-ir/blob/master/midea-ir.c // https://github.com/sheinz/esp-midea-ir/blob/master/midea-ir.c
@ -431,27 +427,29 @@ uint8_t IrHvacMidea(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_P
rawdata[i++] = HVAC_MIDEA_RPT_MARK; rawdata[i++] = HVAC_MIDEA_RPT_MARK;
rawdata[i++] = HVAC_MIDEA_RPT_SPACE; rawdata[i++] = HVAC_MIDEA_RPT_SPACE;
// noInterrupts();
// this takes ~180 ms : // this takes ~180 ms :
irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38);
irsend->sendRaw(rawdata, i, 38); irsend->sendRaw(rawdata, i, 38);
// interrupts();
return IE_NO_ERROR; return IE_NO_ERROR;
} }
#endif // USE_IR_HVAC_MIDEA #endif // USE_IR_HVAC_MIDEA
#ifdef USE_IR_HVAC_MITSUBISHI #ifdef USE_IR_HVAC_MITSUBISHI
/******************* /*-------------------------------------------------------------------------------------------*\
MITSUBISHI * Mitsubishi
********************/ \*-------------------------------------------------------------------------------------------*/
#include <ir_Mitsubishi.h>
uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
{ {
char *p; char *p;
uint8_t mode; uint8_t mode;
mitsubir->stateReset(); IRMitsubishiAC mitsubir(pin[GPIO_IRSEND]);
mitsubir.stateReset();
if (HVAC_Mode == nullptr) { if (HVAC_Mode == nullptr) {
p = (char *)kHvacModeOptions; // default HVAC_HOT p = (char *)kHvacModeOptions; // default HVAC_HOT
@ -463,9 +461,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H
return IE_SYNTAX_IRHVAC; return IE_SYNTAX_IRHVAC;
} }
mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20 mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20
mitsubir->setMode(mode); mitsubir.setMode(mode);
mitsubir->setPower(HVAC_Power); mitsubir.setPower(HVAC_Power);
if (HVAC_FanMode == nullptr) { if (HVAC_FanMode == nullptr) {
p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
@ -477,11 +475,11 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H
return IE_SYNTAX_IRHVAC; return IE_SYNTAX_IRHVAC;
} }
mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6 mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6
mitsubir->setFan(mode); mitsubir.setFan(mode);
mitsubir->setTemp(HVAC_Temp); mitsubir.setTemp(HVAC_Temp);
mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO); mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO);
mitsubir->send(); mitsubir.send();
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"), // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"),
// mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane()); // mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane());
@ -491,9 +489,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H
#endif // USE_IR_HVAC_MITSUBISHI #endif // USE_IR_HVAC_MITSUBISHI
#ifdef USE_IR_HVAC_LG #ifdef USE_IR_HVAC_LG
/******************* /*-------------------------------------------------------------------------------------------*\
LG * LG
********************/ \*-------------------------------------------------------------------------------------------*/
const uint8_t HVAC_LG_DATALEN = 7; const uint8_t HVAC_LG_DATALEN = 7;
@ -600,19 +598,18 @@ uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Powe
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code); // AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code);
// Send LG IR Code // Send LG IR Code
// noInterrupts();
irsend_active = true;
irsend->sendLG(LG_Code, 28); irsend->sendLG(LG_Code, 28);
// interrupts();
return IE_NO_ERROR; return IE_NO_ERROR;
} }
#endif // USE_IR_HVAC_LG #endif // USE_IR_HVAC_LG
#ifdef USE_IR_HVAC_FUJITSU #ifdef USE_IR_HVAC_FUJITSU
/******************* /*-------------------------------------------------------------------------------------------*\
Fujitsu * Fujitsu
********************/ \*-------------------------------------------------------------------------------------------*/
#include <ir_Fujitsu.h>
uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp) uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
{ {
@ -622,8 +619,6 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
IRFujitsuAC ac(pin[GPIO_IRSEND]); IRFujitsuAC ac(pin[GPIO_IRSEND]);
irsend_active = true;
if (0 == HVAC_Power) { if (0 == HVAC_Power) {
ac.off(); ac.off();
ac.send(); ac.send();
@ -665,331 +660,341 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
} }
#endif // USE_IR_HVAC_FUJITSU #endif // USE_IR_HVAC_FUJITSU
/*-------------------------------------------------------------------------------------------*/
uint32_t IrRemoteCmndIrHvacJson(void)
{
// IrHvac { "Vendor": "<Toshiba|Mitsubishi>", "Power": <0|1>, "Mode": "<Hot|Cold|Dry|Auto>", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> }
const char *HVAC_Mode;
const char *HVAC_FanMode;
const char *HVAC_Vendor;
int HVAC_Temp = 21;
bool HVAC_Power = true;
char dataBufUc[XdrvMailbox.data_len];
UpperCase(dataBufUc, XdrvMailbox.data);
RemoveSpace(dataBufUc);
if (strlen(dataBufUc) < 8) {
return IE_INVALID_JSON;
}
StaticJsonBuffer<164> jsonBufer;
JsonObject &root = jsonBufer.parseObject(dataBufUc);
if (!root.success()) {
return IE_INVALID_JSON;
}
HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR];
HVAC_Power = root[D_JSON_IRHVAC_POWER];
HVAC_Mode = root[D_JSON_IRHVAC_MODE];
HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED];
HVAC_Temp = root[D_JSON_IRHVAC_TEMP];
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp);
char vendor[20];
int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors);
irsend_active = true;
switch (vendor_code) {
#ifdef USE_IR_HVAC_TOSHIBA
case VNDR_TOSHIBA:
return IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
#endif
#ifdef USE_IR_HVAC_MITSUBISHI
case VNDR_MITSUBISHI:
return IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
#endif
#ifdef USE_IR_HVAC_LG
case VNDR_LG:
return IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
#endif
#ifdef USE_IR_HVAC_FUJITSU
case VNDR_FUJITSU:
return IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
#endif
#ifdef USE_IR_HVAC_MIDEA
case VNDR_MIDEA:
return IrHvacMidea(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
#endif
default:
irsend_active = false;
}
return IE_SYNTAX_IRHVAC;
}
void CmndIrHvac(void)
{
uint8_t error = IE_SYNTAX_IRHVAC;
if (XdrvMailbox.data_len) {
error = IrRemoteCmndIrHvacJson();
}
IrRemoteCmndResponse(error);
}
#endif // USE_IR_HVAC #endif // USE_IR_HVAC
/*********************************************************************************************\ /*********************************************************************************************\
* Commands * Commands
\*********************************************************************************************/ \*********************************************************************************************/
/* uint32_t IrRemoteCmndIrSendRaw(void)
* ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96
IRsend:
{ "protocol": "RC5", "bits": 12, "data":"0xC86" }
{ "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
IRhvac:
{ "Vendor": "<Toshiba|Mitsubishi>", "Power": <0|1>, "Mode": "<Hot|Cold|Dry|Auto>", "FanSpeed": "<1|2|3|4|5|Auto|Silence>", "Temp": <17..30> }
*/
bool IrSendCommand(void)
{ {
char command [CMDSZ]; // IRsend <freq>,<rawdata>,<rawdata> ...
bool serviced = true; // or
uint8_t error = IE_NO_ERROR; // IRsend raw,<freq>,<zero space>,<bit stream> (one space = zero space *2)
// IRsend raw,<freq>,<zero space>,<zero space multiplier becoming one space>,<bit stream>
// IRsend raw,<freq>,<zero space>,<one space>,<bit stream>
// IRsend raw,<freq>,<header mark>,<header space>,<bit mark>,<zero space>,<one space>,<bit stream>
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands); char *p;
if (-1 == command_code) { char *str = strtok_r(XdrvMailbox.data, ", ", &p);
serviced = false; // Unknown command if (p == nullptr) {
return IE_INVALID_RAWDATA;
} }
else if (CMND_IRSEND == command_code) {
if (XdrvMailbox.data_len) {
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be rawdata uint16_t freq = atoi(str);
// IRsend <freq>,<rawdata>,<rawdata> ... if (!freq && (*str != '0')) { // First parameter is any string
// or uint16_t count = 0;
// IRsend raw,<freq>,<zero space>,<bit stream> (one space = zero space *2) char *q = p;
// IRsend raw,<freq>,<zero space>,<zero space multiplier becoming one space>,<bit stream> for (; *q; count += (*q++ == ','));
// IRsend raw,<freq>,<zero space>,<one space>,<bit stream> if (count < 2) {
// IRsend raw,<freq>,<header mark>,<header space>,<bit mark>,<zero space>,<one space>,<bit stream> return IE_INVALID_RAWDATA;
char *p; } // Parameters must be at least 3
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
if (p == nullptr) { uint16_t parm[count];
error = IE_INVALID_RAWDATA; for (uint32_t i = 0; i < count; i++) {
parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0);
if (!parm[i]) {
if (!i) {
parm[0] = 38000; // Frequency default to 38kHz
} else { } else {
uint16_t freq = atoi(str); return IE_INVALID_RAWDATA; // Other parameters may not be 0
if (!freq && (*str != '0')) { // First parameter is any string
uint16_t count = 0;
char *q = p;
for (; *q; count += (*q++ == ','));
if (count < 2) { // Parameters must be at least 3
error = IE_INVALID_RAWDATA;
} else {
uint16_t parm[count];
for (uint32_t i = 0; i < count; i++) {
parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0);
if (!parm[i]) {
if (!i) {
parm[0] = 38000; // Frequency default to 38kHz
} else {
error = IE_INVALID_RAWDATA; // Other parameters may not be 0
break;
}
}
}
if (IE_NO_ERROR == error) {
uint16_t i = 0;
if (count < 4) {
// IRsend raw,0,889,000000100110000001001
uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5)
if (3 == count) {
if (parm[2] < parm[1]) {
// IRsend raw,0,889,2,000000100110000001001
mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5)
} else {
// IRsend raw,0,889,1778,000000100110000001001
mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5)
}
}
uint16_t raw_array[strlen(p)]; // Bits
for (; *p; *p++) {
if (*p == '0') {
raw_array[i++] = parm[1]; // Space
}
else if (*p == '1') {
raw_array[i++] = mark; // Mark
}
}
irsend_active = true;
irsend->sendRaw(raw_array, i, parm[0]);
}
else if (6 == count) { // NEC Protocol
// IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end
raw_array[i++] = parm[1]; // Header mark
raw_array[i++] = parm[2]; // Header space
for (; *p; *p++) {
if (*p == '0') {
raw_array[i++] = parm[3]; // Bit mark
raw_array[i++] = parm[4]; // Zero space
}
else if (*p == '1') {
raw_array[i++] = parm[3]; // Bit mark
raw_array[i++] = parm[5]; // One space
}
}
raw_array[i++] = parm[3]; // Trailing mark
irsend_active = true;
irsend->sendRaw(raw_array, i, parm[0]);
}
else {
error = IE_INVALID_RAWDATA; // Invalid number of parameters
}
}
}
} else {
if (!freq) { freq = 38000; } // Default to 38kHz
uint16_t count = 0;
char *q = p;
for (; *q; count += (*q++ == ','));
if (0 == count) {
error = IE_INVALID_RAWDATA;
} else { // At least two raw data values
// IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734
count++;
if (count < 200) {
uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length)
for (uint32_t i = 0; i < count; i++) {
raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count);
irsend_active = true;
irsend->sendRaw(raw_array, count, freq);
} else {
uint16_t *raw_array = reinterpret_cast<uint16_t*>(malloc(count * sizeof(uint16_t)));
if (raw_array == nullptr) {
error = IE_INVALID_RAWDATA;
} else {
for (uint32_t i = 0; i < count; i++) {
raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count);
irsend_active = true;
irsend->sendRaw(raw_array, count, freq);
free(raw_array);
}
}
}
}
}
} else {
char dataBufUc[XdrvMailbox.data_len];
UpperCase(dataBufUc, XdrvMailbox.data);
RemoveSpace(dataBufUc);
if (strlen(dataBufUc) < 8) {
error = IE_INVALID_JSON;
} else {
StaticJsonBuffer<140> jsonBuf;
JsonObject &root = jsonBuf.parseObject(dataBufUc);
if (!root.success()) {
error = IE_INVALID_JSON;
} else {
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
// IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 }
char parm_uc[10];
const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))];
uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))];
uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0);
uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))];
if (protocol && bits) {
char protocol_text[20];
int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
char dvalue[64];
char hvalue[64];
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), repeat %d, protocol_code %d"),
protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat, protocol_code);
irsend_active = true;
switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t
#ifdef USE_IR_SEND_RC5
case RC5:
irsend->sendRC5(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_RC6
case RC6:
irsend->sendRC6(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_NEC
case NEC:
irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break;
#endif
#ifdef USE_IR_SEND_SONY
case SONY:
irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, repeat > kSonyMinRepeat ? repeat : kSonyMinRepeat); break;
#endif
#ifdef USE_IR_SEND_PANASONIC
case PANASONIC:
irsend->sendPanasonic64(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_JVC
case JVC:
irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, repeat > 1 ? repeat : 1); break;
#endif
#ifdef USE_IR_SEND_SAMSUNG
case SAMSUNG:
irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits, repeat); break;
#endif
#ifdef USE_IR_SEND_WHYNTER
case WHYNTER:
irsend->sendWhynter(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_AIWA
case AIWA_RC_T501:
irsend->sendAiwaRCT501(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_LG
case LG:
irsend->sendLG(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_SANYO
case SANYO:
irsend->sendSanyoLC7461(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_MITSUBISHI
case MITSUBISHI:
irsend->sendMitsubishi(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_DISH
case DISH:
irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits, repeat > kDishMinRepeat ? repeat : kDishMinRepeat); break;
#endif
#ifdef USE_IR_SEND_SHARP
case SHARP:
irsend->sendSharpRaw(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_PIONEER
case IR_PIONEER:
irsend->sendPioneer(data, bits, repeat); break;
#endif // USE_IR_SEND_PIONEER
default:
irsend_active = false;
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED);
}
} else {
error = IE_SYNTAX_IRSEND;
}
}
} }
} }
} else {
error = IE_SYNTAX_IRSEND;
} }
}
#ifdef USE_IR_HVAC
else if (CMND_IRHVAC == command_code) {
const char *HVAC_Mode;
const char *HVAC_FanMode;
const char *HVAC_Vendor;
int HVAC_Temp = 21;
bool HVAC_Power = true;
if (XdrvMailbox.data_len) { uint16_t i = 0;
char dataBufUc[XdrvMailbox.data_len]; if (count < 4) {
UpperCase(dataBufUc, XdrvMailbox.data); // IRsend raw,0,889,000000100110000001001
RemoveSpace(dataBufUc); uint16_t mark = parm[1] *2; // Protocol where 0 = t, 1 = 2t (RC5)
if (strlen(dataBufUc) < 8) { if (3 == count) {
error = IE_INVALID_JSON; if (parm[2] < parm[1]) {
} else { // IRsend raw,0,889,2,000000100110000001001
StaticJsonBuffer<164> jsonBufer; mark = parm[1] * parm[2]; // Protocol where 0 = t1, 1 = t1*t2 (Could be RC5)
JsonObject &root = jsonBufer.parseObject(dataBufUc);
if (!root.success()) {
error = IE_INVALID_JSON;
} else { } else {
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE); // IRsend raw,0,889,1778,000000100110000001001
HVAC_Vendor = root[D_JSON_IRHVAC_VENDOR]; mark = parm[2]; // Protocol where 0 = t1, 1 = t2 (Could be RC5)
HVAC_Power = root[D_JSON_IRHVAC_POWER];
HVAC_Mode = root[D_JSON_IRHVAC_MODE];
HVAC_FanMode = root[D_JSON_IRHVAC_FANSPEED];
HVAC_Temp = root[D_JSON_IRHVAC_TEMP];
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received Vendor %s, Power %d, Mode %s, FanSpeed %s, Temp %d"), HVAC_Vendor, HVAC_Power, HVAC_Mode, HVAC_FanMode, HVAC_Temp);
char vendor[20];
int vendor_code = GetCommandCode(vendor, sizeof(vendor), HVAC_Vendor, kIrHvacVendors);
switch (vendor_code) {
#ifdef USE_IR_HVAC_TOSHIBA
case VNDR_TOSHIBA:
error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
#endif
#ifdef USE_IR_HVAC_MITSUBISHI
case VNDR_MITSUBISHI:
error = IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
#endif
#ifdef USE_IR_HVAC_LG
case VNDR_LG:
error = IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
#endif
#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;
}
} }
} }
uint16_t raw_array[strlen(p)]; // Bits
for (; *p; *p++) {
if (*p == '0') {
raw_array[i++] = parm[1]; // Space
}
else if (*p == '1') {
raw_array[i++] = mark; // Mark
}
}
irsend_active = true;
irsend->sendRaw(raw_array, i, parm[0]);
}
else if (6 == count) { // NEC Protocol
// IRsend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end
raw_array[i++] = parm[1]; // Header mark
raw_array[i++] = parm[2]; // Header space
for (; *p; *p++) {
if (*p == '0') {
raw_array[i++] = parm[3]; // Bit mark
raw_array[i++] = parm[4]; // Zero space
}
else if (*p == '1') {
raw_array[i++] = parm[3]; // Bit mark
raw_array[i++] = parm[5]; // One space
}
}
raw_array[i++] = parm[3]; // Trailing mark
irsend_active = true;
irsend->sendRaw(raw_array, i, parm[0]);
} }
else { else {
error = IE_SYNTAX_IRHVAC; return IE_INVALID_RAWDATA; // Invalid number of parameters
}
} else {
if (!freq) { freq = 38000; } // Default to 38kHz
uint16_t count = 0;
char *q = p;
for (; *q; count += (*q++ == ','));
if (0 == count) {
return IE_INVALID_RAWDATA;
}
// IRsend 0,896,876,900,888,894,876,1790,874,872,1810,1736,948,872,880,872,936,872,1792,900,888,1734
count++;
if (count < 200) {
uint16_t raw_array[count]; // It's safe to use stack for up to 200 packets (limited by mqtt_data length)
for (uint32_t i = 0; i < count; i++) {
raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: stack count %d"), count);
irsend_active = true;
irsend->sendRaw(raw_array, count, freq);
} else {
uint16_t *raw_array = reinterpret_cast<uint16_t*>(malloc(count * sizeof(uint16_t)));
if (raw_array == nullptr) {
return IE_INVALID_RAWDATA;
}
for (uint32_t i = 0; i < count; i++) {
raw_array[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0); // Allow decimal (20496) and hexadecimal (0x5010) input
}
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: heap count %d"), count);
irsend_active = true;
irsend->sendRaw(raw_array, count, freq);
free(raw_array);
} }
} }
#endif // USE_IR_HVAC
else serviced = false; // Unknown command
return IE_NO_ERROR;
}
uint32_t IrRemoteCmndIrSendJson(void)
{
// ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96
// IRsend { "protocol": "RC5", "bits": 12, "data":"0xC86" }
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
char dataBufUc[XdrvMailbox.data_len];
UpperCase(dataBufUc, XdrvMailbox.data);
RemoveSpace(dataBufUc);
if (strlen(dataBufUc) < 8) {
return IE_INVALID_JSON;
}
StaticJsonBuffer<140> jsonBuf;
JsonObject &root = jsonBuf.parseObject(dataBufUc);
if (!root.success()) {
return IE_INVALID_JSON;
}
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
// IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 }
char parm_uc[10];
const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))];
uint16_t bits = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_BITS))];
uint64_t data = strtoull(root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_DATA))], nullptr, 0);
uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))];
if (!(protocol && bits)) {
return IE_SYNTAX_IRSEND;
}
char protocol_text[20];
int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
char dvalue[64];
char hvalue[64];
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), repeat %d, protocol_code %d"),
protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), repeat, protocol_code);
irsend_active = true;
switch (protocol_code) { // Equals IRremoteESP8266.h enum decode_type_t
#ifdef USE_IR_SEND_RC5
case RC5:
irsend->sendRC5(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_RC6
case RC6:
irsend->sendRC6(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_NEC
case NEC:
irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits, repeat); break;
#endif
#ifdef USE_IR_SEND_SONY
case SONY:
irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, repeat > kSonyMinRepeat ? repeat : kSonyMinRepeat); break;
#endif
#ifdef USE_IR_SEND_PANASONIC
case PANASONIC:
irsend->sendPanasonic64(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_JVC
case JVC:
irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, repeat > 1 ? repeat : 1); break;
#endif
#ifdef USE_IR_SEND_SAMSUNG
case SAMSUNG:
irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits, repeat); break;
#endif
#ifdef USE_IR_SEND_WHYNTER
case WHYNTER:
irsend->sendWhynter(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_AIWA
case AIWA_RC_T501:
irsend->sendAiwaRCT501(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_LG
case LG:
irsend->sendLG(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_SANYO
case SANYO:
irsend->sendSanyoLC7461(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_MITSUBISHI
case MITSUBISHI:
irsend->sendMitsubishi(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_DISH
case DISH:
irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits, repeat > kDishMinRepeat ? repeat : kDishMinRepeat); break;
#endif
#ifdef USE_IR_SEND_SHARP
case SHARP:
irsend->sendSharpRaw(data, bits, repeat); break;
#endif
#ifdef USE_IR_SEND_PIONEER
case IR_PIONEER:
irsend->sendPioneer(data, bits, repeat); break;
#endif // USE_IR_SEND_PIONEER
default:
irsend_active = false;
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_PROTOCOL_NOT_SUPPORTED);
}
return IE_NO_ERROR;
}
void CmndIrSend(void)
{
uint8_t error = IE_SYNTAX_IRSEND;
if (XdrvMailbox.data_len) {
// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson();
if (strstr(XdrvMailbox.data, "{") == nullptr) {
error = IrRemoteCmndIrSendRaw();
} else {
error = IrRemoteCmndIrSendJson();
}
}
IrRemoteCmndResponse(error);
}
void IrRemoteCmndResponse(uint32_t error)
{
switch (error) { switch (error) {
case IE_INVALID_RAWDATA: case IE_INVALID_RAWDATA:
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_RAWDATA); Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_INVALID_RAWDATA);
break; break;
case IE_INVALID_JSON: case IE_INVALID_JSON:
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_JSON); Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_INVALID_JSON);
break; break;
case IE_SYNTAX_IRSEND: case IE_SYNTAX_IRSEND:
Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}")); Response_P(PSTR("{\"" D_CMND_IRSEND "\":\"" D_JSON_NO " " D_JSON_IR_PROTOCOL ", " D_JSON_IR_BITS " " D_JSON_OR " " D_JSON_IR_DATA "\"}"));
@ -999,9 +1004,9 @@ bool IrSendCommand(void)
Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}")); Response_P(PSTR("{\"" D_CMND_IRHVAC "\":\"" D_JSON_WRONG " " D_JSON_IRHVAC_VENDOR ", " D_JSON_IRHVAC_MODE " " D_JSON_OR " " D_JSON_IRHVAC_FANSPEED "\"}"));
break; break;
#endif // USE_IR_HVAC #endif // USE_IR_HVAC
default: // IE_NO_ERROR
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_DONE);
} }
return serviced;
} }
/*********************************************************************************************\ /*********************************************************************************************\
@ -1034,7 +1039,7 @@ bool Xdrv05(uint8_t function)
break; break;
case FUNC_COMMAND: case FUNC_COMMAND:
if (pin[GPIO_IRSEND] < 99) { if (pin[GPIO_IRSEND] < 99) {
result = IrSendCommand(); result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand);
} }
break; break;
} }