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 IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC };
const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ;
const char kIrRemoteCommands[] PROGMEM =
#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
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
;
#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
\*********************************************************************************************/
@ -75,10 +68,6 @@ void IrSendInit(void)
{
irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND
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)
@ -102,6 +91,7 @@ char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
return str;
}
#ifdef USE_IR_RECEIVE
/*********************************************************************************************\
* IR Receive
@ -202,15 +192,22 @@ void IrReceiveCheck(void)
}
#endif // USE_IR_RECEIVE
#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
/*******************
TOSHIBA
********************/
/*-------------------------------------------------------------------------------------------*\
* Toshiba
\*-------------------------------------------------------------------------------------------*/
const uint16_t HVAC_TOSHIBA_HDR_MARK = 4400;
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;
// noInterrupts();
irsend_active = true;
irsend->sendRaw(rawdata, i, 38);
irsend->sendRaw(rawdata, i, 38);
// interrupts();
@ -313,9 +309,9 @@ 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
********************/
/*-------------------------------------------------------------------------------------------*\
* Midea / Komeco
\*-------------------------------------------------------------------------------------------*/
// http://veillard.com/embedded/midea.html
// 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_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
********************/
/*-------------------------------------------------------------------------------------------*\
* Mitsubishi
\*-------------------------------------------------------------------------------------------*/
#include <ir_Mitsubishi.h>
uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
{
char *p;
uint8_t mode;
mitsubir->stateReset();
IRMitsubishiAC mitsubir(pin[GPIO_IRSEND]);
mitsubir.stateReset();
if (HVAC_Mode == nullptr) {
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;
}
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) {
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;
}
mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6
mitsubir->setFan(mode);
mitsubir.setFan(mode);
mitsubir->setTemp(HVAC_Temp);
mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO);
mitsubir->send();
mitsubir.setTemp(HVAC_Temp);
mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO);
mitsubir.send();
// 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());
@ -491,9 +489,9 @@ uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool H
#endif // USE_IR_HVAC_MITSUBISHI
#ifdef USE_IR_HVAC_LG
/*******************
LG
********************/
/*-------------------------------------------------------------------------------------------*\
* LG
\*-------------------------------------------------------------------------------------------*/
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);
// Send LG IR Code
// noInterrupts();
irsend_active = true;
irsend->sendLG(LG_Code, 28);
// interrupts();
return IE_NO_ERROR;
}
#endif // USE_IR_HVAC_LG
#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)
{
@ -622,8 +619,6 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
IRFujitsuAC ac(pin[GPIO_IRSEND]);
irsend_active = true;
if (0 == HVAC_Power) {
ac.off();
ac.send();
@ -665,55 +660,109 @@ uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC
}
#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
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
/*
* 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)
uint32_t IrRemoteCmndIrSendRaw(void)
{
char command [CMDSZ];
bool serviced = true;
uint8_t error = IE_NO_ERROR;
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands);
if (-1 == command_code) {
serviced = false; // Unknown command
}
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
// IRsend <freq>,<rawdata>,<rawdata> ...
// or
// 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>
char *p;
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
if (p == nullptr) {
error = IE_INVALID_RAWDATA;
} else {
return IE_INVALID_RAWDATA;
}
uint16_t freq = atoi(str);
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 {
if (count < 2) {
return IE_INVALID_RAWDATA;
} // Parameters must be at least 3
uint16_t parm[count];
for (uint32_t i = 0; i < count; i++) {
parm[i] = strtol(strtok_r(nullptr, ", ", &p), nullptr, 0);
@ -721,12 +770,11 @@ bool IrSendCommand(void)
if (!i) {
parm[0] = 38000; // Frequency default to 38kHz
} else {
error = IE_INVALID_RAWDATA; // Other parameters may not be 0
break;
return IE_INVALID_RAWDATA; // Other parameters may not be 0
}
}
}
if (IE_NO_ERROR == error) {
uint16_t i = 0;
if (count < 4) {
// IRsend raw,0,889,000000100110000001001
@ -772,9 +820,7 @@ bool IrSendCommand(void)
irsend->sendRaw(raw_array, i, parm[0]);
}
else {
error = IE_INVALID_RAWDATA; // Invalid number of parameters
}
}
return IE_INVALID_RAWDATA; // Invalid number of parameters
}
} else {
if (!freq) { freq = 38000; } // Default to 38kHz
@ -782,8 +828,9 @@ bool IrSendCommand(void)
char *q = p;
for (; *q; count += (*q++ == ','));
if (0 == count) {
error = IE_INVALID_RAWDATA;
} else { // At least two raw data values
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) {
@ -799,8 +846,9 @@ bool IrSendCommand(void)
} else {
uint16_t *raw_array = reinterpret_cast<uint16_t*>(malloc(count * sizeof(uint16_t)));
if (raw_array == nullptr) {
error = IE_INVALID_RAWDATA;
} else {
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
}
@ -812,21 +860,29 @@ bool IrSendCommand(void)
free(raw_array);
}
}
return IE_NO_ERROR;
}
}
}
} else {
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) {
error = IE_INVALID_JSON;
} else {
return IE_INVALID_JSON;
}
StaticJsonBuffer<140> jsonBuf;
JsonObject &root = jsonBuf.parseObject(dataBufUc);
if (!root.success()) {
error = IE_INVALID_JSON;
} else {
return IE_INVALID_JSON;
}
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
// IRsend { "protocol": "NEC", "bits": 32, "data":"0x02FDFE80", "repeat": 2 }
char parm_uc[10];
@ -834,7 +890,10 @@ bool IrSendCommand(void)
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) {
if (!(protocol && bits)) {
return IE_SYNTAX_IRSEND;
}
char protocol_text[20];
int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
@ -907,89 +966,35 @@ bool IrSendCommand(void)
#endif // USE_IR_SEND_PIONEER
default:
irsend_active = false;
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED);
Response_P(S_JSON_COMMAND_SVALUE, XdrvMailbox.command, D_JSON_PROTOCOL_NOT_SUPPORTED);
}
} else {
error = IE_SYNTAX_IRSEND;
return IE_NO_ERROR;
}
}
}
}
} 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;
void CmndIrSend(void)
{
uint8_t error = IE_SYNTAX_IRSEND;
if (XdrvMailbox.data_len) {
char dataBufUc[XdrvMailbox.data_len];
UpperCase(dataBufUc, XdrvMailbox.data);
RemoveSpace(dataBufUc);
if (strlen(dataBufUc) < 8) {
error = IE_INVALID_JSON;
// error = (strstr(XdrvMailbox.data, "{") == nullptr) ? IrRemoteCmndIrSendRaw() : IrRemoteCmndIrSendJson();
if (strstr(XdrvMailbox.data, "{") == nullptr) {
error = IrRemoteCmndIrSendRaw();
} else {
StaticJsonBuffer<164> jsonBufer;
JsonObject &root = jsonBufer.parseObject(dataBufUc);
if (!root.success()) {
error = IE_INVALID_JSON;
} else {
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
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);
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;
}
}
}
}
else {
error = IE_SYNTAX_IRHVAC;
}
}
#endif // USE_IR_HVAC
else serviced = false; // Unknown command
error = IrRemoteCmndIrSendJson();
}
}
IrRemoteCmndResponse(error);
}
void IrRemoteCmndResponse(uint32_t error)
{
switch (error) {
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;
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;
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 "\"}"));
@ -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 "\"}"));
break;
#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;
case FUNC_COMMAND:
if (pin[GPIO_IRSEND] < 99) {
result = IrSendCommand();
result = DecodeCommand(kIrRemoteCommands, IrRemoteCommand);
}
break;
}