2017-02-14 13:27:08 +00:00
|
|
|
/*
|
2018-05-28 10:35:23 +01:00
|
|
|
xdrv_05_irremote.ino - infra red support for Sonoff-Tasmota
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2019-01-01 12:55:01 +00:00
|
|
|
Copyright (C) 2019 Heiko Krupp, Lazar Obradovic and Theo Arends
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2017-05-13 12:02:10 +01:00
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2017-05-13 12:02:10 +01:00
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2017-05-13 12:02:10 +01:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-02-14 13:27:08 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef USE_IR_REMOTE
|
|
|
|
/*********************************************************************************************\
|
2017-10-06 16:28:00 +01:00
|
|
|
* IR Remote send and receive using IRremoteESP8266 library
|
2017-02-14 13:27:08 +00:00
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2018-11-07 09:30:03 +00:00
|
|
|
#define XDRV_05 5
|
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
#include <IRremoteESP8266.h>
|
2017-11-19 17:02:03 +00:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC };
|
|
|
|
|
2018-11-22 16:58:07 +00:00
|
|
|
enum IrRemoteCommands { CMND_IRSEND, CMND_IRHVAC };
|
|
|
|
const char kIrRemoteCommands[] PROGMEM = D_CMND_IRSEND "|" D_CMND_IRHVAC ;
|
|
|
|
|
2017-11-19 17:02:03 +00:00
|
|
|
// Based on IRremoteESP8266.h enum decode_type_t
|
|
|
|
const char kIrRemoteProtocols[] PROGMEM =
|
|
|
|
"UNKNOWN|RC5|RC6|NEC|SONY|PANASONIC|JVC|SAMSUNG|WHYNTER|AIWA_RC_T501|LG|SANYO|MITSUBISHI|DISH|SHARP";
|
|
|
|
|
|
|
|
#ifdef USE_IR_HVAC
|
|
|
|
|
|
|
|
#include <ir_Mitsubishi.h>
|
2018-11-19 18:39:44 +00:00
|
|
|
#include <ir_Fujitsu.h>
|
2017-10-08 15:51:05 +01:00
|
|
|
|
2018-11-22 16:58:07 +00:00
|
|
|
enum IrHvacVendors { VNDR_TOSHIBA, VNDR_MITSUBISHI, VNDR_LG, VNDR_FUJITSU };
|
|
|
|
const char kIrHvacVendors[] PROGMEM = "Toshiba|Mitsubishi|LG|Fujitsu" ;
|
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
// HVAC TOSHIBA_
|
2019-03-31 10:59:04 +01:00
|
|
|
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;
|
2017-10-08 15:51:05 +01:00
|
|
|
|
2018-11-18 09:35:53 +00:00
|
|
|
// HVAC LG
|
2019-03-31 10:59:04 +01:00
|
|
|
const uint8_t HVAC_LG_DATALEN = 7;
|
2018-11-18 09:35:53 +00:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
IRMitsubishiAC *mitsubir = nullptr;
|
2017-10-08 15:51:05 +01:00
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
const char kFanSpeedOptions[] = "A12345S";
|
|
|
|
const char kHvacModeOptions[] = "HDCA";
|
2019-02-03 17:45:20 +00:00
|
|
|
#endif // USE_IR_HVAC
|
2017-02-14 13:27:08 +00:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* IR Send
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2017-11-19 17:02:03 +00:00
|
|
|
#include <IRsend.h>
|
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
IRsend *irsend = nullptr;
|
2019-02-03 17:45:20 +00:00
|
|
|
bool irsend_active = false;
|
2017-02-14 13:27:08 +00:00
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
void IrSendInit(void)
|
2017-02-14 13:27:08 +00:00
|
|
|
{
|
2017-10-08 15:51:05 +01:00
|
|
|
irsend = new IRsend(pin[GPIO_IRSEND]); // an IR led is at GPIO_IRSEND
|
2017-02-14 13:27:08 +00:00
|
|
|
irsend->begin();
|
2017-03-29 17:42:05 +01:00
|
|
|
|
|
|
|
#ifdef USE_IR_HVAC
|
|
|
|
mitsubir = new IRMitsubishiAC(pin[GPIO_IRSEND]);
|
|
|
|
#endif //USE_IR_HVAC
|
2017-02-14 13:27:08 +00:00
|
|
|
}
|
|
|
|
|
2019-04-01 15:39:13 +01:00
|
|
|
char* IrUint64toHex(uint64_t value, char *str, uint16_t bits)
|
|
|
|
{
|
|
|
|
ulltoa(value, str, 16); // Get 64bit value
|
|
|
|
|
|
|
|
int fill = 8;
|
|
|
|
if ((bits > 3) && (bits < 65)) {
|
|
|
|
fill = bits / 4; // Max 16
|
|
|
|
if (bits % 4) { fill++; }
|
|
|
|
}
|
|
|
|
int len = strlen(str);
|
|
|
|
fill -= len;
|
|
|
|
if (fill > 0) {
|
|
|
|
memmove(str + fill, str, len +1);
|
|
|
|
memset(str, '0', fill);
|
|
|
|
}
|
|
|
|
memmove(str + 2, str, strlen(str) +1);
|
|
|
|
str[0] = '0';
|
|
|
|
str[1] = 'x';
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2017-10-06 16:28:00 +01:00
|
|
|
#ifdef USE_IR_RECEIVE
|
2017-10-08 15:51:05 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* IR Receive
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2019-03-31 10:59:04 +01:00
|
|
|
const bool IR_RCV_SAVE_BUFFER = false; // false = do not use buffer, true = use buffer for decoding
|
|
|
|
const uint32_t IR_TIME_AVOID_DUPLICATE = 500; // Milliseconds
|
2017-10-06 16:28:00 +01:00
|
|
|
|
2018-11-21 15:36:10 +00:00
|
|
|
#include <IRrecv.h>
|
2017-10-25 13:27:30 +01:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
IRrecv *irrecv = nullptr;
|
2018-11-21 15:36:10 +00:00
|
|
|
|
2017-10-06 16:28:00 +01:00
|
|
|
unsigned long ir_lasttime = 0;
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
void IrReceiveInit(void)
|
2017-10-06 16:28:00 +01:00
|
|
|
{
|
2018-11-21 15:36:10 +00:00
|
|
|
// 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);
|
2017-10-08 15:51:05 +01:00
|
|
|
irrecv->enableIRIn(); // Start the receiver
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("IrReceive initialized"));
|
2017-10-06 16:28:00 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
void IrReceiveCheck(void)
|
2017-10-08 15:51:05 +01:00
|
|
|
{
|
2017-10-29 17:18:46 +00:00
|
|
|
char sirtype[14]; // Max is AIWA_RC_T501
|
2017-10-08 15:51:05 +01:00
|
|
|
int8_t iridx = 0;
|
2017-02-14 13:27:08 +00:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
decode_results results;
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
if (irrecv->decode(&results)) {
|
2019-04-01 14:15:16 +01:00
|
|
|
char hvalue[64];
|
|
|
|
IrUint64toHex(results.value, hvalue, results.bits); // Get 64bit value as hex 0x00123456
|
2017-02-17 16:18:41 +00:00
|
|
|
|
2019-04-01 14:15:16 +01:00
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_IRR "Echo %d, RawLen %d, Overflow %d, Bits %d, Value %s, Decode %d"),
|
|
|
|
irsend_active, results.rawlen, results.overflow, results.bits, hvalue, results.decode_type);
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
unsigned long now = millis();
|
2018-11-21 15:36:10 +00:00
|
|
|
// if ((now - ir_lasttime > IR_TIME_AVOID_DUPLICATE) && (UNKNOWN != results.decode_type) && (results.bits > 0)) {
|
2019-02-03 17:45:20 +00:00
|
|
|
if (!irsend_active && (now - ir_lasttime > IR_TIME_AVOID_DUPLICATE)) {
|
2017-10-08 15:51:05 +01:00
|
|
|
ir_lasttime = now;
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
iridx = results.decode_type;
|
2019-04-01 14:15:16 +01:00
|
|
|
if ((iridx < 0) || (iridx > 14)) { iridx = 0; } // UNKNOWN
|
|
|
|
char svalue[64];
|
2018-06-25 11:33:23 +01:00
|
|
|
if (Settings.flag.ir_receive_decimal) {
|
2019-04-01 14:15:16 +01:00
|
|
|
ulltoa(results.value, svalue, 10);
|
2018-06-25 11:33:23 +01:00
|
|
|
} else {
|
2019-04-01 14:15:16 +01:00
|
|
|
snprintf_P(svalue, sizeof(svalue), PSTR("\"%s\""), hvalue);
|
2018-06-25 11:33:23 +01:00
|
|
|
}
|
2019-03-23 16:00:59 +00:00
|
|
|
Response_P(PSTR("{\"" D_JSON_IRRECEIVED "\":{\"" D_JSON_IR_PROTOCOL "\":\"%s\",\"" D_JSON_IR_BITS "\":%d,\"" D_JSON_IR_DATA "\":%s"),
|
2019-04-01 14:15:16 +01:00
|
|
|
GetTextIndexed(sirtype, sizeof(sirtype), iridx, kIrRemoteProtocols), results.bits, svalue);
|
2018-06-25 11:33:23 +01:00
|
|
|
|
2018-11-21 15:36:10 +00:00
|
|
|
if (Settings.flag3.receive_raw) {
|
2019-03-23 16:00:59 +00:00
|
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_IR_RAWDATA "\":["));
|
2018-11-21 15:36:10 +00:00
|
|
|
uint16_t i;
|
|
|
|
for (i = 1; i < results.rawlen; i++) {
|
2019-03-23 16:00:59 +00:00
|
|
|
if (i > 1) { ResponseAppend_P(PSTR(",")); }
|
2018-11-21 15:36:10 +00:00
|
|
|
uint32_t usecs;
|
|
|
|
for (usecs = results.rawbuf[i] * kRawTick; usecs > UINT16_MAX; usecs -= UINT16_MAX) {
|
2019-03-23 16:00:59 +00:00
|
|
|
ResponseAppend_P(PSTR("%d,0,"), UINT16_MAX);
|
2018-11-21 15:36:10 +00:00
|
|
|
}
|
2019-03-23 16:00:59 +00:00
|
|
|
ResponseAppend_P(PSTR("%d"), usecs);
|
2018-11-21 15:36:10 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-03-23 16:00:59 +00:00
|
|
|
ResponseAppend_P(PSTR("],\"" D_JSON_IR_RAWDATA "Info\":[%d,%d,%d]"), extended_length, i -1, results.overflow);
|
2018-11-21 15:36:10 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 16:00:59 +00:00
|
|
|
ResponseAppend_P(PSTR("}}"));
|
2018-01-18 15:19:28 +00:00
|
|
|
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_IRRECEIVED));
|
2018-11-21 15:36:10 +00:00
|
|
|
|
|
|
|
if (iridx) {
|
|
|
|
XdrvRulesProcess();
|
2017-10-08 15:51:05 +01:00
|
|
|
#ifdef USE_DOMOTICZ
|
2018-11-21 15:36:10 +00:00
|
|
|
unsigned long value = results.value | (iridx << 28); // [Protocol:4, Data:28]
|
|
|
|
DomoticzSensor(DZ_COUNT, value); // Send data as Domoticz Counter value
|
|
|
|
#endif // USE_DOMOTICZ
|
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
|
|
|
|
irrecv->resume();
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2017-02-14 13:27:08 +00:00
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
#endif // USE_IR_RECEIVE
|
2017-03-29 17:42:05 +01:00
|
|
|
|
|
|
|
#ifdef USE_IR_HVAC
|
2018-11-19 18:39:44 +00:00
|
|
|
/********************************************************************************************* \
|
2017-10-08 15:51:05 +01:00
|
|
|
* IR Heating, Ventilation and Air Conditioning using IRMitsubishiAC library
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
/*******************
|
2018-11-18 09:35:53 +00:00
|
|
|
TOSHIBA
|
|
|
|
********************/
|
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
uint8_t IrHvacToshiba(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
|
2017-03-29 17:42:05 +01:00
|
|
|
{
|
2017-11-19 17:02:03 +00:00
|
|
|
uint16_t rawdata[2 + 2 * 8 * HVAC_TOSHIBA_DATALEN + 2];
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t data[HVAC_TOSHIBA_DATALEN] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00};
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-04-25 17:24:42 +01:00
|
|
|
char *p;
|
2017-04-03 15:38:15 +01:00
|
|
|
uint8_t mode;
|
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_Mode == nullptr) {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = (char *)kHvacModeOptions; // default HVAC_HOT
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-10-18 17:22:34 +01:00
|
|
|
data[6] = (p - kHvacModeOptions) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-04-25 17:24:42 +01:00
|
|
|
if (!HVAC_Power) {
|
2019-01-28 13:08:33 +00:00
|
|
|
data[6] = (uint8_t)0x07; // Turn OFF HVAC
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_FanMode == nullptr) {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-10-18 17:22:34 +01:00
|
|
|
mode = p - kFanSpeedOptions + 1;
|
2017-04-25 17:24:42 +01:00
|
|
|
if ((1 == mode) || (7 == mode)) {
|
|
|
|
mode = 0;
|
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
mode = mode << 5; // AUTO = 0x00, SPEED = 0x40, 0x60, 0x80, 0xA0, 0xC0, SILENT = 0x00
|
2017-04-03 15:38:15 +01:00
|
|
|
data[6] = data[6] | mode;
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t Temp;
|
2017-03-29 17:42:05 +01:00
|
|
|
if (HVAC_Temp > 30) {
|
|
|
|
Temp = 30;
|
|
|
|
}
|
|
|
|
else if (HVAC_Temp < 17) {
|
|
|
|
Temp = 17;
|
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
else {
|
|
|
|
Temp = HVAC_Temp;
|
|
|
|
}
|
2019-01-28 13:08:33 +00:00
|
|
|
data[5] = (uint8_t)(Temp - 17) << 4;
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
data[HVAC_TOSHIBA_DATALEN - 1] = 0;
|
2017-03-29 17:42:05 +01:00
|
|
|
for (int x = 0; x < HVAC_TOSHIBA_DATALEN - 1; x++) {
|
2019-01-28 13:08:33 +00:00
|
|
|
data[HVAC_TOSHIBA_DATALEN - 1] = (uint8_t)data[x] ^ data[HVAC_TOSHIBA_DATALEN - 1]; // CRC is a simple bits addition
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int i = 0;
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t mask = 1;
|
2017-03-29 17:42:05 +01:00
|
|
|
|
|
|
|
//header
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_HDR_MARK;
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_HDR_SPACE;
|
|
|
|
|
|
|
|
//data
|
|
|
|
for (int b = 0; b < HVAC_TOSHIBA_DATALEN; b++) {
|
|
|
|
for (mask = B10000000; mask > 0; mask >>= 1) { //iterate through bit mask
|
|
|
|
if (data[b] & mask) { // Bit ONE
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_BIT_MARK;
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_ONE_SPACE;
|
|
|
|
}
|
|
|
|
else { // Bit ZERO
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_BIT_MARK;
|
|
|
|
rawdata[i++] = HVAC_MISTUBISHI_ZERO_SPACE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//trailer
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_RPT_MARK;
|
|
|
|
rawdata[i++] = HVAC_TOSHIBA_RPT_SPACE;
|
|
|
|
|
2019-02-03 17:45:20 +00:00
|
|
|
// noInterrupts();
|
|
|
|
irsend_active = true;
|
2017-10-08 15:51:05 +01:00
|
|
|
irsend->sendRaw(rawdata, i, 38);
|
|
|
|
irsend->sendRaw(rawdata, i, 38);
|
2019-02-03 17:45:20 +00:00
|
|
|
// interrupts();
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_NO_ERROR;
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
|
|
|
|
2018-11-18 09:35:53 +00:00
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
/*******************
|
2018-11-18 09:35:53 +00:00
|
|
|
MITSUBISHI
|
|
|
|
********************/
|
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
uint8_t IrHvacMitsubishi(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
|
2017-03-29 17:42:05 +01:00
|
|
|
{
|
2017-04-25 17:24:42 +01:00
|
|
|
char *p;
|
2017-04-03 15:38:15 +01:00
|
|
|
uint8_t mode;
|
2017-09-02 13:37:02 +01:00
|
|
|
|
2017-03-29 17:42:05 +01:00
|
|
|
mitsubir->stateReset();
|
2017-04-03 15:38:15 +01:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_Mode == nullptr) {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = (char *)kHvacModeOptions; // default HVAC_HOT
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-10-18 17:22:34 +01:00
|
|
|
mode = (p - kHvacModeOptions + 1) << 3; // HOT = 0x08, DRY = 0x10, COOL = 0x18, AUTO = 0x20
|
2017-04-03 15:38:15 +01:00
|
|
|
mitsubir->setMode(mode);
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2017-08-16 16:05:36 +01:00
|
|
|
mitsubir->setPower(HVAC_Power);
|
2017-03-29 17:42:05 +01:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_FanMode == nullptr) {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
else {
|
2017-10-18 17:22:34 +01:00
|
|
|
p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2017-04-25 17:24:42 +01:00
|
|
|
}
|
2017-10-18 17:22:34 +01:00
|
|
|
mode = p - kFanSpeedOptions; // AUTO = 0, SPEED = 1 .. 5, SILENT = 6
|
2017-04-03 15:38:15 +01:00
|
|
|
mitsubir->setFan(mode);
|
2017-03-29 17:42:05 +01:00
|
|
|
|
|
|
|
mitsubir->setTemp(HVAC_Temp);
|
|
|
|
mitsubir->setVane(MITSUBISHI_AC_VANE_AUTO);
|
|
|
|
mitsubir->send();
|
2017-09-02 13:37:02 +01:00
|
|
|
|
2019-03-08 14:15:42 +00:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Mitsubishi Power %d, Mode %d, FanSpeed %d, Temp %d, VaneMode %d"),
|
2018-11-21 15:36:10 +00:00
|
|
|
// mitsubir->getPower(), mitsubir->getMode(), mitsubir->getFan(), mitsubir->getTemp(), mitsubir->getVane());
|
2017-09-02 13:37:02 +01:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_NO_ERROR;
|
2017-03-29 17:42:05 +01:00
|
|
|
}
|
2018-11-18 09:35:53 +00:00
|
|
|
|
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
/*******************
|
2018-11-18 09:35:53 +00:00
|
|
|
LG
|
|
|
|
********************/
|
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
uint8_t IrHvacLG(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
|
2018-11-18 09:35:53 +00:00
|
|
|
{
|
|
|
|
uint32_t LG_Code;
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t data[HVAC_LG_DATALEN];
|
|
|
|
static bool hvacOn = false;
|
2018-11-18 09:35:53 +00:00
|
|
|
char *p;
|
|
|
|
uint8_t mode;
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t Temp;
|
2018-11-18 09:35:53 +00:00
|
|
|
|
|
|
|
// Constant data
|
|
|
|
data[0] = 0x08;
|
|
|
|
data[1] = 0x08;
|
|
|
|
data[2] = 0x00;
|
|
|
|
|
|
|
|
if (!HVAC_Power) {
|
2019-01-28 13:08:33 +00:00
|
|
|
data[2] = (uint8_t)0x0C; // Turn OFF HVAC, code 0x88C0051
|
|
|
|
data[3] = (uint8_t)0x00;
|
|
|
|
data[4] = (uint8_t)0x00;
|
|
|
|
data[5] = (uint8_t)0x05;
|
|
|
|
data[6] = (uint8_t)0x01;
|
2018-11-18 09:35:53 +00:00
|
|
|
hvacOn = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
else {
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2018-11-18 09:35:53 +00:00
|
|
|
// Set code for HVAC Mode - data[3]
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_Mode == nullptr) {
|
2018-11-18 09:35:53 +00:00
|
|
|
p = (char *)kHvacModeOptions; // default HVAC_HOT
|
|
|
|
}
|
|
|
|
else {
|
2018-11-20 13:10:32 +00:00
|
|
|
p = strchr(kHvacModeOptions, toupper(HVAC_Mode[0]));
|
2018-11-18 09:35:53 +00:00
|
|
|
}
|
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2018-11-18 09:35:53 +00:00
|
|
|
}
|
|
|
|
mode = (p - kHvacModeOptions) ^ 0x03; // HOT = 0x03, DRY = 0x02, COOL = 0x01, AUTO = 0x00
|
|
|
|
switch (mode) {
|
|
|
|
case 0: // AUTO
|
|
|
|
data[3] = 11;
|
|
|
|
break;
|
|
|
|
case 1: // COOL
|
|
|
|
data[3] = 8;
|
|
|
|
break;
|
|
|
|
case 2: // DRY
|
|
|
|
data[3] = 9;
|
|
|
|
break;
|
|
|
|
case 3: // HOT
|
|
|
|
data[3] = 12;
|
2018-11-20 13:10:32 +00:00
|
|
|
break;
|
2018-11-18 09:35:53 +00:00
|
|
|
}
|
|
|
|
if (!hvacOn) {
|
|
|
|
data[3] = data[3] & 7; // reset bit3
|
|
|
|
hvacOn = true;
|
|
|
|
}
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2019-03-08 14:15:42 +00:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: HvacMode %s, ModeVal %d, Code %d"), p, mode, data[3]);
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2018-11-18 09:35:53 +00:00
|
|
|
// Set code for HVAC temperature - data[4]
|
|
|
|
if (HVAC_Temp > 30) {
|
|
|
|
Temp = 30;
|
|
|
|
}
|
|
|
|
else if (HVAC_Temp < 18) {
|
|
|
|
Temp = 18;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Temp = HVAC_Temp;
|
|
|
|
}
|
2019-01-28 13:08:33 +00:00
|
|
|
data[4] = (uint8_t)(Temp - 15);
|
2018-11-18 09:35:53 +00:00
|
|
|
|
|
|
|
// Set code for HVAC fan mode - data[5]
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_FanMode == nullptr) {
|
2018-11-18 09:35:53 +00:00
|
|
|
p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
|
|
|
|
}
|
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2018-11-18 09:35:53 +00:00
|
|
|
}
|
|
|
|
mode = p - kFanSpeedOptions;
|
|
|
|
if ((mode == 0) || (mode > 3)) {
|
|
|
|
data[5] = 5; // Auto = 0x05
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data[5] = (mode * 2) - 2; // Low = 0x00, Mid = 0x02, High = 0x04
|
|
|
|
}
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2019-03-08 14:15:42 +00:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: FanMode %s, ModeVal %d, Code %d"), p, mode, data[5]);
|
2018-11-18 09:35:53 +00:00
|
|
|
|
|
|
|
// Set CRC code - data[6]
|
|
|
|
data[6] = (data[3] + data[4] + data[5]) & 0x0f; // CRC
|
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
}
|
2018-11-18 09:35:53 +00:00
|
|
|
// Build LG IR code
|
|
|
|
LG_Code = data[0] << 4;
|
|
|
|
for (int i = 1; i < 6; i++) {
|
|
|
|
LG_Code = (LG_Code + data[i]) << 4;
|
|
|
|
}
|
|
|
|
LG_Code = LG_Code + data[6];
|
|
|
|
|
2019-03-08 14:15:42 +00:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: LG_Code %d"), LG_Code);
|
2018-11-18 09:35:53 +00:00
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
// Send LG IR Code
|
2019-02-03 17:45:20 +00:00
|
|
|
// noInterrupts();
|
|
|
|
irsend_active = true;
|
2018-11-18 09:35:53 +00:00
|
|
|
irsend->sendLG(LG_Code, 28);
|
2019-02-03 17:45:20 +00:00
|
|
|
// interrupts();
|
2018-11-20 13:10:32 +00:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_NO_ERROR;
|
2018-11-18 09:35:53 +00:00
|
|
|
}
|
2018-11-20 13:10:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*******************
|
|
|
|
Fujitsu
|
|
|
|
********************/
|
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
uint8_t IrHvacFujitsu(const char *HVAC_Mode, const char *HVAC_FanMode, bool HVAC_Power, int HVAC_Temp)
|
2018-11-20 13:10:32 +00:00
|
|
|
{
|
|
|
|
const char kFujitsuHvacModeOptions[] = "HDCAF";
|
|
|
|
|
2019-03-08 14:15:42 +00:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("FUJITSU: mode:%s, fan:%s, power:%u, temp:%u"), HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp);
|
2018-11-20 13:10:32 +00:00
|
|
|
|
|
|
|
IRFujitsuAC ac(pin[GPIO_IRSEND]);
|
|
|
|
|
2019-02-03 17:45:20 +00:00
|
|
|
irsend_active = true;
|
|
|
|
|
2018-11-20 13:10:32 +00:00
|
|
|
if (0 == HVAC_Power) {
|
|
|
|
ac.off();
|
|
|
|
ac.send();
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_NO_ERROR;
|
2018-11-20 13:10:32 +00:00
|
|
|
}
|
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
uint8_t modes[5] = {FUJITSU_AC_MODE_HEAT, FUJITSU_AC_MODE_DRY, FUJITSU_AC_MODE_COOL, FUJITSU_AC_MODE_AUTO, FUJITSU_AC_MODE_FAN};
|
|
|
|
uint8_t fanModes[7] = {FUJITSU_AC_FAN_AUTO, FUJITSU_AC_FAN_LOW, FUJITSU_AC_FAN_MED, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_HIGH, FUJITSU_AC_FAN_QUIET};
|
2018-11-20 13:10:32 +00:00
|
|
|
ac.setCmd(FUJITSU_AC_CMD_TURN_ON);
|
|
|
|
ac.setSwing(FUJITSU_AC_SWING_VERT);
|
|
|
|
|
|
|
|
char *p;
|
2019-03-26 17:26:50 +00:00
|
|
|
if (nullptr == HVAC_Mode) {
|
2018-11-20 13:10:32 +00:00
|
|
|
p = (char *)kFujitsuHvacModeOptions;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p = strchr(kFujitsuHvacModeOptions, toupper(HVAC_Mode[0]));
|
|
|
|
}
|
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2018-11-20 13:10:32 +00:00
|
|
|
}
|
|
|
|
ac.setMode(modes[p - kFujitsuHvacModeOptions]);
|
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (HVAC_FanMode == nullptr) {
|
2018-11-20 13:10:32 +00:00
|
|
|
p = (char *)kFanSpeedOptions; // default FAN_SPEED_AUTO
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
p = strchr(kFanSpeedOptions, toupper(HVAC_FanMode[0]));
|
|
|
|
}
|
|
|
|
if (!p) {
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_SYNTAX_IRHVAC;
|
2018-11-20 13:10:32 +00:00
|
|
|
}
|
|
|
|
ac.setFanSpeed(fanModes[p - kFanSpeedOptions]);
|
|
|
|
|
|
|
|
ac.setTemp(HVAC_Temp);
|
|
|
|
ac.send();
|
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
return IE_NO_ERROR;
|
2018-11-20 13:10:32 +00:00
|
|
|
}
|
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
#endif // USE_IR_HVAC
|
2017-10-06 16:28:00 +01:00
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
/*********************************************************************************************\
|
|
|
|
* Commands
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ArduinoJSON entry used to calculate jsonBuf: JSON_OBJECT_SIZE(3) + 40 = 96
|
|
|
|
IRsend:
|
|
|
|
{ "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> }
|
|
|
|
*/
|
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
bool IrSendCommand(void)
|
2017-10-06 16:28:00 +01:00
|
|
|
{
|
2018-11-22 16:58:07 +00:00
|
|
|
char command [CMDSZ];
|
2019-01-28 13:08:33 +00:00
|
|
|
bool serviced = true;
|
2019-03-31 12:06:42 +01:00
|
|
|
uint8_t error = IE_NO_ERROR;
|
2018-11-22 16:58:07 +00:00
|
|
|
|
|
|
|
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kIrRemoteCommands);
|
|
|
|
if (-1 == command_code) {
|
|
|
|
serviced = false; // Unknown command
|
|
|
|
}
|
|
|
|
else if (CMND_IRSEND == command_code) {
|
2018-01-05 11:26:19 +00:00
|
|
|
if (XdrvMailbox.data_len) {
|
2019-03-23 16:00:59 +00:00
|
|
|
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
|
2018-11-21 15:36:10 +00:00
|
|
|
|
2019-03-26 17:26:50 +00:00
|
|
|
if (strstr(XdrvMailbox.data, "{") == nullptr) { // If no JSON it must be rawdata
|
2018-11-21 15:36:10 +00:00
|
|
|
// IRSend frequency, rawdata, rawdata ...
|
Alternative to IRSend RAW command.
It is not practical to send long streams of data using the existing IRSend RAW command as it is limited by the size of the mqtt buffer.
The format for this command is:
IRSend raw,<freq>,<hdr_mrk>,<hdr_spc>,<bit_mrk>,<zer_spc>,<one_spc>,<bit_str>
where,
<freq> = carrier freq (default 0, 38kHz)
<hdr_mrk> = header mark (ms)
<hdr_spc> = header space (ms)
<bit_mrk> = bit mark (ms)
<zer_spc> = zero space (ms)
<one_spc> = one space (ms)
<bit_str> = bit stream data (stream of ones and zeroes)
This command makes use of the output of the raw IR decoder from https://github.com/ToniA/Raw-IR-decoder-for-Arduino
USAGE:
Example rawirdecode output:
Number of symbols: 75
Symbols:
Hh010101101000111011001110000000001100110000000001100000000000000010001100
Bytes:
00: 0101|0110 | 6A | 01101010
01: 1000|1110 | 71 | 01110001
02: 1100|1110 | 73 | 01110011
03: 0000|0000 | 00 | 00000000
04: 1100|1100 | 33 | 00110011
05: 0000|0001 | 80 | 10000000
06: 1000|0000 | 01 | 00000001
07: 0000|0000 | 00 | 00000000
08: 1000|1100 | 31 | 00110001
6A,71,73,00,33,80,01,00,31
Timings (in us):
PAUSE SPACE: 0
HEADER MARK: 8620
HEADER SPACE: 4260
BIT MARK: 544
ZERO SPACE: 411
ONE SPACE: 1496
Decoding known protocols...
Unknown protocol
Bytecount: 9
Corresponding command:
IRSend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
2019-04-09 10:41:32 +01:00
|
|
|
// or IRSend raw,<freq>,<header mark>,<header space>,<bit mark>,<zero space>,<one space>,<bit stream>
|
2018-11-21 15:36:10 +00:00
|
|
|
char *p;
|
|
|
|
char *str = strtok_r(XdrvMailbox.data, ", ", &p);
|
2019-03-31 12:06:42 +01:00
|
|
|
if (p == nullptr) {
|
|
|
|
error = IE_INVALID_RAWDATA;
|
|
|
|
} else {
|
|
|
|
uint16_t freq = atoi(str);
|
2019-04-09 14:41:01 +01:00
|
|
|
if (!freq && (*str != '0')) { // First parameter is a string
|
Alternative to IRSend RAW command.
It is not practical to send long streams of data using the existing IRSend RAW command as it is limited by the size of the mqtt buffer.
The format for this command is:
IRSend raw,<freq>,<hdr_mrk>,<hdr_spc>,<bit_mrk>,<zer_spc>,<one_spc>,<bit_str>
where,
<freq> = carrier freq (default 0, 38kHz)
<hdr_mrk> = header mark (ms)
<hdr_spc> = header space (ms)
<bit_mrk> = bit mark (ms)
<zer_spc> = zero space (ms)
<one_spc> = one space (ms)
<bit_str> = bit stream data (stream of ones and zeroes)
This command makes use of the output of the raw IR decoder from https://github.com/ToniA/Raw-IR-decoder-for-Arduino
USAGE:
Example rawirdecode output:
Number of symbols: 75
Symbols:
Hh010101101000111011001110000000001100110000000001100000000000000010001100
Bytes:
00: 0101|0110 | 6A | 01101010
01: 1000|1110 | 71 | 01110001
02: 1100|1110 | 73 | 01110011
03: 0000|0000 | 00 | 00000000
04: 1100|1100 | 33 | 00110011
05: 0000|0001 | 80 | 10000000
06: 1000|0000 | 01 | 00000001
07: 0000|0000 | 00 | 00000000
08: 1000|1100 | 31 | 00110001
6A,71,73,00,33,80,01,00,31
Timings (in us):
PAUSE SPACE: 0
HEADER MARK: 8620
HEADER SPACE: 4260
BIT MARK: 544
ZERO SPACE: 411
ONE SPACE: 1496
Decoding known protocols...
Unknown protocol
Bytecount: 9
Corresponding command:
IRSend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
2019-04-09 10:41:32 +01:00
|
|
|
uint16_t count = 0;
|
|
|
|
char *q = p;
|
|
|
|
for (; *q; count += (*q++ == ','));
|
2019-04-09 14:41:01 +01:00
|
|
|
if (count != 6) { // Parameters must be exactly 6
|
Alternative to IRSend RAW command.
It is not practical to send long streams of data using the existing IRSend RAW command as it is limited by the size of the mqtt buffer.
The format for this command is:
IRSend raw,<freq>,<hdr_mrk>,<hdr_spc>,<bit_mrk>,<zer_spc>,<one_spc>,<bit_str>
where,
<freq> = carrier freq (default 0, 38kHz)
<hdr_mrk> = header mark (ms)
<hdr_spc> = header space (ms)
<bit_mrk> = bit mark (ms)
<zer_spc> = zero space (ms)
<one_spc> = one space (ms)
<bit_str> = bit stream data (stream of ones and zeroes)
This command makes use of the output of the raw IR decoder from https://github.com/ToniA/Raw-IR-decoder-for-Arduino
USAGE:
Example rawirdecode output:
Number of symbols: 75
Symbols:
Hh010101101000111011001110000000001100110000000001100000000000000010001100
Bytes:
00: 0101|0110 | 6A | 01101010
01: 1000|1110 | 71 | 01110001
02: 1100|1110 | 73 | 01110011
03: 0000|0000 | 00 | 00000000
04: 1100|1100 | 33 | 00110011
05: 0000|0001 | 80 | 10000000
06: 1000|0000 | 01 | 00000001
07: 0000|0000 | 00 | 00000000
08: 1000|1100 | 31 | 00110001
6A,71,73,00,33,80,01,00,31
Timings (in us):
PAUSE SPACE: 0
HEADER MARK: 8620
HEADER SPACE: 4260
BIT MARK: 544
ZERO SPACE: 411
ONE SPACE: 1496
Decoding known protocols...
Unknown protocol
Bytecount: 9
Corresponding command:
IRSend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
2019-04-09 10:41:32 +01:00
|
|
|
error = IE_INVALID_RAWDATA;
|
|
|
|
} else {
|
2019-04-09 14:41:01 +01:00
|
|
|
uint16_t parm[6];
|
|
|
|
for (uint8_t i = 0; i < 6; i++) {
|
|
|
|
str = strtok_r(nullptr, ", ", &p);
|
|
|
|
parm[i] = atoi(str);
|
|
|
|
if (!parm[i]) {
|
|
|
|
if (!i) {
|
|
|
|
parm[0] = 38000; // Frequency default to 38kHz
|
|
|
|
} else {
|
|
|
|
error = IE_INVALID_RAWDATA;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (IE_NO_ERROR == error) {
|
|
|
|
uint16_t raw_array[strlen(p)*2+3]; // Header + bits + end
|
|
|
|
uint16_t i = 0;
|
|
|
|
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]);
|
|
|
|
}
|
2019-03-31 12:06:42 +01:00
|
|
|
}
|
Alternative to IRSend RAW command.
It is not practical to send long streams of data using the existing IRSend RAW command as it is limited by the size of the mqtt buffer.
The format for this command is:
IRSend raw,<freq>,<hdr_mrk>,<hdr_spc>,<bit_mrk>,<zer_spc>,<one_spc>,<bit_str>
where,
<freq> = carrier freq (default 0, 38kHz)
<hdr_mrk> = header mark (ms)
<hdr_spc> = header space (ms)
<bit_mrk> = bit mark (ms)
<zer_spc> = zero space (ms)
<one_spc> = one space (ms)
<bit_str> = bit stream data (stream of ones and zeroes)
This command makes use of the output of the raw IR decoder from https://github.com/ToniA/Raw-IR-decoder-for-Arduino
USAGE:
Example rawirdecode output:
Number of symbols: 75
Symbols:
Hh010101101000111011001110000000001100110000000001100000000000000010001100
Bytes:
00: 0101|0110 | 6A | 01101010
01: 1000|1110 | 71 | 01110001
02: 1100|1110 | 73 | 01110011
03: 0000|0000 | 00 | 00000000
04: 1100|1100 | 33 | 00110011
05: 0000|0001 | 80 | 10000000
06: 1000|0000 | 01 | 00000001
07: 0000|0000 | 00 | 00000000
08: 1000|1100 | 31 | 00110001
6A,71,73,00,33,80,01,00,31
Timings (in us):
PAUSE SPACE: 0
HEADER MARK: 8620
HEADER SPACE: 4260
BIT MARK: 544
ZERO SPACE: 411
ONE SPACE: 1496
Decoding known protocols...
Unknown protocol
Bytecount: 9
Corresponding command:
IRSend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
2019-04-09 10:41:32 +01:00
|
|
|
} 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
|
|
|
|
count++;
|
|
|
|
uint16_t raw_array[count]; // It's safe to use stack for up to 240 packets (limited by mqtt_data length)
|
|
|
|
uint8_t i = 0;
|
|
|
|
for (str = strtok_r(nullptr, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p)) {
|
|
|
|
raw_array[i++] = strtoul(str, nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input
|
|
|
|
}
|
2018-11-21 15:36:10 +00:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: Count %d, Freq %d, Arr[0] %d, Arr[count -1] %d"), count, freq, raw_array[0], raw_array[count -1]);
|
2018-11-21 15:36:10 +00:00
|
|
|
|
Alternative to IRSend RAW command.
It is not practical to send long streams of data using the existing IRSend RAW command as it is limited by the size of the mqtt buffer.
The format for this command is:
IRSend raw,<freq>,<hdr_mrk>,<hdr_spc>,<bit_mrk>,<zer_spc>,<one_spc>,<bit_str>
where,
<freq> = carrier freq (default 0, 38kHz)
<hdr_mrk> = header mark (ms)
<hdr_spc> = header space (ms)
<bit_mrk> = bit mark (ms)
<zer_spc> = zero space (ms)
<one_spc> = one space (ms)
<bit_str> = bit stream data (stream of ones and zeroes)
This command makes use of the output of the raw IR decoder from https://github.com/ToniA/Raw-IR-decoder-for-Arduino
USAGE:
Example rawirdecode output:
Number of symbols: 75
Symbols:
Hh010101101000111011001110000000001100110000000001100000000000000010001100
Bytes:
00: 0101|0110 | 6A | 01101010
01: 1000|1110 | 71 | 01110001
02: 1100|1110 | 73 | 01110011
03: 0000|0000 | 00 | 00000000
04: 1100|1100 | 33 | 00110011
05: 0000|0001 | 80 | 10000000
06: 1000|0000 | 01 | 00000001
07: 0000|0000 | 00 | 00000000
08: 1000|1100 | 31 | 00110001
6A,71,73,00,33,80,01,00,31
Timings (in us):
PAUSE SPACE: 0
HEADER MARK: 8620
HEADER SPACE: 4260
BIT MARK: 544
ZERO SPACE: 411
ONE SPACE: 1496
Decoding known protocols...
Unknown protocol
Bytecount: 9
Corresponding command:
IRSend raw,0,8620,4260,544,411,1496,010101101000111011001110000000001100110000000001100000000000000010001100
2019-04-09 10:41:32 +01:00
|
|
|
irsend_active = true;
|
|
|
|
irsend->sendRaw(raw_array, count, freq);
|
|
|
|
if (!count) {
|
|
|
|
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_FAILED);
|
|
|
|
}
|
2019-03-31 12:06:42 +01:00
|
|
|
}
|
2018-11-21 15:36:10 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-31 12:06:42 +01:00
|
|
|
} else {
|
2018-11-22 16:58:07 +00:00
|
|
|
char dataBufUc[XdrvMailbox.data_len];
|
|
|
|
UpperCase(dataBufUc, XdrvMailbox.data);
|
2019-03-31 12:06:42 +01:00
|
|
|
RemoveSpace(dataBufUc);
|
|
|
|
if (strlen(dataBufUc) < 8) {
|
|
|
|
error = IE_INVALID_JSON;
|
|
|
|
} else {
|
|
|
|
StaticJsonBuffer<128> jsonBuf;
|
|
|
|
JsonObject &root = jsonBuf.parseObject(dataBufUc);
|
|
|
|
if (!root.success()) {
|
|
|
|
error = IE_INVALID_JSON;
|
|
|
|
} else {
|
|
|
|
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }
|
|
|
|
char parm_uc[10];
|
|
|
|
const char *protocol = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_PROTOCOL))];
|
2019-04-01 14:15:16 +01:00
|
|
|
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);
|
2019-03-31 12:06:42 +01:00
|
|
|
if (protocol && bits) {
|
|
|
|
char protocol_text[20];
|
|
|
|
int protocol_code = GetCommandCode(protocol_text, sizeof(protocol_text), protocol, kIrRemoteProtocols);
|
|
|
|
|
2019-04-01 14:15:16 +01:00
|
|
|
char dvalue[64];
|
|
|
|
char hvalue[64];
|
|
|
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRS: protocol_text %s, protocol %s, bits %d, data %s (%s), protocol_code %d"),
|
|
|
|
protocol_text, protocol, bits, ulltoa(data, dvalue, 10), IrUint64toHex(data, hvalue, bits), protocol_code);
|
2019-03-31 12:06:42 +01:00
|
|
|
|
|
|
|
irsend_active = true;
|
|
|
|
switch (protocol_code) {
|
|
|
|
case NEC:
|
|
|
|
irsend->sendNEC(data, (bits > NEC_BITS) ? NEC_BITS : bits); break;
|
|
|
|
case SONY:
|
|
|
|
irsend->sendSony(data, (bits > SONY_20_BITS) ? SONY_20_BITS : bits, 2); break;
|
|
|
|
case RC5:
|
|
|
|
irsend->sendRC5(data, bits); break;
|
|
|
|
case RC6:
|
|
|
|
irsend->sendRC6(data, bits); break;
|
|
|
|
case DISH:
|
|
|
|
irsend->sendDISH(data, (bits > DISH_BITS) ? DISH_BITS : bits); break;
|
|
|
|
case JVC:
|
|
|
|
irsend->sendJVC(data, (bits > JVC_BITS) ? JVC_BITS : bits, 1); break;
|
|
|
|
case SAMSUNG:
|
|
|
|
irsend->sendSAMSUNG(data, (bits > SAMSUNG_BITS) ? SAMSUNG_BITS : bits); break;
|
|
|
|
case PANASONIC:
|
2019-04-01 14:15:16 +01:00
|
|
|
// irsend->sendPanasonic(bits, data); break;
|
|
|
|
irsend->sendPanasonic64(data, bits); break;
|
2019-03-31 12:06:42 +01:00
|
|
|
default:
|
|
|
|
irsend_active = false;
|
|
|
|
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_PROTOCOL_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
error = IE_SYNTAX_IRSEND;
|
2018-11-22 16:58:07 +00:00
|
|
|
}
|
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
2017-10-06 16:28:00 +01:00
|
|
|
}
|
2019-03-31 12:06:42 +01:00
|
|
|
} else {
|
|
|
|
error = IE_SYNTAX_IRSEND;
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef USE_IR_HVAC
|
2018-11-22 16:58:07 +00:00
|
|
|
else if (CMND_IRHVAC == command_code) {
|
2018-02-03 22:25:05 +00:00
|
|
|
const char *HVAC_Mode;
|
|
|
|
const char *HVAC_FanMode;
|
|
|
|
const char *HVAC_Vendor;
|
|
|
|
int HVAC_Temp = 21;
|
2019-01-28 13:08:33 +00:00
|
|
|
bool HVAC_Power = true;
|
2018-02-03 22:25:05 +00:00
|
|
|
|
2018-01-05 11:26:19 +00:00
|
|
|
if (XdrvMailbox.data_len) {
|
2018-11-22 16:58:07 +00:00
|
|
|
char dataBufUc[XdrvMailbox.data_len];
|
|
|
|
UpperCase(dataBufUc, XdrvMailbox.data);
|
2019-03-31 12:06:42 +01:00
|
|
|
RemoveSpace(dataBufUc);
|
|
|
|
if (strlen(dataBufUc) < 8) {
|
|
|
|
error = IE_INVALID_JSON;
|
|
|
|
} 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) {
|
|
|
|
case VNDR_TOSHIBA:
|
|
|
|
error = IrHvacToshiba(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
|
|
|
|
case VNDR_MITSUBISHI:
|
|
|
|
error = IrHvacMitsubishi(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
|
|
|
|
case VNDR_LG:
|
|
|
|
error = IrHvacLG(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
|
|
|
|
case VNDR_FUJITSU:
|
|
|
|
error = IrHvacFujitsu(HVAC_Mode, HVAC_FanMode, HVAC_Power, HVAC_Temp); break;
|
|
|
|
default:
|
|
|
|
error = IE_SYNTAX_IRHVAC;
|
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2019-03-31 12:06:42 +01:00
|
|
|
error = IE_SYNTAX_IRHVAC;
|
2017-10-08 15:51:05 +01:00
|
|
|
}
|
2017-10-06 16:28:00 +01:00
|
|
|
}
|
2017-10-08 15:51:05 +01:00
|
|
|
#endif // USE_IR_HVAC
|
2018-11-22 16:58:07 +00:00
|
|
|
else serviced = false; // Unknown command
|
2018-05-17 10:55:40 +01:00
|
|
|
|
2019-03-31 12:06:42 +01:00
|
|
|
switch (error) {
|
|
|
|
case IE_INVALID_RAWDATA:
|
|
|
|
Response_P(S_JSON_COMMAND_SVALUE, command, D_JSON_INVALID_RAWDATA);
|
|
|
|
break;
|
|
|
|
case IE_INVALID_JSON:
|
|
|
|
Response_P(S_JSON_COMMAND_SVALUE, 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 "\"}"));
|
|
|
|
break;
|
|
|
|
#ifdef USE_IR_HVAC
|
|
|
|
case IE_SYNTAX_IRHVAC:
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
return serviced;
|
2017-10-06 16:28:00 +01:00
|
|
|
}
|
2018-01-05 11:26:19 +00:00
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
bool Xdrv05(uint8_t function)
|
2018-01-05 11:26:19 +00:00
|
|
|
{
|
2019-01-28 13:08:33 +00:00
|
|
|
bool result = false;
|
2018-01-05 11:26:19 +00:00
|
|
|
|
|
|
|
if ((pin[GPIO_IRSEND] < 99) || (pin[GPIO_IRRECV] < 99)) {
|
|
|
|
switch (function) {
|
2018-06-04 17:10:38 +01:00
|
|
|
case FUNC_PRE_INIT:
|
2018-01-05 11:26:19 +00:00
|
|
|
if (pin[GPIO_IRSEND] < 99) {
|
|
|
|
IrSendInit();
|
|
|
|
}
|
|
|
|
#ifdef USE_IR_RECEIVE
|
|
|
|
if (pin[GPIO_IRRECV] < 99) {
|
|
|
|
IrReceiveInit();
|
|
|
|
}
|
|
|
|
#endif // USE_IR_RECEIVE
|
|
|
|
break;
|
|
|
|
case FUNC_EVERY_50_MSECOND:
|
|
|
|
#ifdef USE_IR_RECEIVE
|
|
|
|
if (pin[GPIO_IRRECV] < 99) {
|
|
|
|
IrReceiveCheck(); // check if there's anything on IR side
|
|
|
|
}
|
|
|
|
#endif // USE_IR_RECEIVE
|
2019-02-03 17:45:20 +00:00
|
|
|
irsend_active = false; // re-enable IR reception
|
2018-01-05 11:26:19 +00:00
|
|
|
break;
|
|
|
|
case FUNC_COMMAND:
|
|
|
|
if (pin[GPIO_IRSEND] < 99) {
|
|
|
|
result = IrSendCommand();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-10-08 15:51:05 +01:00
|
|
|
#endif // USE_IR_REMOTE
|