From caa501b1afa16afddb052b7f130b61e2bb372a6e Mon Sep 17 00:00:00 2001 From: Steffen <14131163+slo617@users.noreply.github.com> Date: Sun, 1 Sep 2024 16:04:40 +0200 Subject: [PATCH] add support for nexus protocol to rc-switch library (#21886) * add support for nexus protocol to RCSwitch library Nexus protocol is used by temperature and humidity sensor that operate at 433 MHz. It is used by various brands. * calc separation limit for RCSwitch library automatically --- lib/lib_rf/rc-switch/src/RCSwitch.cpp | 51 +++++++++++++++++-- lib/lib_rf/rc-switch/src/RCSwitch.h | 9 ++-- .../tasmota_xdrv_driver/xdrv_17_rcswitch.ino | 7 ++- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/lib/lib_rf/rc-switch/src/RCSwitch.cpp b/lib/lib_rf/rc-switch/src/RCSwitch.cpp index 05d2a8313..4f3940e81 100644 --- a/lib/lib_rf/rc-switch/src/RCSwitch.cpp +++ b/lib/lib_rf/rc-switch/src/RCSwitch.cpp @@ -151,8 +151,9 @@ static const RCSwitch::Protocol PROGMEM proto[] = { { 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 + { 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 }; enum { @@ -166,7 +167,7 @@ volatile unsigned int RCSwitch::nReceivedBitlength = 0; volatile unsigned int RCSwitch::nReceivedDelay = 0; volatile unsigned int RCSwitch::nReceivedProtocol = 0; int RCSwitch::nReceiveTolerance = 60; -const unsigned int RCSwitch::nSeparationLimit = RCSWITCH_SEPARATION_LIMIT; +unsigned int RCSwitch::nSeparationLimit = RCSWITCH_SEPARATION_LIMIT; unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; unsigned int RCSwitch::buftimings[4]; #endif @@ -238,8 +239,50 @@ void RCSwitch::setReceiveTolerance(int nPercent) { RCSwitch::nReceiveTolerance = nPercent; } -void RCSwitch::setReceiveProtocolMask(unsigned long long mask) { +bool RCSwitch::setReceiveProtocolMask(unsigned long long mask) { RCSwitch::nReceiveProtocolMask = mask; + return updateSeparationLimit(); +} + +bool RCSwitch::updateSeparationLimit() +{ + unsigned int longestPulseTime = std::numeric_limits::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 = std::min(proto[i].Header.high, proto[i].Header.low); + const unsigned int headerLongPulseCount = std::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 = std::max(longestDataPulseCount, proto[i].zero.high); + longestDataPulseCount = std::max(longestDataPulseCount, proto[i].zero.low); + longestDataPulseCount = std::max(longestDataPulseCount, proto[i].one.high); + longestDataPulseCount = std::max(longestDataPulseCount, proto[i].one.low); + + const unsigned int longestDataPulseTime = proto[i].pulseLength * longestDataPulseCount; + + longestPulseTime = std::min(longestPulseTime, headerLongPulseTime); + shortestPulseTime = std::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 diff --git a/lib/lib_rf/rc-switch/src/RCSwitch.h b/lib/lib_rf/rc-switch/src/RCSwitch.h index 6d660c0db..28d946d65 100644 --- a/lib/lib_rf/rc-switch/src/RCSwitch.h +++ b/lib/lib_rf/rc-switch/src/RCSwitch.h @@ -57,9 +57,9 @@ #endif // Number of maximum high/Low changes per packet. -// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync +// 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 67 // default 67 +#define RCSWITCH_MAX_CHANGES 75 // default 75 - longest protocol that requires this buffer size is 38/nexus // 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 @@ -108,7 +108,7 @@ class RCSwitch { void setRepeatTransmit(int nRepeatTransmit); #if not defined( RCSwitchDisableReceiving ) void setReceiveTolerance(int nPercent); - void setReceiveProtocolMask(unsigned long long mask); + bool setReceiveProtocolMask(unsigned long long mask); #endif /** @@ -171,6 +171,7 @@ class RCSwitch { #if not defined( RCSwitchDisableReceiving ) static void handleInterrupt(); static bool receiveProtocol(const int p, unsigned int changeCount); + static bool updateSeparationLimit(); int nReceiverInterrupt; #endif int nTransmitterPin; @@ -184,7 +185,7 @@ class RCSwitch { volatile static unsigned int nReceivedBitlength; volatile static unsigned int nReceivedDelay; volatile static unsigned int nReceivedProtocol; - const static unsigned int nSeparationLimit; + static unsigned int nSeparationLimit; /* * timings[0] contains sync timing, followed by a number of bits */ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino b/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino index bfd223f89..65e30acae 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_17_rcswitch.ino @@ -92,7 +92,7 @@ void RfInit(void) { if (!Settings->rf_protocol_mask) { Settings->rf_protocol_mask = (1ULL << mySwitch.getNumProtos()) -1; } - mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask); + (void)mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask); } } @@ -132,7 +132,10 @@ void CmndRfProtocol(void) { } } } - mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask); + const bool incompatibleProtocolsSelected = !mySwitch.setReceiveProtocolMask(Settings->rf_protocol_mask); + if (incompatibleProtocolsSelected) { + AddLog(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Incompatible protocols selected, using default separation limit, some protocols may not work")); + } // AddLog(LOG_LEVEL_INFO, PSTR("RFR: CmndRfProtocol:: Start responce")); Response_P(PSTR("{\"" D_CMND_RFPROTOCOL "\":\"")); bool gotone = false;