From ee25464c26245ff79b697260390479c7caa96c01 Mon Sep 17 00:00:00 2001 From: openshwprojects <85486843+openshwprojects@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:56:10 +0100 Subject: [PATCH] RF driver (receiver RF directly with SYN590R or similar) * test * T * T * W * Update RCSwitch.cpp * t * w * Update RCSwitch.h * Update RCSwitch.cpp * w * ww * Update RCSwitch.cpp * t * ttt * test * dbg * Update RCSwitch.cpp * Update RCSwitch.cpp * ww * Update drv_rc.cpp * c * Update RCSwitch.h * make * fix * Update drv_rc.cpp * wwww * wwwwwwwwwww * Update drv_rc.cpp * fix * dsfsdsd * IOR_RCRecv * Update RCSwitch.cpp * test * ffffffffffffx * sds * try * fx * tttttttt * fixes * fx * hold * hide debug vars --- platforms/obk_main.mk | 2 + src/cmnds/cmd_eventHandlers.c | 2 + src/cmnds/cmd_public.h | 2 + src/driver/drv_main.c | 17 + src/driver/drv_rc.cpp | 101 ++ src/driver/drv_rc.h | 17 + src/httpserver/new_http.c | 2 + src/libraries/rc-switch/.gitignore | 17 + src/libraries/rc-switch/README.md | 16 + .../ReceiveDemo_Advanced.ino | 24 + .../examples/ReceiveDemo_Advanced/output.ino | 70 ++ .../ReceiveDemo_Simple/ReceiveDemo_Simple.ino | 29 + .../rc-switch/examples/SendDemo/SendDemo.ino | 57 + .../TypeA_WithDIPSwitches.ino | 40 + .../TypeA_WithDIPSwitches_Lightweight.ino | 43 + .../TypeB_WithRotaryOrSlidingSwitches.ino | 40 + .../TypeC_Intertechno/TypeC_Intertechno.ino | 40 + .../examples/TypeD_REV/TypeD_REV.ino | 41 + .../examples/Webserver/Webserver.ino | 154 +++ src/libraries/rc-switch/keywords.txt | 57 + src/libraries/rc-switch/library.json | 19 + src/libraries/rc-switch/library.properties | 10 + src/libraries/rc-switch/platformio.ini | 103 ++ src/libraries/rc-switch/src/RCSwitch.cpp | 1039 +++++++++++++++++ src/libraries/rc-switch/src/RCSwitch.h | 212 ++++ src/new_pins.h | 14 + src/obk_config.h | 1 + 27 files changed, 2169 insertions(+) create mode 100644 src/driver/drv_rc.cpp create mode 100644 src/driver/drv_rc.h create mode 100644 src/libraries/rc-switch/.gitignore create mode 100644 src/libraries/rc-switch/README.md create mode 100644 src/libraries/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino create mode 100644 src/libraries/rc-switch/examples/ReceiveDemo_Advanced/output.ino create mode 100644 src/libraries/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino create mode 100644 src/libraries/rc-switch/examples/SendDemo/SendDemo.ino create mode 100644 src/libraries/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino create mode 100644 src/libraries/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino create mode 100644 src/libraries/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino create mode 100644 src/libraries/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino create mode 100644 src/libraries/rc-switch/examples/TypeD_REV/TypeD_REV.ino create mode 100644 src/libraries/rc-switch/examples/Webserver/Webserver.ino create mode 100644 src/libraries/rc-switch/keywords.txt create mode 100644 src/libraries/rc-switch/library.json create mode 100644 src/libraries/rc-switch/library.properties create mode 100644 src/libraries/rc-switch/platformio.ini create mode 100644 src/libraries/rc-switch/src/RCSwitch.cpp create mode 100644 src/libraries/rc-switch/src/RCSwitch.h diff --git a/platforms/obk_main.mk b/platforms/obk_main.mk index f6db33ad9..fc32ae59f 100644 --- a/platforms/obk_main.mk +++ b/platforms/obk_main.mk @@ -171,7 +171,9 @@ OBKM_SRC += $(OBK_SRCS)i2c/drv_i2c_mcp23017.c OBKM_SRC += $(OBK_SRCS)i2c/drv_i2c_tc74.c OBKM_SRC_CXX += $(OBK_SRCS)driver/drv_ir.cpp +OBKM_SRC_CXX += $(OBK_SRCS)driver/drv_rc.cpp OBKM_SRC_CXX += $(OBK_SRCS)driver/drv_ir_new.cpp +OBKM_SRC_CXX += $(OBK_SRCS)libraries/rc-switch/src/RCSwitch.cpp OBKM_SRC_CXX += $(OBK_SRCS)libraries/IRremoteESP8266/src/IRac.cpp OBKM_SRC_CXX += $(OBK_SRCS)libraries/IRremoteESP8266/src/IRproto.cpp OBKM_SRC_CXX += $(OBK_SRCS)libraries/IRremoteESP8266/src/IRrecv.cpp diff --git a/src/cmnds/cmd_eventHandlers.c b/src/cmnds/cmd_eventHandlers.c index 95697d1f1..faa0812b2 100644 --- a/src/cmnds/cmd_eventHandlers.c +++ b/src/cmnds/cmd_eventHandlers.c @@ -234,6 +234,8 @@ int EVENT_ParseEventName(const char *s) { return CMD_EVENT_ON_HTTP; if (!stricmp(s, "OnDiscovery")) return CMD_EVENT_ON_DISCOVERY; + if (!stricmp(s, "RC")) + return CMD_EVENT_RC; if (isdigit((unsigned char)*s)) { return atoi(s); } diff --git a/src/cmnds/cmd_public.h b/src/cmnds/cmd_public.h index 09e8c06d8..c3f7cc2b4 100644 --- a/src/cmnds/cmd_public.h +++ b/src/cmnds/cmd_public.h @@ -172,6 +172,8 @@ enum EventCode { CMD_EVENT_ON_DISCOVERY, + CMD_EVENT_RC, + // must be lower than 256 CMD_EVENT_MAX_TYPES }; diff --git a/src/driver/drv_main.c b/src/driver/drv_main.c index 1f480c4fb..217ec6f74 100644 --- a/src/driver/drv_main.c +++ b/src/driver/drv_main.c @@ -5,6 +5,7 @@ #include "drv_bl_shared.h" #include "drv_cse7766.h" #include "drv_ir.h" +#include "drv_rc.h" #include "drv_local.h" #include "drv_ntp.h" #include "drv_deviceclock.h" @@ -757,6 +758,22 @@ static driver_t g_drivers[] = { false, // loaded }, #endif +#if ENABLE_DRIVER_RC + //drvdetail:{"name":"RC", + //drvdetail:"title":"TODO", + //drvdetail:"descr":"", + //drvdetail:"requires":""} + { "RC", // Driver Name + DRV_RC_Init, // Init + NULL, // onEverySecond + RC_AppendInformationToHTTPIndexPage, // appendInformationToHTTPIndexPage + DRV_RC_RunFrame, // runQuickTick + NULL, // stopFunction + NULL, // onChannelChanged + NULL, // onHassDiscovery + false, // loaded + }, +#endif #if ENABLE_DRIVER_IR2 //drvdetail:{"name":"IR2", //drvdetail:"title":"TODO", diff --git a/src/driver/drv_rc.cpp b/src/driver/drv_rc.cpp new file mode 100644 index 000000000..6f91b809d --- /dev/null +++ b/src/driver/drv_rc.cpp @@ -0,0 +1,101 @@ + +#include "../obk_config.h" + +#if ENABLE_DRIVER_RC + +extern "C" { + // these cause error: conflicting declaration of 'int bk_wlan_mcu_suppress_and_sleep(unsigned int)' with 'C' linkage +#include "../new_common.h" +#include "../httpserver/new_http.h" +#include "../logging/logging.h" +#include "../new_pins.h" +#include "../new_cfg.h" +#include "../cmnds/cmd_public.h" + +} + +#include "drv_rc.h" +#include "../libraries/rc-switch/src/RCSwitch.h" + +RCSwitch mySwitch = RCSwitch(); +void DRV_RC_Init() { + // allow user to change them + int pin = -1; + bool pup = true; + pin = PIN_FindPinIndexForRole(IOR_RCRecv, pin); + if (pin == -1) + { + pin = PIN_FindPinIndexForRole(IOR_RCRecv_nPup, pin); + if (pin >= 0) + pup = false; + } + ADDLOG_INFO(LOG_FEATURE_IR, "DRV_RC_Init: passing pin %i\n", pin); + mySwitch.enableReceive(pin, pup); // Receiver on interrupt 0 => that is pin #2 +} +//extern long g_micros; +//extern int rc_triggers; +//extern int g_rcpin; +//extern int rc_checkedProtocols; +//extern int rc_singleRepeats; +//extern int rc_repeats; +static int rc_totalDecoded = 0; + +void RC_AppendInformationToHTTPIndexPage(http_request_t *request, int bPreState) { + + if (bPreState) { + } + else { + hprintf255(request, "

RC signals decoded: %i

", rc_totalDecoded); + //hprintf255(request, "

Triggers: %i

", (int)rc_triggers); + //hprintf255(request, "

Micros: %i

", (int)g_micros); + //hprintf255(request, "

g_rcpin: %i

", (int)g_rcpin); + //hprintf255(request, "

rc_checkedProtocols: %i

", (int)rc_checkedProtocols); + //hprintf255(request, "

rc_singleRepeats: %i

", (int)rc_singleRepeats); + //hprintf255(request, "

rc_repeats: %i

", (int)rc_repeats); + } +} +unsigned long rc_prev = 0; +int loopsUntilClear = 0; +void DRV_RC_RunFrame() { + if (loopsUntilClear) { + loopsUntilClear--; + if (loopsUntilClear <= 0) { + rc_prev = 0; + ADDLOG_INFO(LOG_FEATURE_IR, "Clearing hold timer\n"); + } + } + if (mySwitch.available()) { + rc_totalDecoded++; + + unsigned long rc_now = mySwitch.getReceivedValue(); + int bHold = 0; + if (rc_now != rc_prev) { + rc_prev = rc_now; + } + else { + bHold = 1; + } + loopsUntilClear = 15; + // TODO 64 bit + // generic + // addEventHandler RC 1234 toggleChannel 5 123 + // on first receive + // addEventHandler2 RC 1234 0 toggleChannel 5 123 + // on hold + // addEventHandler2 RC 1234 1 toggleChannel 5 123 + ADDLOG_INFO(LOG_FEATURE_IR, "Received %lu / %u bit protocol %u, hold %i\n", + rc_now, + mySwitch.getReceivedBitlength(), + mySwitch.getReceivedProtocol(), + bHold); + EventHandlers_FireEvent2(CMD_EVENT_RC, mySwitch.getReceivedValue(), bHold); + + + mySwitch.resetAvailable(); + } +} + + +#endif + + diff --git a/src/driver/drv_rc.h b/src/driver/drv_rc.h new file mode 100644 index 000000000..482bdddc1 --- /dev/null +++ b/src/driver/drv_rc.h @@ -0,0 +1,17 @@ +#ifndef __DRV_RC_H__ +#define __DRV_RC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void DRV_RC_Init(); +void DRV_RC_RunFrame(); +void RC_AppendInformationToHTTPIndexPage(http_request_t *request, int bPreState); + +#ifdef __cplusplus +} + +#endif +#endif + diff --git a/src/httpserver/new_http.c b/src/httpserver/new_http.c index c80bc494d..e927cc153 100644 --- a/src/httpserver/new_http.c +++ b/src/httpserver/new_http.c @@ -558,6 +558,8 @@ const char* htmlPinRoleNames[] = { "StripState", "StripState_n", "HLW_8112_SCSN", + "RCRecv", + "RCRecv_nPup", "error", "error", "error", diff --git a/src/libraries/rc-switch/.gitignore b/src/libraries/rc-switch/.gitignore new file mode 100644 index 000000000..d0972bb47 --- /dev/null +++ b/src/libraries/rc-switch/.gitignore @@ -0,0 +1,17 @@ +# Mac stuff +.DS_Store + +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a + diff --git a/src/libraries/rc-switch/README.md b/src/libraries/rc-switch/README.md new file mode 100644 index 000000000..8debb6d7d --- /dev/null +++ b/src/libraries/rc-switch/README.md @@ -0,0 +1,16 @@ +**Modified Tasmota Fork of RC-SWITCH by @sui77 and @1technophile** + +## Info + +This will most likely work with all popular low cost power outlet sockets. + +All you need is a 315/433MHz AM transmitter and one +or more devices with one of the supported chipsets: + + - SC5262 / SC5272 + - HX2262 / HX2272 + - PT2262 / PT2272 + - EV1527 / RT1527 / FP1527 / HS1527 + - Intertechno outlets + - HT6P20X + diff --git a/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino b/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino new file mode 100644 index 000000000..18380d336 --- /dev/null +++ b/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/ReceiveDemo_Advanced.ino @@ -0,0 +1,24 @@ +/* + Example for receiving + + https://github.com/sui77/rc-switch/ + + If you want to visualize a telegram copy the raw data and + paste it into http://test.sui.li/oszi/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 +} + +void loop() { + if (mySwitch.available()) { + output(mySwitch.getReceivedValue(), mySwitch.getReceivedBitlength(), mySwitch.getReceivedDelay(), mySwitch.getReceivedRawdata(),mySwitch.getReceivedProtocol()); + mySwitch.resetAvailable(); + } +} diff --git a/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/output.ino b/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/output.ino new file mode 100644 index 000000000..31e61ba69 --- /dev/null +++ b/src/libraries/rc-switch/examples/ReceiveDemo_Advanced/output.ino @@ -0,0 +1,70 @@ +static const char* bin2tristate(const char* bin); +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength); + +void output(unsigned long decimal, unsigned int length, unsigned int delay, unsigned int* raw, unsigned int protocol) { + + const char* b = dec2binWzerofill(decimal, length); + Serial.print("Decimal: "); + Serial.print(decimal); + Serial.print(" ("); + Serial.print( length ); + Serial.print("Bit) Binary: "); + Serial.print( b ); + Serial.print(" Tri-State: "); + Serial.print( bin2tristate( b) ); + Serial.print(" PulseLength: "); + Serial.print(delay); + Serial.print(" microseconds"); + Serial.print(" Protocol: "); + Serial.println(protocol); + + Serial.print("Raw data: "); + for (unsigned int i=0; i<= length*2; i++) { + Serial.print(raw[i]); + Serial.print(","); + } + Serial.println(); + Serial.println(); +} + +static const char* bin2tristate(const char* bin) { + static char returnValue[50]; + int pos = 0; + int pos2 = 0; + while (bin[pos]!='\0' && bin[pos+1]!='\0') { + if (bin[pos]=='0' && bin[pos+1]=='0') { + returnValue[pos2] = '0'; + } else if (bin[pos]=='1' && bin[pos+1]=='1') { + returnValue[pos2] = '1'; + } else if (bin[pos]=='0' && bin[pos+1]=='1') { + returnValue[pos2] = 'F'; + } else { + return "not applicable"; + } + pos = pos+2; + pos2++; + } + returnValue[pos2] = '\0'; + return returnValue; +} + +static char * dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { + static char bin[64]; + unsigned int i=0; + + while (Dec > 0) { + bin[32+i++] = ((Dec & 1) > 0) ? '1' : '0'; + Dec = Dec >> 1; + } + + for (unsigned int j = 0; j< bitLength; j++) { + if (j >= bitLength - i) { + bin[j] = bin[ 31 + i - (j - (bitLength - i)) ]; + } else { + bin[j] = '0'; + } + } + bin[bitLength] = '\0'; + + return bin; +} diff --git a/src/libraries/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino b/src/libraries/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino new file mode 100644 index 000000000..bb1076387 --- /dev/null +++ b/src/libraries/rc-switch/examples/ReceiveDemo_Simple/ReceiveDemo_Simple.ino @@ -0,0 +1,29 @@ +/* + Simple example for receiving + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + Serial.begin(9600); + mySwitch.enableReceive(0); // Receiver on interrupt 0 => that is pin #2 +} + +void loop() { + if (mySwitch.available()) { + + Serial.print("Received "); + Serial.print( mySwitch.getReceivedValue() ); + Serial.print(" / "); + Serial.print( mySwitch.getReceivedBitlength() ); + Serial.print("bit "); + Serial.print("Protocol: "); + Serial.println( mySwitch.getReceivedProtocol() ); + + mySwitch.resetAvailable(); + } +} diff --git a/src/libraries/rc-switch/examples/SendDemo/SendDemo.ino b/src/libraries/rc-switch/examples/SendDemo/SendDemo.ino new file mode 100644 index 000000000..5acb4958c --- /dev/null +++ b/src/libraries/rc-switch/examples/SendDemo/SendDemo.ino @@ -0,0 +1,57 @@ +/* + Example for different sending methods + + https://github.com/sui77/rc-switch/ + +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + Serial.begin(9600); + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set protocol (default is 1, will work for most outlets) + // mySwitch.setProtocol(2); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + + // Optional set number of transmission repetitions. + // mySwitch.setRepeatTransmit(15); + +} + +void loop() { + + /* See Example: TypeA_WithDIPSwitches */ + mySwitch.switchOn("11111", "00010"); + delay(1000); + mySwitch.switchOff("11111", "00010"); + delay(1000); + + /* Same switch as above, but using decimal code */ + mySwitch.send(5393, 24); + delay(1000); + mySwitch.send(5396, 24); + delay(1000); + + /* Same switch as above, but using binary code */ + mySwitch.send("000000000001010100010001"); + delay(1000); + mySwitch.send("000000000001010100010100"); + delay(1000); + + /* Same switch as above, but tri-state code */ + mySwitch.sendTriState("00000FFF0F0F"); + delay(1000); + mySwitch.sendTriState("00000FFF0FF0"); + delay(1000); + + delay(20000); +} diff --git a/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino b/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino new file mode 100644 index 000000000..14f7d2a6c --- /dev/null +++ b/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches/TypeA_WithDIPSwitches.ino @@ -0,0 +1,40 @@ +/* + Example for outlets which are configured with a 10 pole DIP switch. + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first 5 DIP switches. + // In this example it's ON-ON-OFF-OFF-ON. + // + // The second parameter represents the setting of the last 5 DIP switches. + // In this example the last 5 DIP switches are OFF-ON-OFF-ON-OFF. + mySwitch.switchOn("11001", "01010"); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff("11001", "01010"); + + // Wait another second + delay(1000); + +} diff --git a/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino b/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino new file mode 100644 index 000000000..214daf41a --- /dev/null +++ b/src/libraries/rc-switch/examples/TypeA_WithDIPSwitches_Lightweight/TypeA_WithDIPSwitches_Lightweight.ino @@ -0,0 +1,43 @@ +/* + This is a minimal sketch without using the library at all but only works for + the 10 pole dip switch sockets. It saves a lot of memory and thus might be + very useful to use with ATTinys :) + + https://github.com/sui77/rc-switch/ +*/ + +int RCLpin = 7; + +void setup() { + pinMode(RCLpin, OUTPUT); +} + +void loop() { + RCLswitch(0b010001000001); // DIPs an Steckdose: 0100010000 An:01 + delay(2000); + + RCLswitch(0b010001000010); // DIPs an Steckdose: 0100010000 Aus:10 + delay(2000); +} + +void RCLswitch(uint16_t code) { + for (int nRepeat=0; nRepeat<6; nRepeat++) { + for (int i=4; i<16; i++) { + RCLtransmit(1,3); + if (((code << (i-4)) & 2048) > 0) { + RCLtransmit(1,3); + } else { + RCLtransmit(3,1); + } + } + RCLtransmit(1,31); + } +} + +void RCLtransmit(int nHighPulses, int nLowPulses) { + digitalWrite(RCLpin, HIGH); + delayMicroseconds( 350 * nHighPulses); + digitalWrite(RCLpin, LOW); + delayMicroseconds( 350 * nLowPulses); +} + diff --git a/src/libraries/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino b/src/libraries/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino new file mode 100644 index 000000000..e8568e25b --- /dev/null +++ b/src/libraries/rc-switch/examples/TypeB_WithRotaryOrSlidingSwitches/TypeB_WithRotaryOrSlidingSwitches.ino @@ -0,0 +1,40 @@ +/* + Example for outlets which are configured with two rotary/sliding switches. + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the setting of the first rotary switch. + // In this example it's switched to "1" or "A" or "I". + // + // The second parameter represents the setting of the second rotary switch. + // In this example it's switched to "4" or "D" or "IV". + mySwitch.switchOn(1, 4); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff(1, 4); + + // Wait another second + delay(1000); + +} diff --git a/src/libraries/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino b/src/libraries/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino new file mode 100644 index 000000000..0fc69c7c8 --- /dev/null +++ b/src/libraries/rc-switch/examples/TypeC_Intertechno/TypeC_Intertechno.ino @@ -0,0 +1,40 @@ +/* + Example for Intertechno outlets + + https://github.com/sui77/rc-switch/ +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // Optional set pulse length. + // mySwitch.setPulseLength(320); + +} + +void loop() { + + // Switch on: + // The first parameter represents the familycode (a, b, c, ... f) + // The second parameter represents the group number + // The third parameter represents the device number + // + // In this example it's family 'b', group #3, device #2 + mySwitch.switchOn('b', 3, 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('b', 3, 2); + + // Wait another second + delay(1000); + +} \ No newline at end of file diff --git a/src/libraries/rc-switch/examples/TypeD_REV/TypeD_REV.ino b/src/libraries/rc-switch/examples/TypeD_REV/TypeD_REV.ino new file mode 100644 index 000000000..878272945 --- /dev/null +++ b/src/libraries/rc-switch/examples/TypeD_REV/TypeD_REV.ino @@ -0,0 +1,41 @@ +/* + Example for REV outlets (e.g. 8342L) + + https://github.com/sui77/rc-switch/ + + Need help? http://forum.ardumote.com +*/ + +#include + +RCSwitch mySwitch = RCSwitch(); + +void setup() { + + // Transmitter is connected to Arduino Pin #10 + mySwitch.enableTransmit(10); + + // set pulse length. + mySwitch.setPulseLength(360); + +} + +void loop() { + + // Switch on: + // The first parameter represents the channel (a, b, c, d) + // The second parameter represents the device number + // + // In this example it's family 'd', device #2 + mySwitch.switchOn('d', 2); + + // Wait a second + delay(1000); + + // Switch off + mySwitch.switchOff('d', 2); + + // Wait another second + delay(1000); + +} diff --git a/src/libraries/rc-switch/examples/Webserver/Webserver.ino b/src/libraries/rc-switch/examples/Webserver/Webserver.ino new file mode 100644 index 000000000..66668e76a --- /dev/null +++ b/src/libraries/rc-switch/examples/Webserver/Webserver.ino @@ -0,0 +1,154 @@ +/* + A simple RCSwitch/Ethernet/Webserver demo + + https://github.com/sui77/rc-switch/ +*/ + +#include +#include +#include + +// Ethernet configuration +uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // MAC Address +uint8_t ip[] = { 192,168,0, 2 }; // IP Address +EthernetServer server(80); // Server Port 80 + +// RCSwitch configuration +RCSwitch mySwitch = RCSwitch(); +int RCTransmissionPin = 7; + +// More to do... +// You should also modify the processCommand() and +// httpResponseHome() functions to fit your needs. + + + +/** + * Setup + */ +void setup() { + Ethernet.begin(mac, ip); + server.begin(); + mySwitch.enableTransmit( RCTransmissionPin ); +} + +/** + * Loop + */ +void loop() { + char* command = httpServer(); +} + +/** + * Command dispatcher + */ +void processCommand(char* command) { + if (strcmp(command, "1-on") == 0) { + mySwitch.switchOn(1,1); + } else if (strcmp(command, "1-off") == 0) { + mySwitch.switchOff(1,1); + } else if (strcmp(command, "2-on") == 0) { + mySwitch.switchOn(1,2); + } else if (strcmp(command, "2-off") == 0) { + mySwitch.switchOff(1,2); + } +} + +/** + * HTTP Response with homepage + */ +void httpResponseHome(EthernetClient c) { + c.println("HTTP/1.1 200 OK"); + c.println("Content-Type: text/html"); + c.println(); + c.println(""); + c.println(""); + c.println( "RCSwitch Webserver Demo"); + c.println( ""); + c.println(""); + c.println(""); + c.println( "

RCSwitch Webserver Demo

"); + c.println( ""); + c.println( ""); + c.println( "
"); + c.println( "https://github.com/sui77/rc-switch/"); + c.println(""); + c.println(""); +} + +/** + * HTTP Redirect to homepage + */ +void httpResponseRedirect(EthernetClient c) { + c.println("HTTP/1.1 301 Found"); + c.println("Location: /"); + c.println(); +} + +/** + * HTTP Response 414 error + * Command must not be longer than 30 characters + **/ +void httpResponse414(EthernetClient c) { + c.println("HTTP/1.1 414 Request URI too long"); + c.println("Content-Type: text/plain"); + c.println(); + c.println("414 Request URI too long"); +} + +/** + * Process HTTP requests, parse first request header line and + * call processCommand with GET query string (everything after + * the ? question mark in the URL). + */ +char* httpServer() { + EthernetClient client = server.available(); + if (client) { + char sReturnCommand[32]; + int nCommandPos=-1; + sReturnCommand[0] = '\0'; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + if ((c == '\n') || (c == ' ' && nCommandPos>-1)) { + sReturnCommand[nCommandPos] = '\0'; + if (strcmp(sReturnCommand, "\0") == 0) { + httpResponseHome(client); + } else { + processCommand(sReturnCommand); + httpResponseRedirect(client); + } + break; + } + if (nCommandPos>-1) { + sReturnCommand[nCommandPos++] = c; + } + if (c == '?' && nCommandPos == -1) { + nCommandPos = 0; + } + } + if (nCommandPos > 30) { + httpResponse414(client); + sReturnCommand[0] = '\0'; + break; + } + } + if (nCommandPos!=-1) { + sReturnCommand[nCommandPos] = '\0'; + } + // give the web browser time to receive the data + delay(1); + client.stop(); + + return sReturnCommand; + } + return '\0'; +} \ No newline at end of file diff --git a/src/libraries/rc-switch/keywords.txt b/src/libraries/rc-switch/keywords.txt new file mode 100644 index 000000000..2474367c4 --- /dev/null +++ b/src/libraries/rc-switch/keywords.txt @@ -0,0 +1,57 @@ +####################################### +# Syntax Coloring Map For RCSwitch +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +RCSwitch KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +########## +#SENDS Begin +########## +switchOn KEYWORD2 +switchOff KEYWORD2 +sendTriState KEYWORD2 +send KEYWORD2 +########## +#SENDS End +########## + +########## +#RECEIVE Begin +########## +enableReceive KEYWORD2 +disableReceive KEYWORD2 +available KEYWORD2 +resetAvailable KEYWORD2 +setReceiveTolerance KEYWORD2 +getReceivedValue KEYWORD2 +getReceivedBitlength KEYWORD2 +getReceivedDelay KEYWORD2 +getReceivedProtocol KEYWORD2 +getReceivedRawdata KEYWORD2 +########## +#RECEIVE End +########## + +########## +#OTHERS Begin +########## +enableTransmit KEYWORD2 +disableTransmit KEYWORD2 +setPulseLength KEYWORD2 +setProtocol KEYWORD2 +setRepeatTransmit KEYWORD2 +########## +#OTHERS End +########## + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/src/libraries/rc-switch/library.json b/src/libraries/rc-switch/library.json new file mode 100644 index 000000000..0d6a337f4 --- /dev/null +++ b/src/libraries/rc-switch/library.json @@ -0,0 +1,19 @@ +{ + "name": "rc-switch", + "description": "Use ESP8266/ESP32 to operate remote radio controlled devices", + "keywords": "rf, radio, wireless", + "authors": + { + "name": "Theo Arends" + }, + "repository": + { + "type": "git", + "url": "https://github.com/arendst/Tasmota.git" + }, + "version": "1.0.0", + "frameworks": [ + "arduino" + ], + "platforms": "*" +} diff --git a/src/libraries/rc-switch/library.properties b/src/libraries/rc-switch/library.properties new file mode 100644 index 000000000..09a0193fe --- /dev/null +++ b/src/libraries/rc-switch/library.properties @@ -0,0 +1,10 @@ +name=rc-switch +version=1.0.0 +author=@sui77 @1technophile and more +maintainer=Theo Arends +sentence=Operate 433/315Mhz devices. +paragraph=Use ESP8266/ESP32 to operate remote radio controlled devices. +category=Device Control +url=https://github.com/arendst/Tasmota +architectures=esp8266,esp32 +includes=RCSwitch.h diff --git a/src/libraries/rc-switch/platformio.ini b/src/libraries/rc-switch/platformio.ini new file mode 100644 index 000000000..74999c992 --- /dev/null +++ b/src/libraries/rc-switch/platformio.ini @@ -0,0 +1,103 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[platformio] +src_dir = examples/ReceiveDemo_Advanced + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ENVIRONMENT CHOICE ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Uncomment the env line corresponding to your board and modules required, ; +;you can also adapt the modules by removing the corresponding lines in the env detail ; +; if you go to the build flag section of your env you will see that some user_config.h ; +; parameters can be overwritten here, for example the gateway name. ; +; If you want to avoid the lost of your environments at each update you can put them ; +; into a separate file called prod_env.ini, it will be automatically read by pio ; +; an example (prod_env.ini.example) is available into the same folder as this file. ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;default_envs = sonoff-basic-rfr3 +;default_envs = esp32dev-rf +;default_envs = nodemcuv2-rf +;default_envs = rf-wifi-gateway +;default_envs = uno-rf + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ENVIRONMENTS PARAMETERS ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;Libraries and parameters shared accross environements ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + + +[env] +lib_extra_dirs = . +framework = arduino +build_flags = + -w ; supress all warnings +monitor_speed = 115200 + +[com] +esp8266_platform = espressif8266@2.2.3 +esp32_platform = espressif32@1.11.1 +atmelavr_platform = atmelavr@1.13.0 + +[com-esp] +build_flags = + ${env.build_flags} + +[com-arduino] +build_flags = + ${env.build_flags} + +[com-arduino-low-memory] +build_flags = + ${env.build_flags} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ENVIRONMENTS LIST ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;List of environments that can be build ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[env:esp32dev-rf] +platform = ${com.esp32_platform} +board = esp32dev +build_flags = + ${com-esp.build_flags} + +[env:nodemcuv2-rf] +platform = ${com.esp8266_platform} +board = nodemcuv2 +build_flags = + ${com-esp.build_flags} +board_build.flash_mode = dout + +[env:rf-wifi-gateway] +platform = ${com.esp8266_platform} +board = nodemcuv2 +build_flags = + ${com-esp.build_flags} +board_build.flash_mode = dout + + +[env:sonoff-basic-rfr3] +platform = ${com.esp8266_platform} +board = esp8285 +build_flags = + ${com-esp.build_flags} +board_build.flash_mode = dout + +[env:uno-rf] +platform = ${com.atmelavr_platform} +board = uno +build_flags = + ${com-arduino-low-memory.build_flags} diff --git a/src/libraries/rc-switch/src/RCSwitch.cpp b/src/libraries/rc-switch/src/RCSwitch.cpp new file mode 100644 index 000000000..45d2abf31 --- /dev/null +++ b/src/libraries/rc-switch/src/RCSwitch.cpp @@ -0,0 +1,1039 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Andreas Steinel / A.(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + - Johann Richard / .(at)gmail(dot)com + - Vlad Gheorghe / .(at)gmail(dot)com https://github.com/vgheo + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "RCSwitch.h" + +#ifdef RaspberryPi + // PROGMEM and _P functions are for AVR based microprocessors, + // so we must normalize these for the ARM processor: + #define PROGMEM + #define memcpy(dest, src, num) memcpy((dest), (src), (num)) +#endif + +#if defined(ESP8266) + // interrupt handler and related code must be in RAM on ESP8266, + // according to issue #46. + #define RECEIVE_ATTR ICACHE_RAM_ATTR + #define VAR_ISR_ATTR +#elif defined(ESP32) + #define RECEIVE_ATTR IRAM_ATTR + #define VAR_ISR_ATTR DRAM_ATTR +#else + #define RECEIVE_ATTR + #define VAR_ISR_ATTR + + +#endif + + + +#if PLATFORM_BEKEN + +extern "C" { +#include "include.h" +#include "arm_arch.h" +#include "bk_timer_pub.h" +#include "drv_model_pub.h" + +#include +//#include "pwm.h" +#include "pwm_pub.h" + +#include "../../beken378/func/include/net_param_pub.h" +#include "../../beken378/func/user_driver/BkDriverPwm.h" +#include "../../beken378/func/user_driver/BkDriverI2c.h" +#include "../../beken378/driver/i2c/i2c1.h" +#include "../../beken378/driver/gpio/gpio.h" +#include "../../beken378/driver/pwm/pwm.h" +#if PLATFORM_BK7231N +#if PLATFORM_BEKEN_NEW +#include "pwm_bk7231n.h" +#else +#include "../../beken378/driver/pwm/pwm_new.h" +#endif +#endif +} + + +#endif + + +/* Protocol description format + * + * { + * Pulse length, + * + * PreambleFactor, + * Preamble {high,low}, + * + * HeaderFactor, + * Header {high,low}, + * + * "0" bit {high,low}, + * "1" bit {high,low}, + * + * Inverted Signal, + * Guard time + * } + * + * Pulse length: pulse duration (Te) in microseconds, + * for example 350 + * PreambleFactor: Number of high and low states to send + * (One pulse = 2 states, in orther words, number of pulses is + * ceil(PreambleFactor/2).) + * Preamble: Pulse shape which defines a preamble bit. + * Sent ceil(PreambleFactor/2) times. + * For example, {1, 2} with factor 3 would send + * _ _ + * | |__| |__ (each horizontal bar has a duration of Te, + * vertical bars are ignored) + * HeaderFactor: Number of times to send the header pulse. + * Header: Pulse shape which defines a header (or "sync"/"clock") pulse. + * {1, 31} means one pulse of duration 1 Te high and 31 Te low + * _ + * | |_______________________________ (don't count the vertical bars) + * + * "0" bit: pulse shape defining a data bit, which is a logical "0" + * {1, 3} means 1 pulse duration Te high level and 3 low + * _ + * | |___ + * + * "1" bit: pulse shape that defines the data bit, which is a logical "1" + * {3, 1} means 3 pulses with a duration of Te high level and 1 low + * ___ + * | |_ + * + * (note: to form the state bit Z (Tri-State bit), two codes are combined) + * + * Inverted Signal: Signal inversion - if true the signal is inverted + * replacing high to low in a transmitted / received packet + * Guard time: Separation time between two retries. It will be followed by the + * next preamble of the next packet. In number of Te. + * e.g. 39 pulses of duration Te low level + */ + +#if defined(ESP8266) || defined(ESP32) +static const VAR_ISR_ATTR RCSwitch::Protocol proto[] = { +#else +static const RCSwitch::Protocol proto[] = { +#endif + { 350, 0, { 0, 0 }, 1, { 1, 31 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 01 (Princeton, PT-2240) + { 650, 0, { 0, 0 }, 1, { 1, 10 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 02 + { 100, 0, { 0, 0 }, 1, { 30, 71 }, { 4, 11 }, { 9, 6 }, false, 0 }, // 03 + { 380, 0, { 0, 0 }, 1, { 1, 6 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 04 + { 500, 0, { 0, 0 }, 1, { 6, 14 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 05 + { 450, 0, { 0, 0 }, 1, { 23, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 06 (HT6P20B) + { 150, 0, { 0, 0 }, 1, { 2, 62 }, { 1, 6 }, { 6, 1 }, false, 0 }, // 07 (HS2303-PT, i. e. used in AUKEY Remote) + { 320, 0, { 0, 0 }, 1, { 36, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 08 (Came 12bit, HT12E) + { 700, 0, { 0, 0 }, 1, { 32, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 09 (Nice_Flo 12bit) + { 420, 0, { 0, 0 }, 1, { 60, 6 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 10 (V2 phoenix) + { 500, 2, { 3, 3 }, 0, { 0, 0 }, { 1, 2 }, { 2, 1 }, false, 37 }, // 11 (Nice_FloR-S 52bit) + { 400, 23, { 1, 1 }, 1, { 0, 9 }, { 2, 1 }, { 1, 2 }, false, 39 }, // 12 Placeholder not working! (Keeloq 64/66) + { 300, 6, { 2, 2 }, 3, { 8, 3 }, { 2, 2 }, { 3, 3 }, false, 0 }, // 13 test (CFM) + { 250, 12, { 4, 4 }, 0, { 0, 0 }, { 1, 1 }, { 2, 2 }, false, 0 }, // 14 test (StarLine) + { 500, 0, { 0, 0 }, 0, { 100, 1 }, { 1, 2 }, { 2, 1 }, false, 35 }, // 15 + + { 361, 0, { 0, 0 }, 1, { 52, 1 }, { 1, 3 }, { 3, 1 }, true, 0 }, // 16 (Einhell) + { 500, 0, { 0, 0 }, 1, { 1, 23 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 17 (InterTechno PAR-1000) + { 180, 0, { 0, 0 }, 1, { 1, 15 }, { 1, 1 }, { 1, 8 }, false, 0 }, // 18 (Intertechno ITT-1500) + { 350, 0, { 0, 0 }, 1, { 1, 2 }, { 0, 2 }, { 3, 2 }, false, 0 }, // 19 (Murcury) + { 150, 0, { 0, 0 }, 1, { 34, 3 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 20 (AC114) + { 360, 0, { 0, 0 }, 1, { 13, 4 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 21 (DC250) + { 650, 0, { 0, 0 }, 1, { 1, 10 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 22 (Mandolyn/Lidl TR-502MSV/RC-402/RC-402DX) + { 641, 0, { 0, 0 }, 1, { 115, 1 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 23 (Lidl TR-502MSV/RC-402 - Flavien) + { 620, 0, { 0, 0 }, 1, { 0, 64 }, { 0, 1 }, { 1, 0 }, false, 0 }, // 24 (Lidl TR-502MSV/RC701) + { 560, 0, { 0, 0 }, 1, { 16, 8 }, { 1, 1 }, { 1, 3 }, false, 0 }, // 25 (NEC) + { 385, 0, { 0, 0 }, 1, { 1, 17 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 26 (Arlec RC210) + { 188, 0, { 0, 0 }, 1, { 1, 31 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 27 (Zap, FHT-7901) + + { 700, 1, { 0, 1 }, 1, { 116, 0 }, { 1, 2 }, { 2, 1 }, true, 0 }, // 28 (Quigg GT-7000) from @Tho85 https://github.com/sui77/rc-switch/pull/115 + { 220, 0, { 0, 0 }, 1, { 1, 46 }, { 1, 6 }, { 1, 1 }, false, 2 }, // 29 (NEXA) + { 260, 0, { 0, 0 }, 1, { 1, 8 }, { 1, 4 }, { 4, 1 }, true, 0 }, // 30 (Anima) + + { 400, 0, { 0, 0 }, 1, { 1, 1 }, { 1, 2 }, { 2, 1 }, false, 43 }, // 31 (Mertik Maxitrol G6R-H4T1) + { 365, 0, { 0, 0 }, 1, { 18, 1 }, { 3, 1 }, { 1, 3 }, true, 0 }, // 32 (1ByOne Doorbell) from @Fatbeard https://github.com/sui77/rc-switch/pull/277 + { 340, 0, { 0, 0 }, 1, { 14, 4 }, { 1, 2 }, { 2, 1 }, false, 0 }, // 33 (Dooya Control DC2708L) + { 120, 0, { 0, 0 }, 1, { 1, 28 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 34 DIGOO SD10 - so as to use this protocol RCSWITCH_SEPARATION_LIMIT must be set to 2600 + { 20, 0, { 0, 0 }, 1, { 239, 78 }, {20, 35 }, {35, 20}, false, 10000},// 35 Dooya 5-Channel blinds remote DC1603 + { 250, 0, { 0, 0 }, 1, { 18, 6 }, { 1, 3 }, { 3, 1 }, false, 0 }, // 36 Dooya remote DC2700AC for Dooya DT82TV curtains motor + { 200, 0, { 0, 0 }, 0, { 0, 0 }, { 1, 3 }, { 3, 1 }, false, 20 }, // 37 DEWENWILS Power Strip + { 500, 0, { 0, 0 }, 1, { 7, 1 }, { 2, 1 }, { 4, 1 }, true, 0 }, // 38 temperature and humidity sensor, various brands, nexus protocol, 36 bits + start impulse + { 560, 0, { 0, 0 }, 1, { 15, 1 }, { 3, 1 }, { 7, 1 }, true, 0 } // 39 Hyundai WS Senzor 77/77TH, 36 bits (requires disabled protocol 38: 'RfProtocol38 0') +}; + +int numProto = sizeof(proto) / sizeof(proto[0]); + +#if not defined( RCSwitchDisableReceiving ) +volatile unsigned long long RCSwitch::nReceivedValue = 0; +volatile unsigned long long RCSwitch::nReceiveProtocolMask; +volatile unsigned int RCSwitch::nReceivedBitlength = 0; +volatile unsigned int RCSwitch::nReceivedDelay = 0; +volatile unsigned int RCSwitch::nReceivedProtocol = 0; +int RCSwitch::nReceiveTolerance = 60; +unsigned int RCSwitch::nSeparationLimit = RCSWITCH_SEPARATION_LIMIT; +unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; +unsigned int RCSwitch::buftimings[4]; +#endif + +RCSwitch::RCSwitch() { + this->nTransmitterPin = -1; + this->setRepeatTransmit(5); + this->setProtocol(1); + #if not defined( RCSwitchDisableReceiving ) + this->nReceiverInterrupt = -1; + this->setReceiveTolerance(60); + RCSwitch::nReceivedValue = 0; + RCSwitch::nReceiveProtocolMask = (1ULL << numProto)-1; //pow(2,numProto)-1; + #endif + RCSwitch::nReceiveProtocolMask = -1; +} + +uint8_t RCSwitch::getNumProtos() { + return numProto; +} +/** + * Sets the protocol to send. + */ +void RCSwitch::setProtocol(Protocol protocol) { + this->protocol = protocol; +} + +/** + * Sets the protocol to send, from a list of predefined protocols + */ +void RCSwitch::setProtocol(int nProtocol) { + if (nProtocol < 1 || nProtocol > numProto) { + nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ??? + } +#if defined(ESP8266) || defined(ESP32) + this->protocol = proto[nProtocol-1]; +#else + memcpy(&this->protocol, &proto[nProtocol-1], sizeof(Protocol)); +#endif +} + +/** + * Sets the protocol to send with pulse length in microseconds. + */ +void RCSwitch::setProtocol(int nProtocol, int nPulseLength) { + setProtocol(nProtocol); + this->setPulseLength(nPulseLength); +} + + +/** + * Sets pulse length in microseconds + */ +void RCSwitch::setPulseLength(int nPulseLength) { + this->protocol.pulseLength = nPulseLength; +} + +/** + * Sets Repeat Transmits + */ +void RCSwitch::setRepeatTransmit(int nRepeatTransmit) { + this->nRepeatTransmit = nRepeatTransmit; +} + +/** + * Set Receiving Tolerance + */ +#if not defined( RCSwitchDisableReceiving ) +void RCSwitch::setReceiveTolerance(int nPercent) { + RCSwitch::nReceiveTolerance = nPercent; +} + +bool RCSwitch::setReceiveProtocolMask(unsigned long long mask) { + RCSwitch::nReceiveProtocolMask = mask; + return updateSeparationLimit(); +} + +bool RCSwitch::updateSeparationLimit() +{ + unsigned int longestPulseTime = UINT_MAX; + unsigned int shortestPulseTime = 0; + + unsigned long long thisMask = 1; + for(unsigned int i = 0; i < numProto; i++) { + //if (RCSwitch::nReceiveProtocolMask & thisMask) + { + const unsigned int headerShortPulseCount = MIN(proto[i].Header.high, proto[i].Header.low); + const unsigned int headerLongPulseCount = MAX(proto[i].Header.high, proto[i].Header.low); + + // This must be the longest pulse-length of this protocol. nSeparationLimit must of this length or shorter. + // This pulse will be used to detect the beginning of a transmission. + const unsigned int headerLongPulseTime = proto[i].pulseLength * headerLongPulseCount; + + // nSeparationLimit must be longer than any of the following pulses to avoid detecting a new transmission in the middle of a frame. + unsigned int longestDataPulseCount = headerShortPulseCount; + longestDataPulseCount = MAX(longestDataPulseCount, proto[i].zero.high); + longestDataPulseCount = MAX(longestDataPulseCount, proto[i].zero.low); + longestDataPulseCount = MAX(longestDataPulseCount, proto[i].one.high); + longestDataPulseCount = MAX(longestDataPulseCount, proto[i].one.low); + + const unsigned int longestDataPulseTime = proto[i].pulseLength * longestDataPulseCount; + + longestPulseTime = MIN(longestPulseTime, headerLongPulseTime); + shortestPulseTime = MAX(shortestPulseTime, longestDataPulseTime); + } + thisMask <<= 1; + } + + if (longestPulseTime <= shortestPulseTime) { + // incompatible protocols enabled, fall back to default value + nSeparationLimit = RCSWITCH_SEPARATION_LIMIT; + return false; + } + + const unsigned int timeDiff = longestPulseTime - shortestPulseTime; + nSeparationLimit = longestPulseTime - (timeDiff / 2); + return true; +} +#endif + + +/** + * Enable transmissions + * + * @param nTransmitterPin Arduino Pin to which the sender is connected to + */ +void RCSwitch::enableTransmit(int nTransmitterPin) { + this->nTransmitterPin = nTransmitterPin; + HAL_PIN_Setup_Output(this->nTransmitterPin); +} + +/** + * Disable transmissions + */ +void RCSwitch::disableTransmit() { + this->nTransmitterPin = -1; +} + +/** + * Switch a remote switch on (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOn(char sGroup, int nDevice) { + this->sendTriState( this->getCodeWordD(sGroup, nDevice, true) ); +} + +/** + * Switch a remote switch off (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOff(char sGroup, int nDevice) { + this->sendTriState( this->getCodeWordD(sGroup, nDevice, false) ); +} + +/** + * Switch a remote switch on (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) { + this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, true) ); +} + +/** + * Switch a remote switch off (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) { + this->sendTriState( this->getCodeWordC(sFamily, nGroup, nDevice, false) ); +} + +/** + * Switch a remote switch on (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOn(int nAddressCode, int nChannelCode) { + this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, true) ); +} + +/** + * Switch a remote switch off (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOff(int nAddressCode, int nChannelCode) { + this->sendTriState( this->getCodeWordB(nAddressCode, nChannelCode, false) ); +} + +/** + * Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOn(const char* sGroup, int nChannel) { + const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; + this->switchOn(sGroup, code[nChannel]); +} + +/** + * Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOff(const char* sGroup, int nChannel) { + const char* code[6] = { "00000", "10000", "01000", "00100", "00010", "00001" }; + this->switchOff(sGroup, code[nChannel]); +} + +/** + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOn(const char* sGroup, const char* sDevice) { + this->sendTriState( this->getCodeWordA(sGroup, sDevice, true) ); +} + +/** + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOff(const char* sGroup, const char* sDevice) { + this->sendTriState( this->getCodeWordA(sGroup, sDevice, false) ); +} + + +/** + * Returns a char[13], representing the code word to be send. + * + */ +char* RCSwitch::getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0'; + } + + for (int i = 0; i < 5; i++) { + sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0'; + } + + sReturn[nReturnPos++] = bStatus ? '0' : 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for type B switches with two rotary/sliding switches. + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-----------------------------+----------+------------+ + * | 4 bits address | 4 bits address | 3 bits | 1 bit | + * | switch group | switch number | not used | on / off | + * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 | + * +-----------------------------+-----------------------------+----------+------------+ + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) { + return 0; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F'; + } + + for (int i = 1; i <= 4; i++) { + sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F'; + } + + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Like getCodeWord (Type C = Intertechno) + */ +char* RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + int nFamily = (int)sFamily - 'a'; + if ( nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) { + return 0; + } + + // encode the family into four bits + sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0'; + + // encode the device and group + sReturn[nReturnPos++] = ((nDevice-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nDevice-1) & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup-1) & 2) ? 'F' : '0'; + + // encode the status code + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for the REV Switch Type + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-------------------+----------+--------------+ + * | 4 bits address | 3 bits address | 3 bits | 2 bits | + * | switch group | device number | not used | on / off | + * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 | + * +-----------------------------+-------------------+----------+--------------+ + * + * Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/ + * + * @param sGroup Name of the switch group (A..D, resp. a..d) + * @param nDevice Number of the switch itself (1..3) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char* RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) { + static char sReturn[13]; + int nReturnPos = 0; + + // sGroup must be one of the letters in "abcdABCD" + int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A'; + if ( nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) { + return 0; + } + + for (int i = 0; i < 4; i++) { + sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F'; + } + + for (int i = 1; i <= 3; i++) { + sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F'; + } + + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + + sReturn[nReturnPos++] = bStatus ? '1' : '0'; + sReturn[nReturnPos++] = bStatus ? '0' : '1'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * @param sCodeWord a tristate code word consisting of the letter 0, 1, F + */ +void RCSwitch::sendTriState(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 2L; + switch (*p) { + case '0': + // bit pattern 00 + break; + case 'F': + // bit pattern 01 + code |= 1ULL; + break; + case '1': + // bit pattern 11 + code |= 3ULL; + break; + } + length += 2; + } + this->sendGeneric(code, length); +} + +/** + * @param duration no. of microseconds to delay + */ +static inline void safeDelayMicroseconds(unsigned long duration) { +#if defined(ESP8266) || defined(ESP32) + if (duration > 10000) { + // if delay > 10 milliseconds, use yield() to avoid wdt reset + unsigned long start = micros(); + while ((micros() - start) < duration) { + yield(); + } + } + else { + usleep(duration); + } +#else + usleep(duration); +#endif +} + +/** + * @param sCodeWord a binary code word consisting of the letter 0, 1 + */ +void RCSwitch::sendGeneric(const char* sCodeWord) { + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long long code = 0; + unsigned int length = 0; + for (const char* p = sCodeWord; *p; p++) { + code <<= 1ULL; + if (*p != '0') + code |= 1ULL; + length++; + } + this->sendGeneric(code, length); +} + +/** + * Transmit the first 'length' bits of the integer 'code'. The + * bits are sent from MSB to LSB, i.e., first the bit at position length-1, + * then the bit at position length-2, and so on, till finally the bit at position 0. + */ +void RCSwitch::sendGeneric(unsigned long long code, unsigned int length) { + if (this->nTransmitterPin == -1) + return; + +#if not defined( RCSwitchDisableReceiving ) + // make sure the receiver is disabled while we transmit + int nReceiverInterrupt_backup = nReceiverInterrupt; + if (nReceiverInterrupt_backup != -1) { + this->disableReceive(); + } +#endif + + // repeat sending the packet nRepeatTransmit times + for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) { + // send the preamble + for (int i = 0; i < ((protocol.PreambleFactor / 2) + (protocol.PreambleFactor %2 )); i++) { + this->transmit({protocol.Preamble.high, protocol.Preamble.low}); + } + // send the header + if (protocol.HeaderFactor > 0) { + for (int i = 0; i < protocol.HeaderFactor; i++) { + this->transmit(protocol.Header); + } + } + // send the code + for (int i = length - 1; i >= 0; i--) { + if (code & (1ULL << i)) + this->transmit(protocol.one); + else + this->transmit(protocol.zero); + } + // Set the guard Time + if (protocol.Guard > 0) { + HAL_PIN_SetOutputValue(this->nTransmitterPin, 0); + safeDelayMicroseconds(this->protocol.pulseLength * protocol.Guard); + } + } + + // Disable transmit after sending (i.e., for inverted protocols) + HAL_PIN_SetOutputValue(this->nTransmitterPin, 0); + +#if not defined( RCSwitchDisableReceiving ) + // enable receiver again if we just disabled it + if (nReceiverInterrupt_backup != -1) { + this->enableReceive(nReceiverInterrupt_backup, bUsePullUp); + } +#endif +} + +/** + * Transmit a single high-low pulse. + */ +void RCSwitch::transmit(HighLow pulses) { + uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? 0 : 1; + uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? 1 : 0; + + if (pulses.high > 0) { + HAL_PIN_SetOutputValue(this->nTransmitterPin, firstLogicLevel); + usleep( this->protocol.pulseLength * pulses.high); + } + if (pulses.low > 0) { + HAL_PIN_SetOutputValue(this->nTransmitterPin, secondLogicLevel); + usleep( this->protocol.pulseLength * pulses.low); + } +} + + +#if not defined( RCSwitchDisableReceiving ) +/** + * Enable receiving data + */ +void RCSwitch::enableReceive(int interrupt, bool pup) { + this->nReceiverInterrupt = interrupt; + this->bUsePullUp = pup; + this->enableReceive(); +} + + +/** + * Disable receiving data + */ +void RCSwitch::disableReceive() { +#if not defined(RaspberryPi) // Arduino + HAL_DetachInterrupt(this->nReceiverInterrupt); +#endif // For Raspberry Pi (wiringPi) you can't unregister the ISR + this->nReceiverInterrupt = -1; +} + +bool RCSwitch::available() { + return RCSwitch::nReceivedValue != 0; +} + +void RCSwitch::resetAvailable() { + RCSwitch::nReceivedValue = 0; +} + +unsigned long long RCSwitch::getReceivedValue() { + return RCSwitch::nReceivedValue; +} + +unsigned int RCSwitch::getReceivedBitlength() { + return RCSwitch::nReceivedBitlength; +} + +unsigned int RCSwitch::getReceivedDelay() { + return RCSwitch::nReceivedDelay; +} + +unsigned int RCSwitch::getReceivedProtocol() { + return RCSwitch::nReceivedProtocol; +} + +unsigned int* RCSwitch::getReceivedRawdata() { + return RCSwitch::timings; +} + +/* helper function for the receiveProtocol method */ +static inline unsigned int diff(int A, int B) { + return abs(A - B); +} + +/** + * + */ +bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) { +#if defined(ESP8266) || defined(ESP32) + const Protocol &pro = proto[p-1]; +#else + Protocol pro; + memcpy(&pro, &proto[p-1], sizeof(Protocol)); +#endif + + unsigned long long code = 0; + unsigned int FirstTiming = 0; + if (pro.PreambleFactor > 0) { + FirstTiming = pro.PreambleFactor + 1; + } + unsigned int BeginData = 0; + if (pro.HeaderFactor > 0) { + BeginData = (pro.invertedSignal) ? (2) : (1); + // Header pulse count correction for more than one + if (pro.HeaderFactor > 1) { + BeginData += (pro.HeaderFactor - 1) * 2; + } + } + //Assuming the longer pulse length is the pulse captured in timings[FirstTiming] + // берем наибольшее значение из Header + const unsigned int syncLengthInPulses = ((pro.Header.low) > (pro.Header.high)) ? (pro.Header.low) : (pro.Header.high); + // определяем длительность Te как длительность первого импульса header деленную на количество импульсов в нем + // или как длительность импульса preamble деленную на количество Te в нем + unsigned int sdelay = 0; + if (syncLengthInPulses > 0) { + sdelay = RCSwitch::timings[FirstTiming] / syncLengthInPulses; + } else if (pro.PreambleFactor > 0) { + sdelay = RCSwitch::timings[FirstTiming-2] / pro.PreambleFactor; + } + const unsigned int delay = sdelay; + // nReceiveTolerance = 60 + // допустимое отклонение длительностей импульсов на 60 % + const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100; + + // 0 - sync перед preamble или data + // BeginData - сдвиг на 1 или 2 от sync к preamble/data + // FirstTiming - сдвиг на preamble к header + // firstDataTiming первый импульс data + // bitChangeCount - количество импульсов в data + + /* For protocols that start low, the sync period looks like + * _________ + * _____________| |XXXXXXXXXXXX| + * + * |--1st dur--|-2nd dur-|-Start data-| + * + * The 3rd saved duration starts the data. + * + * For protocols that start high, the sync period looks like + * + * ______________ + * | |____________|XXXXXXXXXXXXX| + * + * |-filtered out-|--1st dur--|--Start data--| + * + * The 2nd saved duration starts the data + */ + // если invertedSignal=false, то сигнал начинается с 1 элемента массива (высокий уровень) + // если invertedSignal=true, то сигнал начинается со 2 элемента массива (низкий уровень) + // добавляем поправку на Преамбулу и Хедер + const unsigned int firstDataTiming = BeginData + FirstTiming; + unsigned int bitChangeCount = changeCount - firstDataTiming - 1 + pro.invertedSignal; + if (bitChangeCount > 128) { + bitChangeCount = 128; + } + + for (unsigned int i = firstDataTiming; i < firstDataTiming + bitChangeCount; i += 2) { + code <<= 1; + if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) { + // zero + } else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) { + // one + code |= 1; + } else { + // Failed + return false; + } + } + + if (bitChangeCount > 14) { // ignore very short transmissions: no device sends them, so this must be noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = bitChangeCount / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = p; + return true; + } + + return false; +} + +volatile long g_micros = 0; +volatile int rc_triggers = 0; +int prev = 0; +int g_rcpin = 0; +static const uint32_t ir_periodus = 50; +void RC_ISR(uint8_t t) { + g_micros += ir_periodus; + int n = HAL_PIN_ReadDigitalInput(g_rcpin); + if (n != prev) { + prev = n; + rc_triggers++; + RCSwitch::handleInterrupt(0); + } +} +static uint32_t ir_chan +#if PLATFORM_BEKEN += BKTIMER0 +#endif +; +static uint32_t ir_div = 1; +void obk_startTimer() { +#if PLATFORM_BEKEN + timer_param_t params = { + (unsigned char)ir_chan, + (unsigned char)ir_div, // div + ir_periodus, // us + RC_ISR + }; + //GLOBAL_INT_DECLARATION(); + + + UINT32 res; + // test what error we get with an invalid command + res = sddev_control((char *)TIMER_DEV_NAME, -1, 0); + + + + + //ADDLOG_INFO(LOG_FEATURE_IR, (char *)"ir timer init"); + // do not need to do this + //bk_timer_init(); + //ADDLOG_INFO(LOG_FEATURE_IR, (char *)"ir timer init done"); + //ADDLOG_INFO(LOG_FEATURE_IR, (char *)"will ir timer setup %u", res); + res = sddev_control((char *)TIMER_DEV_NAME, CMD_TIMER_INIT_PARAM_US, ¶ms); + //ADDLOG_INFO(LOG_FEATURE_IR, (char *)"ir timer setup %u", res); + res = sddev_control((char *)TIMER_DEV_NAME, CMD_TIMER_UNIT_ENABLE, &ir_chan); + //ADDLOG_INFO(LOG_FEATURE_IR, (char *)"ir timer enabled %u", res); +#endif +} +void RCSwitch::enableReceive() { + if (this->nReceiverInterrupt != -1) { + RCSwitch::nReceivedValue = 0; + RCSwitch::nReceivedBitlength = 0; + // attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE); + //HAL_AttachInterrupt(this->nReceiverInterrupt, INTERRUPT_CHANGE, handleInterrupt); + if (bUsePullUp) { + HAL_PIN_Setup_Input_Pullup(this->nReceiverInterrupt); + ADDLOG_INFO(LOG_FEATURE_IR, "RC: Using pin %i (with pullup)\n", this->nReceiverInterrupt); + } + else { + HAL_PIN_Setup_Input(this->nReceiverInterrupt); + ADDLOG_INFO(LOG_FEATURE_IR, "RC: Using pin %i (no pullup)\n", this->nReceiverInterrupt); + } + g_rcpin = this->nReceiverInterrupt; + obk_startTimer(); + } +} +int rc_checkedProtocols = 0; +int rc_singleRepeats = 0; +int rc_repeats = 0; +void RECEIVE_ATTR RCSwitch::handleInterrupt(int xyz) { + + static unsigned int changeCount = 0; + static unsigned long lastTime = 0; + static unsigned char repeatCount = 0; + + const long time = g_micros; // micros(); + const unsigned int duration = time - lastTime; + + RCSwitch::buftimings[3]=RCSwitch::buftimings[2]; + RCSwitch::buftimings[2]=RCSwitch::buftimings[1]; + RCSwitch::buftimings[1]=RCSwitch::buftimings[0]; + RCSwitch::buftimings[0]=duration; + + if (duration > RCSwitch::nSeparationLimit || + changeCount == 156 || + (diff(RCSwitch::buftimings[3], RCSwitch::buftimings[2]) < 50 && + diff(RCSwitch::buftimings[2], RCSwitch::buftimings[1]) < 50 && + changeCount > 25)) { + // принят длинный импульс продолжительностью более nSeparationLimit (4300) + // A long stretch without signal level change occurred. This could + // be the gap between two transmission. + if (diff(duration, RCSwitch::timings[0]) < 400 || + changeCount == 156 || + (diff(RCSwitch::buftimings[3], RCSwitch::timings[1]) < 50 && + diff(RCSwitch::buftimings[2], RCSwitch::timings[2]) < 50 && + diff(RCSwitch::buftimings[1], RCSwitch::timings[3]) < 50 && + changeCount > 25)) { + // если его длительность отличается от первого импульса, + // который приняли раньше, менее чем на +-200 (исходно 200) + // то считаем это повторным пакетом и игнорируем его + // This long signal is close in length to the long signal which + // started the previously recorded timings; this suggests that + // it may indeed by a a gap between two transmissions (we assume + // here that a sender will send the signal multiple times, + // with roughly the same gap between them). + + // количество повторных пакетов + repeatCount++; + rc_repeats++; + // при приеме второго повторного начинаем анализ принятого первым + if (repeatCount == 1) { + rc_singleRepeats++; + unsigned long long thismask = 1; + for(unsigned int i = 1; i <= numProto; i++) { + //if (RCSwitch::nReceiveProtocolMask & thismask) + { + rc_checkedProtocols++; + if (receiveProtocol(i, changeCount)) { + // receive succeeded for protocol i + break; + } + } + thismask <<= 1; + } + // очищаем количество повторных пакетов + repeatCount = 0; + } + } + // дительность отличается более чем на +-200 от первого + // принятого ранее, очищаем счетчик для приема нового пакета + changeCount = 0; + if (diff(RCSwitch::buftimings[3], RCSwitch::buftimings[2]) < 50 && + diff(RCSwitch::buftimings[2], RCSwitch::buftimings[1]) < 50) { + RCSwitch::timings[1]=RCSwitch::buftimings[3]; + RCSwitch::timings[2]=RCSwitch::buftimings[2]; + RCSwitch::timings[3]=RCSwitch::buftimings[1]; + changeCount = 4; + } + } + + // detect overflow + if (changeCount >= RCSWITCH_MAX_CHANGES) { + changeCount = 0; + repeatCount = 0; + } + + // заносим в массив длительность очередного принятого импульса + if (changeCount > 0 && duration < 100) { // игнорируем шумовые всплески менее 100 мкс + RCSwitch::timings[changeCount-1] += duration; + } else { + RCSwitch::timings[changeCount++] = duration; + } + lastTime = time; +} +#endif diff --git a/src/libraries/rc-switch/src/RCSwitch.h b/src/libraries/rc-switch/src/RCSwitch.h new file mode 100644 index 000000000..ac7dda38d --- /dev/null +++ b/src/libraries/rc-switch/src/RCSwitch.h @@ -0,0 +1,212 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _RCSwitch_h +#define _RCSwitch_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific + #include "Energia.h" +#elif defined(RPI) // Raspberry Pi + #define RaspberryPi + + // Include libraries for RPi: + #include /* memcpy */ + #include /* abs */ + #include +#elif defined(SPARK) + #include "application.h" +#else + // #include "WProgram.h" + #include /* memcpy */ + #include /* abs */ +extern "C" { +#include "../../../obk_config.h" +#include "../../../new_common.h" +#include "../../../logging/logging.h" +#include "../../../hal/hal_pins.h" +} +#endif + +#include + + +// At least for the ATTiny X4/X5, receiving has to be disabled due to +// missing libm depencies (udivmodhi4) +#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) +#define RCSwitchDisableReceiving +#endif + +// Number of maximum high/Low changes per packet. +// We can handle up to 36 bit * 2 H/L changes per bit + 2 for sync +// Для keeloq нужно увеличить RCSWITCH_MAX_CHANGES до 23+1+66*2+1=157 +//#define RCSWITCH_MAX_CHANGES 75 // default 75 - longest protocol that requires this buffer size is 38/nexus +#define RCSWITCH_MAX_CHANGES 131 // default 75 - Supports 64 too + +// separationLimit: minimum microseconds between received codes, closer codes are ignored. +// according to discussion on issue #14 it might be more suitable to set the separation +// limit to the same time as the 'low' part of the sync signal for the current protocol. +// should be set to the minimum value of pulselength * the sync signal +#define RCSWITCH_SEPARATION_LIMIT 3600 + +class RCSwitch { + + public: + RCSwitch(); + + void switchOn(int nGroupNumber, int nSwitchNumber); + void switchOff(int nGroupNumber, int nSwitchNumber); + void switchOn(const char* sGroup, int nSwitchNumber); + void switchOff(const char* sGroup, int nSwitchNumber); + void switchOn(char sFamily, int nGroup, int nDevice); + void switchOff(char sFamily, int nGroup, int nDevice); + void switchOn(const char* sGroup, const char* sDevice); + void switchOff(const char* sGroup, const char* sDevice); + void switchOn(char sGroup, int nDevice); + void switchOff(char sGroup, int nDevice); + + void sendTriState(const char* sCodeWord); + void sendGeneric(unsigned long long code, unsigned int length); + void sendGeneric(const char* sCodeWord); + + #if not defined( RCSwitchDisableReceiving ) + void enableReceive(int interrupt, bool pup); + void enableReceive(); + void disableReceive(); + bool available(); + void resetAvailable(); + + unsigned long long getReceivedValue(); + unsigned int getReceivedBitlength(); + unsigned int getReceivedDelay(); + unsigned int getReceivedProtocol(); + unsigned int* getReceivedRawdata(); + uint8_t getNumProtos(); + #endif + + void enableTransmit(int nTransmitterPin); + void disableTransmit(); + void setPulseLength(int nPulseLength); + void setRepeatTransmit(int nRepeatTransmit); + #if not defined( RCSwitchDisableReceiving ) + void setReceiveTolerance(int nPercent); + bool setReceiveProtocolMask(unsigned long long mask); + #endif + + /** + * Description of a single pule, which consists of a high signal + * whose duration is "high" times the base pulse length, followed + * by a low signal lasting "low" times the base pulse length. + * Thus, the pulse overall lasts (high+low)*pulseLength + */ + struct HighLow { + uint8_t high; + uint8_t low; + }; + + /** + * A "protocol" describes how zero and one bits are encoded into high/low + * pulses. + */ + struct Protocol { + /** base pulse length in microseconds, e.g. 350 */ + uint16_t pulseLength; + uint8_t PreambleFactor; + HighLow Preamble; + uint8_t HeaderFactor; + HighLow Header; + + HighLow zero; + HighLow one; + + /** + * If true, interchange high and low logic levels in all transmissions. + * + * By default, RCSwitch assumes that any signals it sends or receives + * can be broken down into pulses which start with a high signal level, + * followed by a a low signal level. This is e.g. the case for the + * popular PT 2260 encoder chip, and thus many switches out there. + * + * But some devices do it the other way around, and start with a low + * signal level, followed by a high signal level, e.g. the HT6P20B. To + * accommodate this, one can set invertedSignal to true, which causes + * RCSwitch to change how it interprets any HighLow struct FOO: It will + * then assume transmissions start with a low signal lasting + * FOO.high*pulseLength microseconds, followed by a high signal lasting + * FOO.low*pulseLength microseconds. + */ + bool invertedSignal; + uint16_t Guard; + }; + + void setProtocol(Protocol protocol); + void setProtocol(int nProtocol); + void setProtocol(int nProtocol, int nPulseLength); + + private: + char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus); + char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus); + char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus); + char* getCodeWordD(char group, int nDevice, bool bStatus); + void transmit(HighLow pulses); + + #if not defined( RCSwitchDisableReceiving ) +public: + static void handleInterrupt(int xyz); +private: + static bool receiveProtocol(const int p, unsigned int changeCount); + static bool updateSeparationLimit(); + int nReceiverInterrupt; + bool bUsePullUp; + #endif + int nTransmitterPin; + int nRepeatTransmit; + Protocol protocol; + + #if not defined( RCSwitchDisableReceiving ) + static int nReceiveTolerance; + volatile static unsigned long long nReceivedValue; + volatile static unsigned long long nReceiveProtocolMask; + volatile static unsigned int nReceivedBitlength; + volatile static unsigned int nReceivedDelay; + volatile static unsigned int nReceivedProtocol; + static unsigned int nSeparationLimit; + /* + * timings[0] contains sync timing, followed by a number of bits + */ + static unsigned int timings[RCSWITCH_MAX_CHANGES]; + // буфер длительностей последних четырех пакетов, [0] - последний + static unsigned int buftimings[4]; + #endif + + +}; + +#endif diff --git a/src/new_pins.h b/src/new_pins.h index 06bb63da2..6f9239f97 100644 --- a/src/new_pins.h +++ b/src/new_pins.h @@ -616,6 +616,20 @@ typedef enum ioRole_e { //iodetail:"file":"new_pins.h", //iodetail:"driver":"HLW8112SPI"} IOR_HLW8112_SCSN, + //iodetail:{"name":"IOR_RCRecv", + //iodetail:"title":"IOR_RCRecv Pin", + //iodetail:"descr":"IOR_RCRecv.", + //iodetail:"enum":"IOR_RCRecv", + //iodetail:"file":"new_pins.h", + //iodetail:"driver":"RC"} + IOR_RCRecv, + //iodetail:{"name":"IOR_RCRecv_nPup", + //iodetail:"title":"IOR_RCRecv_nPup Pin", + //iodetail:"descr":"IOR_RCRecv_nPup.", + //iodetail:"enum":"IOR_RCRecv_nPup", + //iodetail:"file":"new_pins.h", + //iodetail:"driver":"RC"} + IOR_RCRecv_nPup, //iodetail:{"name":"Total_Options", //iodetail:"title":"TODO", //iodetail:"descr":"Current total number of available IOR roles", diff --git a/src/obk_config.h b/src/obk_config.h index 87444eded..815af035f 100644 --- a/src/obk_config.h +++ b/src/obk_config.h @@ -289,6 +289,7 @@ #define ENABLE_DRIVER_DDP 1 #define ENABLE_DRIVER_SSDP 1 #define ENABLE_DRIVER_IR 1 +#define ENABLE_DRIVER_RC 1 // #define ENABLE_DRIVER_IR2 1 #define ENABLE_DRIVER_DS1820 1 #define ENABLE_DRIVER_CHT83XX 1