mirror of https://github.com/arendst/Tasmota.git
255 lines
8.3 KiB
C++
255 lines
8.3 KiB
C++
/*
|
|
xdrv_17_rcswitch.ino - RF transceiver using RcSwitch library for Tasmota
|
|
|
|
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};
|
|
|
|
#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();
|
|
|
|
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];
|
|
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);
|
|
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) {
|
|
if (PinUsed(GPIO_RFSEND)) {
|
|
mySwitch.enableTransmit(Pin(GPIO_RFSEND));
|
|
}
|
|
if (PinUsed(GPIO_RFRECV)) {
|
|
if (Settings->rf_duplicate_time < 10) {
|
|
Settings->rf_duplicate_time = RF_TIME_AVOID_DUPLICATE;
|
|
}
|
|
pinMode( Pin(GPIO_RFRECV), INPUT);
|
|
mySwitch.enableReceive(Pin(GPIO_RFRECV));
|
|
if (!Settings->rf_protocol_mask) {
|
|
Settings->rf_protocol_mask = (1ULL << mySwitch.getNumProtos()) -1;
|
|
}
|
|
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) {
|
|
Settings->rf_protocol_mask |= thisdat;
|
|
} else {
|
|
Settings->rf_protocol_mask &= ~thisdat;
|
|
}
|
|
}
|
|
else if (XdrvMailbox.data_len > 0) {
|
|
return; // Not a number
|
|
}
|
|
} else {
|
|
if (XdrvMailbox.data_len > 0) {
|
|
if ('A' == toupper(XdrvMailbox.data[0])) {
|
|
Settings->rf_protocol_mask = (1ULL << mySwitch.getNumProtos()) -1;
|
|
} else {
|
|
thisdat = strtoull(XdrvMailbox.data, nullptr, 0);
|
|
if ((thisdat > 0) || ('0' == XdrvMailbox.data[0])) {
|
|
Settings->rf_protocol_mask = thisdat;
|
|
} else {
|
|
return; // Not a number
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask);
|
|
// AddLog(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Start responce"));
|
|
Response_P(PSTR("{\"" D_CMND_RFPROTOCOL "\":\""));
|
|
bool gotone = false;
|
|
thisdat = 1;
|
|
for (uint32_t i = 0; i < mySwitch.getNumProtos(); i++) {
|
|
if (Settings->rf_protocol_mask & thisdat) {
|
|
ResponseAppend_P(PSTR("%s%d"), (gotone) ? "," : "", i+1);
|
|
gotone = true;
|
|
}
|
|
thisdat <<=1;
|
|
}
|
|
if (!gotone) { ResponseAppend_P(PSTR(D_JSON_NONE_ENABLED)); }
|
|
ResponseAppend_P(PSTR("\""));
|
|
ResponseJsonEnd();
|
|
}
|
|
|
|
void CmndRfSend(void)
|
|
{
|
|
if (!PinUsed(GPIO_RFSEND)) { return; }
|
|
|
|
bool error = false;
|
|
|
|
if (XdrvMailbox.data_len) {
|
|
unsigned long long data = 0; // unsigned long long => support payload >32bit
|
|
unsigned int bits = 24;
|
|
int protocol = 1;
|
|
int repeat = 10;
|
|
int pulse = 0; // 0 leave the library use the default value depending on protocol
|
|
|
|
JsonParser parser(XdrvMailbox.data);
|
|
JsonParserObject root = parser.getRootObject();
|
|
if (root) {
|
|
// RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350}
|
|
char parm_uc[10];
|
|
data = root.getULong(PSTR(D_JSON_RF_DATA), data); // read payload data even >32bit
|
|
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);
|
|
} 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 = strtoull(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!protocol) { protocol = 1; }
|
|
mySwitch.setProtocol(protocol);
|
|
// if pulse is specified in the command, enforce the provided value (otherwise lib takes default)
|
|
if (pulse) { mySwitch.setPulseLength(pulse); }
|
|
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;
|
|
}
|
|
} 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
|
|
\*********************************************************************************************/
|
|
|
|
bool Xdrv17(uint32_t function)
|
|
{
|
|
bool result = false;
|
|
|
|
if (PinUsed(GPIO_RFSEND) || PinUsed(GPIO_RFRECV)) {
|
|
switch (function) {
|
|
case FUNC_EVERY_50_MSECOND:
|
|
if (PinUsed(GPIO_RFRECV)) {
|
|
RfReceiveCheck();
|
|
}
|
|
break;
|
|
case FUNC_COMMAND:
|
|
result = DecodeCommand(kRfCommands, RfCommands);
|
|
break;
|
|
case FUNC_INIT:
|
|
RfInit();
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#endif // USE_RC_SWITCH
|