Tasmota/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino

255 lines
8.3 KiB
Arduino
Raw Normal View History

/*
xdrv_17_rcswitch.ino - RF transceiver using RcSwitch library for Tasmota
2021-01-01 12:44:04 +00:00
Copyright (C) 2021 Theo Arends
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.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_RC_SWITCH
/*********************************************************************************************\
* RF send and receive using RCSwitch library https://github.com/sui77/rc-switch/
\*********************************************************************************************/
#define XDRV_17 17
#define D_JSON_RF_PROTOCOL "Protocol"
#define D_JSON_RF_BITS "Bits"
#define D_JSON_RF_DATA "Data"
#define D_CMND_RFSEND "Send"
#define D_CMND_RFPROTOCOL "Protocol"
#define D_CMND_RFTIMEOUT "TimeOut"
#define D_JSON_RF_PULSE "Pulse"
#define D_JSON_RF_REPEAT "Repeat"
#define D_JSON_NONE_ENABLED "None Enabled"
const char kRfCommands[] PROGMEM = "Rf|" // No prefix
D_CMND_RFSEND "|" D_CMND_RFPROTOCOL "|" D_CMND_RFTIMEOUT;
void (* const RfCommands[])(void) PROGMEM = {
&CmndRfSend, &CmndRfProtocol, &CmndRfTimeOut};
2019-08-11 14:18:11 +01:00
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();
#define RF_TIME_AVOID_DUPLICATE 1000 // Milliseconds
uint32_t rf_lasttime = 0;
void RfReceiveCheck(void) {
if (mySwitch.available()) {
unsigned long data = mySwitch.getReceivedValue();
unsigned int bits = mySwitch.getReceivedBitlength();
int protocol = mySwitch.getReceivedProtocol();
int delay = mySwitch.getReceivedDelay();
2021-01-23 15:26:23 +00:00
AddLog(LOG_LEVEL_DEBUG, PSTR("RFR: Data 0x%lX (%u), Bits %d, Protocol %d, Delay %d"), data, data, bits, protocol, delay);
uint32_t now = millis();
if ((now - rf_lasttime > Settings->rf_duplicate_time) && (data > 0)) {
rf_lasttime = now;
char stemp[16];
2021-06-11 17:14:12 +01:00
if (Settings->flag.rf_receive_decimal) { // SetOption28 - RF receive data format (0 = hexadecimal, 1 = decimal)
snprintf_P(stemp, sizeof(stemp), PSTR("%u"), (uint32_t)data);
} else {
snprintf_P(stemp, sizeof(stemp), PSTR("\"0x%lX\""), (uint32_t)data);
}
ResponseTime_P(PSTR(",\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_RF_DATA "\":%s,\"" D_JSON_RF_BITS "\":%d,\"" D_JSON_RF_PROTOCOL "\":%d,\"" D_JSON_RF_PULSE "\":%d}}"),
stemp, bits, protocol, delay);
2020-07-20 16:24:51 +01:00
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED));
#ifdef USE_DOMOTICZ
DomoticzSensor(DZ_COUNT, data); // Send data as Domoticz Counter value
#endif // USE_DOMOTICZ
}
mySwitch.resetAvailable();
}
}
void RfInit(void) {
2020-04-27 11:54:07 +01:00
if (PinUsed(GPIO_RFSEND)) {
2020-04-26 16:33:27 +01:00
mySwitch.enableTransmit(Pin(GPIO_RFSEND));
}
2020-04-27 11:54:07 +01:00
if (PinUsed(GPIO_RFRECV)) {
if (Settings->rf_duplicate_time < 10) {
Settings->rf_duplicate_time = RF_TIME_AVOID_DUPLICATE;
}
2020-04-26 16:33:27 +01:00
pinMode( Pin(GPIO_RFRECV), INPUT);
mySwitch.enableReceive(Pin(GPIO_RFRECV));
2021-06-11 17:14:12 +01:00
if (!Settings->rf_protocol_mask) {
Settings->rf_protocol_mask = (1ULL << mySwitch.getNumProtos()) -1;
}
2021-06-11 17:14:12 +01:00
mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask);
}
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
void CmndRfProtocol(void) {
if (!PinUsed(GPIO_RFRECV)) { return; }
// AddLog(LOG_LEVEL_INFO, PSTR("RFR:CmndRfRxProtocol:: index:%d usridx:%d data_len:%d data:\"%s\""),XdrvMailbox.index, XdrvMailbox.usridx, XdrvMailbox.data_len,XdrvMailbox.data);
uint64_t thisdat;
if (1 == XdrvMailbox.usridx) {
if (XdrvMailbox.payload >= 0) {
thisdat = (1ULL << (XdrvMailbox.index -1));
if (XdrvMailbox.payload &1) {
2021-06-11 17:14:12 +01:00
Settings->rf_protocol_mask |= thisdat;
} else {
2021-06-11 17:14:12 +01:00
Settings->rf_protocol_mask &= ~thisdat;
}
}
else if (XdrvMailbox.data_len > 0) {
return; // Not a number
}
} else {
if (XdrvMailbox.data_len > 0) {
2020-12-08 18:58:43 +00:00
if ('A' == toupper(XdrvMailbox.data[0])) {
2021-06-11 17:14:12 +01:00
Settings->rf_protocol_mask = (1ULL << mySwitch.getNumProtos()) -1;
} else {
thisdat = strtoull(XdrvMailbox.data, nullptr, 0);
if ((thisdat > 0) || ('0' == XdrvMailbox.data[0])) {
2021-06-11 17:14:12 +01:00
Settings->rf_protocol_mask = thisdat;
} else {
return; // Not a number
}
2020-12-05 10:51:57 +00:00
}
}
}
2021-06-11 17:14:12 +01:00
mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask);
2021-01-23 15:26:23 +00:00
// AddLog(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Start responce"));
Response_P(PSTR("{\"" D_CMND_RFPROTOCOL "\":\""));
2020-12-05 10:51:57 +00:00
bool gotone = false;
thisdat = 1;
for (uint32_t i = 0; i < mySwitch.getNumProtos(); i++) {
2021-06-11 17:14:12 +01:00
if (Settings->rf_protocol_mask & thisdat) {
ResponseAppend_P(PSTR("%s%d"), (gotone) ? "," : "", i+1);
2020-12-05 10:51:57 +00:00
gotone = true;
}
thisdat <<=1;
2020-12-05 10:51:57 +00:00
}
if (!gotone) { ResponseAppend_P(PSTR(D_JSON_NONE_ENABLED)); }
2020-12-05 10:51:57 +00:00
ResponseAppend_P(PSTR("\""));
ResponseJsonEnd();
}
2020-12-05 11:13:24 +00:00
2019-08-11 14:18:11 +01:00
void CmndRfSend(void)
{
if (!PinUsed(GPIO_RFSEND)) { return; }
bool error = false;
2019-08-11 14:18:11 +01:00
if (XdrvMailbox.data_len) {
2020-11-29 14:23:47 +00:00
unsigned long long data = 0; // unsigned long long => support payload >32bit
2019-08-11 14:18:11 +01:00
unsigned int bits = 24;
int protocol = 1;
int repeat = 10;
int pulse = 0; // 0 leave the library use the default value depending on protocol
2019-08-11 14:18:11 +01:00
2020-09-23 18:38:24 +01:00
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
2020-09-23 07:45:14 +01:00
if (root) {
2019-08-11 14:18:11 +01:00
// RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350}
char parm_uc[10];
2020-11-29 14:23:47 +00:00
data = root.getULong(PSTR(D_JSON_RF_DATA), data); // read payload data even >32bit
2020-09-23 07:45:14 +01:00
bits = root.getUInt(PSTR(D_JSON_RF_BITS), bits);
protocol = root.getInt(PSTR(D_JSON_RF_PROTOCOL), protocol);
repeat = root.getInt(PSTR(D_JSON_RF_REPEAT), repeat);
pulse = root.getInt(PSTR(D_JSON_RF_PULSE), pulse);
2019-08-11 14:18:11 +01:00
} else {
// RFsend data, bits, protocol, repeat, pulse
char *p;
uint8_t i = 0;
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < 5; str = strtok_r(nullptr, ", ", &p)) {
switch (i++) {
case 0:
data = strtoul(str, nullptr, 0); // Allow decimal (5246996) and hexadecimal (0x501014) input
break;
case 1:
bits = atoi(str);
break;
case 2:
protocol = atoi(str);
break;
case 3:
repeat = atoi(str);
break;
case 4:
pulse = atoi(str);
}
}
2019-08-11 14:18:11 +01:00
}
2019-08-11 14:18:11 +01:00
if (!protocol) { protocol = 1; }
mySwitch.setProtocol(protocol);
// if pulse is specified in the command, enforce the provided value (otherwise lib takes default)
2021-02-08 08:05:03 +00:00
if (pulse) { mySwitch.setPulseLength(pulse); }
2019-08-11 14:18:11 +01:00
if (!repeat) { repeat = 10; } // Default at init
mySwitch.setRepeatTransmit(repeat);
if (!bits) { bits = 24; } // Default 24 bits
if (data) {
mySwitch.send(data, bits);
ResponseCmndDone();
} else {
error = true;
}
2019-08-11 14:18:11 +01:00
} else {
error = true;
}
if (error) {
Response_P(PSTR("{\"" D_CMND_RFSEND "\":\"" D_JSON_NO " " D_JSON_RF_DATA ", " D_JSON_RF_BITS ", " D_JSON_RF_PROTOCOL ", " D_JSON_RF_REPEAT " " D_JSON_OR " " D_JSON_RF_PULSE "\"}"));
}
}
void CmndRfTimeOut(void) {
if (XdrvMailbox.payload >= 10) {
Settings->rf_duplicate_time = XdrvMailbox.payload;
}
ResponseCmndNumber(Settings->rf_duplicate_time);
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
2022-11-11 09:44:56 +00:00
bool Xdrv17(uint32_t function)
{
bool result = false;
2020-04-27 11:54:07 +01:00
if (PinUsed(GPIO_RFSEND) || PinUsed(GPIO_RFRECV)) {
switch (function) {
case FUNC_EVERY_50_MSECOND:
2020-04-27 11:54:07 +01:00
if (PinUsed(GPIO_RFRECV)) {
RfReceiveCheck();
}
break;
case FUNC_COMMAND:
result = DecodeCommand(kRfCommands, RfCommands);
break;
2019-08-11 14:18:11 +01:00
case FUNC_INIT:
RfInit();
break;
}
}
return result;
}
#endif // USE_RC_SWITCH