diff --git a/MyConfig.h b/MyConfig.h index 201dc04fd..352de5e5d 100755 --- a/MyConfig.h +++ b/MyConfig.h @@ -204,6 +204,17 @@ * @{ */ +/** + * @def MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION + * @brief Define this for verbose debug prints related to transport encryption. + */ +//#define MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION + +/** + * @def MY_DEBUG_VERBOSE_TRANSPORT_QUEUE + * @brief Define this for verbose debug prints related to transport queues. + */ +//#define MY_DEBUG_VERBOSE_TRANSPORT_QUEUE /** * @defgroup RS485SettingGrpPub RS485 @@ -296,6 +307,24 @@ #define MY_RADIO_RF24 #endif +/** + * @def MY_RF24_ATC + * @brief Define this to enable a rudimentary ATC on RF24 radios + */ +//#define MY_RF24_ATC + +/** + * @def MY_RF24_BC_RETRIES + * @brief Define number of broadcasting retransmissions + */ +//#define MY_RF24_BC_RETRIES + +/** + * @def MY_RF24_USE_INTERRUPTS + * @brief Define this to use interrupts for RF24-based radio communication. + */ +//#define MY_RF24_USE_INTERRUPTS + /** * @def MY_RADIO_RF24 * @brief Define this to use a RF24-based radio transport for sensor network communication. @@ -470,6 +499,85 @@ * @{ */ +/** + * @def MY_NRF5_CHL_MODE + * @brief Depending on HW and implementation CHL mode is low or high + * + */ +//#define MY_NRF5_CHL_MODE (HIGH) + +/** + * @def MY_NRF5_CHL_PIN + * @brief CHL toggle pin + * + */ +//#define MY_NRF5_CHL_PIN + +/** + * @def MY_NRF5_CPS_MODE + * @brief Depending on HW and implementation CPS mode is low or high + * + */ +//#define MY_NRF5_CPS_MODE (LOW) + +/** + * @def MY_NRF5_CPS_PIN + * @brief CPS toggle pin + * + */ +//#define MY_NRF5_CPS_PIN + +/** + * @def MY_NRF5_LNA_ENABLED + * @brief Enable LNA + * + */ +//#define MY_NRF5_LNA_ENABLED + +/** + * @def MY_NRF5_LNA_DISABLED + * @brief Disable LNA + * + */ +//#define MY_NRF5_LNA_DISABLED + +/** + * @def MY_NRF5_LNA_PIN + * @brief LNA toggle pin + * + */ +//#define MY_NRF5_LNA_PIN + + +/** + * @def MY_NRF5_PA_DISABLED + * @brief PA disabled + * + */ +//#define MY_NRF5_PA_DISABLED + + +/** + * @def MY_NRF5_PA_ENABLED + * @brief PA enabled + * + */ +//#define MY_NRF5_PA_ENABLED + +/** + * @def MY_NRF5_PA_LNA + * @brief Define to enable PA/LNA functionality + * + */ +//#define MY_NRF5_PA_LNA + +/** + * @def MY_NRF5_PA_PIN + * @brief PA toggle pin + * + */ +//#define MY_NRF5_PA_PIN + /** * @def MY_RADIO_NRF5_ESB * @brief Define this to use nRF5 based radios for sensor network communication. @@ -570,7 +678,7 @@ * @brief Declare the amount of incoming messages that can be buffered at driver level. */ #ifndef MY_NRF5_ESB_RX_BUFFER_SIZE -#define MY_NRF5_ESB_RX_BUFFER_SIZE (20) +#define MY_NRF5_ESB_RX_BUFFER_SIZE (5) #endif /** @@ -778,6 +886,16 @@ */ //#define MY_RFM69_ENABLE_ENCRYPTION +/** + * @def MY_RFM69_ENABLE_SW_ENCRYPTION + * @brief Define this to enable SW %AES encryption in the %RFM69 module. + * + * All nodes and gateway must have this enabled, and all must be personalized with the same %AES + * key. + * @see @ref personalization + */ +//#define MY_RFM69_ENABLE_SW_ENCRYPTION + /** * @def MY_RFM69_MODEM_CONFIGURATION * @brief %RFM69 modem configuration, default is %RFM69_FSK_BR55_5_FD50 @@ -2191,7 +2309,7 @@ #define MY_DEBUG_VERBOSE_OTA_UPDATE //!< MY_DEBUG_VERBOSE_OTA_UPDATE #endif -#if defined(MY_DEBUG) || defined(MY_DEBUG_VERBOSE_CORE) || defined(MY_DEBUG_VERBOSE_TRANSPORT) || defined(MY_DEBUG_VERBOSE_GATEWAY) || defined(MY_DEBUG_VERBOSE_SIGNING) || defined(MY_DEBUG_VERBOSE_OTA_UPDATE) || defined(MY_DEBUG_VERBOSE_RF24) || defined(MY_DEBUG_VERBOSE_NRF5_ESB) || defined(MY_DEBUG_VERBOSE_RFM69) || defined(MY_DEBUG_VERBOSE_RFM95) || defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) +#if defined(MY_DEBUG) || defined(MY_DEBUG_VERBOSE_CORE) || defined(MY_DEBUG_VERBOSE_TRANSPORT) || defined(MY_DEBUG_VERBOSE_GATEWAY) || defined(MY_DEBUG_VERBOSE_SIGNING) || defined(MY_DEBUG_VERBOSE_OTA_UPDATE) || defined(MY_DEBUG_VERBOSE_RF24) || defined(MY_DEBUG_VERBOSE_NRF5_ESB) || defined(MY_DEBUG_VERBOSE_RFM69) || defined(MY_DEBUG_VERBOSE_RFM95) || defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) || defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) #define DEBUG_OUTPUT_ENABLED //!< DEBUG_OUTPUT_ENABLED #ifndef MY_DEBUG_OTA #define DEBUG_OUTPUT(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug @@ -2307,6 +2425,8 @@ #define MY_OTA_LOG_RECEIVER_FEATURE #define MY_OTA_LOG_SENDER_FEATURE // transport +#define MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION +#define MY_DEBUG_VERBOSE_TRANSPORT_QUEUE #define MY_PARENT_NODE_IS_STATIC #define MY_REGISTRATION_CONTROLLER #define MY_TRANSPORT_UPLINK_CHECK_DISABLED @@ -2396,12 +2516,25 @@ #define MY_RF24_ENABLE_ENCRYPTION #define MY_RX_MESSAGE_BUFFER_FEATURE #define MY_RX_MESSAGE_BUFFER_SIZE +#define MY_RF24_ATC +#define MY_RF24_USE_INTERRUPTS // NRF5_ESB #define MY_RADIO_NRF5_ESB #define MY_NRF5_ESB_ENABLE_ENCRYPTION #define MY_DEBUG_VERBOSE_NRF5_ESB #define MY_NRF5_ESB_REVERSE_ACK_RX #define MY_NRF5_ESB_REVERSE_ACK_TX +#define MY_NRF5_CPS_PIN +#define MY_NRF5_CPS_MODE +#define MY_NRF5_CHL_PIN +#define MY_NRF5_CHL_MODE +#define MY_NRF5_PA_PIN +#define MY_NRF5_PA_ENABLED +#define MY_NRF5_PA_DISABLED +#define MY_NRF5_LNA_PIN +#define MY_NRF5_LNA_ENABLED +#define MY_NRF5_LNA_DISABLED +#define MY_NRF5_PA_LNA // RFM69 #define MY_RADIO_RFM69 #define MY_IS_RFM69HW @@ -2409,6 +2542,7 @@ #define MY_RFM69_POWER_PIN #define MY_RFM69_MODEM_CONFIGURATION #define MY_RFM69_ENABLE_ENCRYPTION +#define MY_RFM69_ENABLE_SW_ENCRYPTION #define MY_RFM69_ATC_MODE_DISABLED #define MY_RFM69_MAX_POWER_LEVEL_DBM #define MY_RFM69_RST_PIN diff --git a/MySensors.h b/MySensors.h index 36aee1702..b6c566125 100644 --- a/MySensors.h +++ b/MySensors.h @@ -286,9 +286,17 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #define __RS485CNT 0 //!< __RS485CNT #endif -#if (__RF24CNT + __NRF5ESBCNT + __RFM69CNT + __RFM95CNT + __RS485CNT > 1) -#error Only one forward link driver can be activated +#define MY_TRANSPORT_COUNT (__RF24CNT + __NRF5ESBCNT + __RFM69CNT + __RFM95CNT + __RS485CNT) //!< MY_TRANSPORT_COUNT + +#if (MY_TRANSPORT_COUNT > 1) +// more than 1 transport requires RX queue +#define MY_TRANSPORT_RX_QUEUE //!< MY_TRANSPORT_RX_QUEUE +#endif +// RF24 + IRQ requires RX queue +#if defined(MY_RADIO_RF24) && defined(MY_RF24_USE_INTERRUPTS) +#define MY_TRANSPORT_RX_QUEUE #endif + #endif //DOXYGEN // SANITY CHECK @@ -297,7 +305,7 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #endif // TRANSPORT INCLUDES -#if defined(MY_RADIO_RF24) || defined(MY_RADIO_NRF5_ESB) || defined(MY_RADIO_RFM69) || defined(MY_RADIO_RFM95) || defined(MY_RS485) +#if (MY_TRANSPORT_COUNT > 0) #include "hal/transport/MyTransportHAL.h" #include "core/MyTransport.h" @@ -352,18 +360,25 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #endif #endif +#if (defined(MY_RF24_ENABLE_ENCRYPTION) && defined(MY_RADIO_RF24)) || (defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) && defined(MY_RADIO_NRF5_ESB)) || (defined(MY_RFM69_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM69)) || (defined(MY_RFM95_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM95)) +#define MY_TRANSPORT_ENCRYPTION //!< internal flag +#include "hal/transport/MyTransportEncryption.cpp" +#endif + // Transport drivers #if defined(MY_RADIO_RF24) #include "hal/transport/RF24/driver/RF24.cpp" #include "hal/transport/RF24/MyTransportRF24.cpp" -#elif defined(MY_RADIO_NRF5_ESB) +#endif +#if defined(MY_RADIO_NRF5_ESB) #if !defined(ARDUINO_ARCH_NRF5) #error No support for nRF5 radio on this platform #endif #include "hal/transport/NRF5_ESB/driver/Radio.cpp" #include "hal/transport/NRF5_ESB/driver/Radio_ESB.cpp" #include "hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp" -#elif defined(MY_RS485) +#endif +#if defined(MY_RS485) #if !defined(MY_RS485_HWSERIAL) #if defined(__linux__) #error You must specify MY_RS485_HWSERIAL for RS485 transport @@ -371,20 +386,23 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs #include "drivers/AltSoftSerial/AltSoftSerial.cpp" #endif #include "hal/transport/RS485/MyTransportRS485.cpp" -#elif defined(MY_RADIO_RFM69) +#endif +#if defined(MY_RADIO_RFM69) #if defined(MY_RFM69_NEW_DRIVER) #include "hal/transport/RFM69/driver/new/RFM69_new.cpp" #else #include "hal/transport/RFM69/driver/old/RFM69_old.cpp" #endif #include "hal/transport/RFM69/MyTransportRFM69.cpp" -#elif defined(MY_RADIO_RFM95) +#endif +#if defined(MY_RADIO_RFM95) +#if defined(MY_RFM95_RFM69_COMPATIBILITY) +#include "hal/transport/RFM95/driver/RFM95_RFM69.cpp" +#include "hal/transport/RFM95/MyTransportRFM95_RFM69.cpp" +#else #include "hal/transport/RFM95/driver/RFM95.cpp" #include "hal/transport/RFM95/MyTransportRFM95.cpp" #endif - -#if (defined(MY_RF24_ENABLE_ENCRYPTION) && defined(MY_RADIO_RF24)) || (defined(MY_NRF5_ESB_ENABLE_ENCRYPTION) && defined(MY_RADIO_NRF5_ESB)) || (defined(MY_RFM69_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM69)) || (defined(MY_RFM95_ENABLE_ENCRYPTION) && defined(MY_RADIO_RFM95)) -#define MY_TRANSPORT_ENCRYPTION //!< ïnternal flag #endif #include "hal/transport/MyTransportHAL.cpp" diff --git a/core/MyHelperFunctions.cpp b/core/MyHelperFunctions.cpp index d3bf71f32..5fbdb3230 100644 --- a/core/MyHelperFunctions.cpp +++ b/core/MyHelperFunctions.cpp @@ -39,3 +39,33 @@ static char convertI2H(const uint8_t i) return 'A' + k - 10; } } + +static int timingneutralMemcmp(const void* a, const void* b, size_t sz) +{ + int retVal; + size_t i; + int done = 0; + const uint8_t* ptrA = (const uint8_t*)a; + const uint8_t* ptrB = (const uint8_t*)b; + for (i = 0; i < sz; i++) { + if (ptrA[i] == ptrB[i]) { + if (done > 0) { + done = 1; + } else { + done = 0; + } + } else { + if (done > 0) { + done = 2; + } else { + done = 3; + } + } + } + if (done > 0) { + retVal = -1; + } else { + retVal = 0; + } + return retVal; +} diff --git a/core/MyHelperFunctions.h b/core/MyHelperFunctions.h index 837ba754c..c61008ac1 100644 --- a/core/MyHelperFunctions.h +++ b/core/MyHelperFunctions.h @@ -34,5 +34,18 @@ static uint8_t convertH2I(const char c) __attribute__((unused)); */ static char convertI2H(const uint8_t i) __attribute__((unused)); +/** + * @brief Do a timing neutral memory comparison. + * + * The function behaves similar to memcmp with the difference that it will + * always use the same number of instructions for a given number of bytes, + * no matter how the two buffers differ and the response is either 0 or -1. + * + * @param a First buffer for comparison. + * @param b Second buffer for comparison. + * @param sz The number of bytes to compare. + * @returns 0 if buffers match, -1 if they do not. + */ +static int timingneutralMemcmp(const void* a, const void* b, size_t sz) __attribute__((unused)); #endif diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 6e4b79ac7..669718af4 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -804,7 +804,7 @@ void _nodeLock(const char *str) doYield(); (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED).set(str)); #if defined(MY_SENSOR_NETWORK) - transportSleep(); + transportHALSleep(); CORE_DEBUG(PSTR("MCO:NLK:TSL\n")); // sleep transport #endif setIndication(INDICATION_SLEEP); diff --git a/core/MySigning.cpp b/core/MySigning.cpp index 56bbf6e9f..ef4d0af87 100644 --- a/core/MySigning.cpp +++ b/core/MySigning.cpp @@ -321,36 +321,6 @@ bool signerVerifyMsg(MyMessage &msg) return verificationResult; } -int signerMemcmp(const void* a, const void* b, size_t sz) -{ - int retVal; - size_t i; - int done = 0; - const uint8_t* ptrA = (const uint8_t*)a; - const uint8_t* ptrB = (const uint8_t*)b; - for (i=0; i < sz; i++) { - if (ptrA[i] == ptrB[i]) { - if (done > 0) { - done = 1; - } else { - done = 0; - } - } else { - if (done > 0) { - done = 2; - } else { - done = 3; - } - } - } - if (done > 0) { - retVal = -1; - } else { - retVal = 0; - } - return retVal; -} - #if defined(MY_SIGNING_FEATURE) // Helper function to centralize signing/verification exceptions // cppcheck-suppress constParameter @@ -401,6 +371,11 @@ static void prepareSigningPresentation(MyMessage &msg, uint8_t destination) static bool signerInternalProcessPresentation(MyMessage &msg) { const uint8_t sender = msg.getSender(); +#if defined(MY_GATEWAY_FEATURE) && (F_CPU > 16*1000000ul) + // let's slow down things a bit - on fast GWs we may send messages before slow node's radio switches from TX->RX + delay(50); +#endif + #if defined(MY_SIGNING_FEATURE) if (msg.data[0] != SIGNING_PRESENTATION_VERSION_1) { SIGN_DEBUG(PSTR("!SGN:PRE:VER=%" PRIu8 "\n"), diff --git a/core/MySigning.h b/core/MySigning.h index 1b29f2cef..b4bb5414e 100644 --- a/core/MySigning.h +++ b/core/MySigning.h @@ -711,20 +711,6 @@ bool signerSignMsg(MyMessage &msg); */ bool signerVerifyMsg(MyMessage &msg); -/** - * @brief Do a timing neutral memory comparison. - * - * The function behaves similar to memcmp with the difference that it will - * always use the same number of instructions for a given number of bytes, - * no matter how the two buffers differ and the response is either 0 or -1. - * - * @param a First buffer for comparison. - * @param b Second buffer for comparison. - * @param sz The number of bytes to compare. - * @returns 0 if buffers match, -1 if they do not. - */ -int signerMemcmp(const void* a, const void* b, size_t sz); - #endif /** @}*/ diff --git a/core/MySigningAtsha204.cpp b/core/MySigningAtsha204.cpp index eaf83c450..663f792cd 100644 --- a/core/MySigningAtsha204.cpp +++ b/core/MySigningAtsha204.cpp @@ -256,8 +256,8 @@ bool signerAtsha204VerifyMsg(MyMessage &msg) _signing_hmac[0] = SIGNING_IDENTIFIER; // Compare the calculated signature with the provided signature - if (signerMemcmp(&msg.data[msg.getLength()], _signing_hmac, - min(MAX_PAYLOAD_SIZE - msg.getLength(), 32))) { + if (timingneutralMemcmp(&msg.data[msg.getLength()], _signing_hmac, + min(MAX_PAYLOAD_SIZE - msg.getLength(), 32))) { return false; } else { return true; diff --git a/core/MySigningAtsha204Soft.cpp b/core/MySigningAtsha204Soft.cpp index b5f880969..7b77395d8 100644 --- a/core/MySigningAtsha204Soft.cpp +++ b/core/MySigningAtsha204Soft.cpp @@ -261,8 +261,8 @@ bool signerAtsha204SoftVerifyMsg(MyMessage &msg) _signing_hmac[0] = SIGNING_IDENTIFIER; // Compare the calculated signature with the provided signature - if (signerMemcmp(&msg.data[msg.getLength()], _signing_hmac, - MIN((uint8_t)(MAX_PAYLOAD_SIZE - msg.getLength()), (uint8_t)32))) { + if (timingneutralMemcmp(&msg.data[msg.getLength()], _signing_hmac, + MIN((uint8_t)(MAX_PAYLOAD_SIZE - msg.getLength()), (uint8_t)32))) { return false; } else { return true; diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 4c91da55f..516402300 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -27,6 +27,10 @@ extern char _convBuf[MAX_PAYLOAD_SIZE * 2 + 1]; #define TRANSPORT_DEBUG(x,...) //!< debug NULL #endif +#if defined(MY_TRANSPORT_ENCRYPTION) +// for nodes, session key doesn't need to be stored +static sessionInformation_t _sessionInformation; +#endif // SM: transitions and update states static transportState_t stInit = { stInitTransition, stInitUpdate }; @@ -144,6 +148,10 @@ void stParentTransition(void) { TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent setIndication(INDICATION_FIND_PARENT); +#if defined(MY_TRANSPORT_ENCRYPTION) + // reset session key + _sessionInformation.nextRenewal_ms = 0; +#endif _transportSM.uplinkOk = false; _transportSM.preferredParentFound = false; #if defined(MY_PARENT_NODE_IS_STATIC) || defined(MY_PASSIVE_NODE) @@ -305,6 +313,14 @@ void stReadyTransition(void) void stReadyUpdate(void) { #if defined(MY_GATEWAY_FEATURE) +#if defined(MY_TRANSPORT_ENCRYPTION) + if ((int32_t)(hwMillis() - _sessionInformation.nextRenewal_ms) > 0) { + transportRenewSessionKey(); + (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, + I_SESSION_KEY_RESPONSE).set(_sessionInformation.key, sizeof(_sessionInformation.key))); + _sessionInformation.nextRenewal_ms = hwMillis() + SESSION_KEY_INTERVAL_MS; + } +#endif if (!_lastNetworkDiscovery || (hwMillis() - _lastNetworkDiscovery > MY_TRANSPORT_DISCOVERY_INTERVAL_MS)) { _lastNetworkDiscovery = hwMillis(); @@ -597,6 +613,27 @@ bool transportSendRoute(MyMessage &message) return result; } +void transportRenewSessionKey(void) +{ +#if defined(MY_GATEWAY_FEATURE) && defined(MY_TRANSPORT_ENCRYPTION) + TRANSPORT_DEBUG(PSTR("TSF:RSK:GEN\n")); +#if defined(MY_HW_HAS_GETENTROPY) + while (hwGetentropy(&_sessionInformation.key, + sizeof(_sessionInformation.key)) != sizeof(_sessionInformation.key)); +#else + uint8_t buffer[32]; + for (uint8_t i = 0; i < sizeof(buffer); i++) { + buffer[i] = random(256) ^ (hwMillis() & 0xFF); + } + uint8_t output[32]; + SHA256(output, buffer, sizeof(buffer)); + (void)memcpy((void *)_sessionInformation.key, (const void *)output, + sizeof(_sessionInformation.key)); +#endif + (void)transportHALSetSessionKey(_sessionInformation.key, sizeof(_sessionInformation.key)); +#endif +} + // only be used inside transport bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType) { @@ -653,10 +690,8 @@ void transportProcessMessage(void) if (!transportHALReceive(&_msg, &payloadLength)) { return; } - // get message length and limit size - const uint8_t msgLength = _msg.getLength(); - // calculate expected length + const uint8_t msgLength = _msg.getLength(); const uint8_t command = _msg.getCommand(); const uint8_t type = _msg.getType(); const uint8_t sender = _msg.getSender(); @@ -764,7 +799,7 @@ void transportProcessMessage(void) _msg.getByte()); // node pinged #if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000) // delay for fast GW and slow nodes - delay(5); + delay(50); #endif (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG).set((uint8_t)1)); @@ -951,6 +986,8 @@ void transportProcessFIFO(void) } #endif + transportHALHandler(); + uint8_t _processedMessages = MAX_SUBSEQ_MSGS; // process all msgs in FIFO or counter exit while (transportHALDataAvailable() && _processedMessages--) { @@ -978,12 +1015,15 @@ bool transportSendWrite(const uint8_t to, MyMessage &message) // msg length changes if signed const uint8_t totalMsgLength = HEADER_SIZE + ( message.getSigned() ? MAX_PAYLOAD_SIZE : message.getLength() ); - const bool noACK = _transportConfig.passiveMode || (to == BROADCAST_ADDRESS); + bool noACK = _transportConfig.passiveMode; + noACK |= to == BROADCAST_ADDRESS; + noACK |= (message.getCommand() == C_INTERNAL && (message.type == I_PING || message.type == I_PONG || + message.type == I_FIND_PARENT_RESPONSE + )); // send setIndication(INDICATION_TX); const bool result = transportHALSend(to, &message, totalMsgLength, noACK); - TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%" PRIu8 "-%" PRIu8 "-%" PRIu8 "-%" PRIu8 ",s=%" PRIu8 ",c=%" PRIu8 ",t=%" PRIu8 ",pt=%" PRIu8 ",l=%" PRIu8 ",sg=%" PRIu8 ",ft=%" PRIu8 ",st=%s:%s\n"), (noACK ? "?" : result ? "" : "!"), message.getSender(), message.getLast(), diff --git a/core/MyTransport.h b/core/MyTransport.h index ef1451cad..208368d87 100644 --- a/core/MyTransport.h +++ b/core/MyTransport.h @@ -238,6 +238,16 @@ #define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequently processed messages in FIFO (to prevent transport deadlock if HW issue) #define UPLINK_QUALITY_WEIGHT (0.05f) //!< UPLINK_QUALITY_WEIGHT +#define SESSION_KEY_SIZE (16u) //!< SESSION_KEY_SIZE +#if !defined(SESSION_KEY_INTERVAL_MS) +#define SESSION_KEY_INTERVAL_MS (60*1000ul) //!< SESSION_KEY_INTERVAL_MS +#endif +#if !defined(SESSION_KEY_REQUEST_DELAY_MS) +#define SESSION_KEY_REQUEST_DELAY_MS (10*1000ul) //!< SESSION_KEY_REQUEST_DELAY_MS +#endif +#if !defined(SESSION_KEY_REQUEST_TIMEOUT_MS) +#define SESSION_KEY_REQUEST_TIMEOUT_MS (5*1000ul) //!< SESSION_KEY_REQUEST_TIMEOUT_MS +#endif // parent node check #if defined(MY_PARENT_NODE_IS_STATIC) && !defined(MY_PARENT_NODE_ID) @@ -275,6 +285,15 @@ typedef struct { void(*Transition)(void); //!< state transition function void(*Update)(void); //!< state update function } transportState_t; + +/** + * @brief This structure stores session states + */ +typedef struct { + uint8_t key[SESSION_KEY_SIZE]; //!< session key + uint32_t nextRenewal_ms; //!< timestamp key renewal +} sessionInformation_t; + /** * @brief Datatype for internal RSSI storage */ @@ -548,6 +567,11 @@ void transportDisable(void); */ void transportReInitialise(void); +/** +* @brief transportRenewSessionKey +*/ +void transportRenewSessionKey(void); + /** * @brief Get transport signal report * @param command: diff --git a/hal/transport/MyTransportEncryption.cpp b/hal/transport/MyTransportEncryption.cpp new file mode 100644 index 000000000..58097910d --- /dev/null +++ b/hal/transport/MyTransportEncryption.cpp @@ -0,0 +1,283 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */ + +#include "MyTransportEncryption.h" + +static uint8_t _transportHMAC256_KEY[32]; + +// debug +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) +#define TRANSPORT_ENCRYPTION_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug +#else +#define TRANSPORT_ENCRYPTION_DEBUG(x,...) //!< debug NULL +#endif + +static void transportEncryptionKeyDerivation(const uint8_t *salt, const uint8_t salt_len, + const uint8_t *ikm, + const uint8_t ikm_len, const uint8_t *info, uint8_t info_len, uint8_t *okm) +{ + uint8_t _temp_buffer[64]; + (void)memcpy((void *)_temp_buffer, (const void *)salt, salt_len); + (void)memcpy((void *)&_temp_buffer[salt_len], (const void *)info, info_len); + SHA256HMAC(okm, ikm, ikm_len, _temp_buffer, salt_len + info_len); +} + +static void transportEncryptionInit(uint8_t *PSK, const uint8_t len) +{ +#if defined(MY_ENCRYPTION_SIMPLE_PASSWD) + (void)memset((void *)PSK, 0, len); + (void)memcpy((void *)PSK, MY_ENCRYPTION_SIMPLE_PASSWD, + strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, len)); +#else + hwReadConfigBlock((void *)PSK, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, + len); +#endif + +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)PSK, len); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:INIT:PSK=%s\n"), hwDebugPrintStr); +#endif +} + + +static uint8_t transportEncryptionInsecureAESEncryption(void *data, uint8_t len) +{ + // We us IV vector filled with zeros and randomize unused bytes in encryption block + uint8_t IV[16] = { 0 }; + const uint8_t finalLength = len > 16 ? 32 : 16; + // fill block with random data + for (uint8_t i = len; i < finalLength; i++) { + *((uint8_t *)data + i) = random(256); + } +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)data, finalLength); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:IEX:PLAIN=%s\n"), + hwDebugPrintStr); +#endif + //encrypt data + AES128CBCEncrypt(IV, (uint8_t *)data, finalLength); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)data, finalLength); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:IEX:CIP=%s\n"), + hwDebugPrintStr); // IEX = insecure AES encryption +#endif + return finalLength; +} + +static void transportEncryptionInsecureAESDecryption(void *data, const uint8_t len) +{ + // has to be adjusted, WIP! + uint8_t IV[16] = { 0 }; + // decrypt data + AES128CBCDecrypt(IV, (uint8_t *)data, len); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)data, len); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:IDX:PLAIN=%s\n"), hwDebugPrintStr); +#endif +} + +static uint8_t transportEncryptionSecureAESEncryption(uint8_t *outputBuffer, + const uint8_t *inputBuffer, + const uint8_t inputLen, const uint8_t *auxBuffer, const uint8_t auxBufferLen, const uint8_t IV_SIZE, + const uint8_t HMAC_SIZE) +{ + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:AEX\n")); + uint8_t _IV[16]; + uint8_t _processedPayload[32]; + uint8_t _HMAC[32]; +#if defined(MY_HW_HAS_GETENTROPY) + while (hwGetentropy(&_IV, sizeof(_IV)) != sizeof(_IV)); +#else + // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter + // and then the byte is hashed (SHA256) to produce the resulting nonce + for (uint8_t i = 0; i < sizeof(_HMAC); i++) { + _HMAC[i] = random(256) ^ (hwMillis() & 0xFF); + } + SHA256(outputBuffer, _HMAC, sizeof(_HMAC)); + (void)memcpy((void *)_IV, (const void *)outputBuffer, IV_SIZE); +#endif + // padding IV + (void)memset((void *)&_IV[IV_SIZE], 0xAA, sizeof(_IV) - IV_SIZE); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_IV, sizeof(_IV)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:IV=%s\n"), hwDebugPrintStr); +#endif + const uint8_t finalLength = inputLen > 16 ? 32 : 16; + (void)memcpy((void *)_processedPayload, (const void *)inputBuffer, inputLen); + // add padding bytes, PKCS#7 + for (uint8_t i = inputLen; i < finalLength; i++) { + _processedPayload[i] = finalLength - inputLen; + } +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_processedPayload, finalLength); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:PAD=%s\n"), hwDebugPrintStr); +#endif + (void)memcpy((void *)outputBuffer, (const void *)_IV, IV_SIZE); + AES128CBCEncrypt(_IV, _processedPayload, finalLength); + (void)memset((void *)_IV, 0, sizeof(_IV)); + // create IV + ciphertext + aux for HMAC256 + (void)memcpy((void *)&outputBuffer[IV_SIZE], (const void *)_processedPayload, finalLength); + // add auxillary buffer (i.e. header) for HMAC + (void)memcpy((void *)&outputBuffer[IV_SIZE + finalLength], (const void *)auxBuffer, auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str(auxBuffer, auxBufferLen); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:AUX=%s\n"), hwDebugPrintStr); +#endif + // calculate HMAC256 + SHA256HMAC(_HMAC, _transportHMAC256_KEY, sizeof(_transportHMAC256_KEY), outputBuffer, + IV_SIZE + finalLength + auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + // padding HMAC, not needed + (void)memset((void *)&_HMAC[HMAC_SIZE], 0xAA, sizeof(_HMAC) - HMAC_SIZE); + hwDebugBuf2Str((const uint8_t *)&_HMAC, sizeof(_HMAC)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:HMAC=%s\n"), hwDebugPrintStr); +#endif + // create IV + HMAC + (void)memcpy((void *)&outputBuffer[IV_SIZE], (const void *)_HMAC, HMAC_SIZE); + // create IV + HMAC + ciphertext + (void)memcpy((void *)&outputBuffer[IV_SIZE + HMAC_SIZE], (const void *)_processedPayload, + finalLength); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_processedPayload, finalLength); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:ENC:CIP=%s\n"), hwDebugPrintStr); +#endif + return IV_SIZE + HMAC_SIZE + finalLength; +} + +static bool transportEncryptionSecureAESDecryption(uint8_t *buffer, uint8_t bufferLen, + const uint8_t *auxBuffer, + const uint8_t auxBufferLen, const uint8_t IV_SIZE, const uint8_t HMAC_SIZE) +{ + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:EAX\n")); + bool result = false; + uint8_t _IV[16]; + uint8_t _ciphertext[32]; + uint8_t _HMACin[32]; + const uint8_t len = bufferLen - (IV_SIZE + HMAC_SIZE); + (void)memcpy((void *)_IV, (const void *)buffer, IV_SIZE); + // padding IV + (void)memset(&_IV[IV_SIZE], 0xAA, sizeof(_IV) - IV_SIZE); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_IV, sizeof(_IV)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:IV=%s\n"), hwDebugPrintStr); +#endif + (void)memcpy((void *)_HMACin, (const void *)&buffer[IV_SIZE], + HMAC_SIZE); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + // padding HMAC + (void)memset((void *)&_HMACin[HMAC_SIZE], 0xAA, sizeof(_HMACin) - HMAC_SIZE); + hwDebugBuf2Str((const uint8_t *)&_HMACin, sizeof(_HMACin)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:HMACin=%s\n"), hwDebugPrintStr); +#endif + (void)memcpy((void *)_ciphertext, + (const void *)&buffer[IV_SIZE + HMAC_SIZE], + len); + // prepare HMAC verification: concatenate IV + ciphertext + auxBuffer + (void)memcpy((void *)&buffer[IV_SIZE], (const void *)_ciphertext, len); + // add auxillary buffer (i.e. header) for HMAC + (void)memcpy((void *)&buffer[IV_SIZE + len], (const void *)auxBuffer, auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str(auxBuffer, auxBufferLen); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:AUX=%s\n"), hwDebugPrintStr); +#endif + uint8_t _HMACout[32]; + SHA256HMAC(_HMACout, _transportHMAC256_KEY, sizeof(_transportHMAC256_KEY), + buffer, + IV_SIZE + len + auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + // padding HMACout + (void)memset((void *)&_HMACout[HMAC_SIZE], 0xAA, sizeof(_HMACin) - HMAC_SIZE); + hwDebugBuf2Str((const uint8_t *)&_HMACout, sizeof(_HMACout)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:HMACout=%s\n"), hwDebugPrintStr); +#endif + if (timingneutralMemcmp(_HMACin, _HMACout, (size_t)HMAC_SIZE)) { + // HMAC failed + TRANSPORT_ENCRYPTION_DEBUG(PSTR("!TEX:DEC:HMAC\n")); + } else { + result = true; + } +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_ciphertext, len); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:CIP=%s\n"), hwDebugPrintStr); +#endif + AES128CBCDecrypt(_IV, (uint8_t *)_ciphertext, len); + // copy plaintext back + (void)memcpy((void *)buffer, _ciphertext, len); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_ciphertext, len); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:DEC:PLAIN=%s\n"), hwDebugPrintStr); +#endif + return result; +} + + + +static uint8_t transportEncryptionSignData(uint8_t *outputBuffer, + const uint8_t *inputBuffer, + const uint8_t inputLen, const uint8_t *auxBuffer, const uint8_t auxBufferLen, + const uint8_t NONCE_SIZE, + const uint8_t HMAC_SIZE) +{ + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:SDA:SIGN\n")); + uint8_t _NONCE[32]; + uint8_t _HMAC[32]; +#if defined(MY_HW_HAS_GETENTROPY) + while (hwGetentropy(&_NONCE, sizeof(_NONCE)) != sizeof(_NONCE)); +#else + // We used a basic whitening technique that XORs a random byte with the current hwMillis() counter + // and then the byte is hashed (SHA256) to produce the resulting nonce + for (uint8_t i = 0; i < sizeof(_HMAC); i++) { + _HMAC[i] = random(256) ^ (hwMillis() & 0xFF); + } + SHA256(outputBuffer, _HMAC, sizeof(_HMAC)); + (void)memcpy((void *)_NONCE, (const void *)outputBuffer, NONCE_SIZE); +#endif + // padding IV + (void)memset((void *)&_NONCE[NONCE_SIZE], 0xAA, sizeof(_NONCE) - NONCE_SIZE); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str((const uint8_t *)&_NONCE, sizeof(_NONCE)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:SDA:NONCE=%s\n"), hwDebugPrintStr); +#endif + (void)memcpy((void *)outputBuffer, (const void *)_NONCE, NONCE_SIZE); + (void)memset((void *)_NONCE, 0, sizeof(_NONCE)); + // create NONCE + cleartext + auxBuffer for HMAC256 + (void)memcpy((void *)&outputBuffer[NONCE_SIZE], (const void *)inputBuffer, inputLen); + // add auxillary buffer (i.e. header) for HMAC + (void)memcpy((void *)&outputBuffer[NONCE_SIZE + inputLen], (const void *)auxBuffer, auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + hwDebugBuf2Str(auxBuffer, auxBufferLen); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:SDA:AUX=%s\n"), hwDebugPrintStr); +#endif + // calculate HMAC256 + SHA256HMAC(_HMAC, _transportHMAC256_KEY, sizeof(_transportHMAC256_KEY), outputBuffer, + NONCE_SIZE + inputLen + auxBufferLen); +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION) + // padding HMAC, not needed + (void)memset((void *)&_HMAC[HMAC_SIZE], 0xAA, sizeof(_HMAC) - HMAC_SIZE); + hwDebugBuf2Str((const uint8_t *)&_HMAC, sizeof(_HMAC)); + TRANSPORT_ENCRYPTION_DEBUG(PSTR("TEX:SDA:HMAC=%s\n"), hwDebugPrintStr); +#endif + // create IV + HMAC + (void)memcpy((void *)&outputBuffer[NONCE_SIZE], (const void *)_HMAC, HMAC_SIZE); + // create IV + HMAC + cleartext + (void)memcpy((void *)&outputBuffer[NONCE_SIZE + HMAC_SIZE], (const void *)inputBuffer, + inputLen); + return NONCE_SIZE + HMAC_SIZE + inputLen; +} diff --git a/hal/transport/MyTransportEncryption.h b/hal/transport/MyTransportEncryption.h new file mode 100644 index 000000000..763980a95 --- /dev/null +++ b/hal/transport/MyTransportEncryption.h @@ -0,0 +1,105 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + */ + +#ifndef MyTransportEncryption_h +#define MyTransportEncryption_h + +/** +* @brief transportEncryptionKeyDerivation +* @param salt +* @param salt_len +* @param ikm +* @param ikm_len +* @param info +* @param info_len +* @param okm +* @return +*/ +static void transportEncryptionKeyDerivation(const uint8_t *salt, const uint8_t salt_len, + const uint8_t *ikm, + const uint8_t ikm_len, const uint8_t *info, uint8_t info_len, uint8_t *okm) __attribute__((unused)); + +/** +* @brief transportEncryptionInsecureAESEncryption +* @param data +* @param len +* @return final length +*/ + +static uint8_t transportEncryptionInsecureAESEncryption(void *data, + uint8_t len) __attribute__((unused)); + +/** +* @brief transportEncryptionInsecureAESDecryption +* @param data +* @param len +*/ +static void transportEncryptionInsecureAESDecryption(void *data, + const uint8_t len) __attribute__((unused)); + +/** +* @brief transportEncryptionSecureAESEncryption +* @param +* @param +* @param +* @param +* @param +* @param +* @param +* @return +*/ +static uint8_t transportEncryptionSecureAESEncryption(uint8_t *outputBuffer, + const uint8_t *inputBuffer, + const uint8_t inputLen, const uint8_t *auxBuffer, const uint8_t auxBufferLen, const uint8_t IV_SIZE, + const uint8_t HMAC_SIZE) __attribute__((unused)); + +/** +* @brief transportEncryptionSecureAESDecryption +* @param +* @param +* @param +* @param +* @param +* @param +* @param +* @return +*/ +static bool transportEncryptionSecureAESDecryption(uint8_t *buffer, uint8_t bufferLen, + const uint8_t *auxBuffer, + const uint8_t auxBufferLen, const uint8_t IV_SIZE, const uint8_t HMAC_SIZE) __attribute__((unused)); + +/** +* @brief transportEncryptionSignData +* @param +* @param +* @param +* @param +* @param +* @param +* @param +* @return +*/ +static uint8_t transportEncryptionSignData(uint8_t *outputBuffer, + const uint8_t *inputBuffer, + const uint8_t inputLen, const uint8_t *auxBuffer, const uint8_t auxBufferLen, + const uint8_t NONCE_SIZE, + const uint8_t HMAC_SIZE) __attribute__((unused)); + +#endif diff --git a/hal/transport/MyTransportHAL.cpp b/hal/transport/MyTransportHAL.cpp index 7fdd6cd9d..85367eeb9 100644 --- a/hal/transport/MyTransportHAL.cpp +++ b/hal/transport/MyTransportHAL.cpp @@ -20,39 +20,139 @@ #include "MyTransportHAL.h" +// debug #if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) #define TRANSPORT_HAL_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug #else #define TRANSPORT_HAL_DEBUG(x,...) //!< debug NULL #endif -bool transportHALInit(void) -{ - TRANSPORT_HAL_DEBUG(PSTR("THA:INIT\n")); -#if defined(MY_TRANSPORT_ENCRYPTION) - uint8_t transportPSK[16]; -#if defined(MY_ENCRYPTION_SIMPLE_PASSWD) - (void)memset((void *)transportPSK, 0, sizeof(transportPSK)); - (void)memcpy((void *)transportPSK, MY_ENCRYPTION_SIMPLE_PASSWD, - strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, sizeof(transportPSK))); +#if (MY_TRANSPORT_COUNT > 1) +#if defined(MY_RADIO_RFM69) && !defined(MY_RFM69_NEW_DRIVER) +#error Multitransport requires the new RFM69 driver! +#endif + +#if (MY_TRANSPORT_COUNT <= 3) +// 2 - 3 transports, 2 bits: 0=BC, 1=TSP1, 2=TSP2, 3=TSP3 +// 256 / 4 = 64 bytes RAM +#define SIZE_BITS_PER_CHANNEL 2 +#elif (MY_TRANSPORT_COUNT > 3) && (MY_TRANSPORT_COUNT <= 15) +// 4 - 7 transports, 4 bits: 0000 - 1111 +// 256 / 2 = 128 bytes RAM +#define SIZE_BITS_PER_CHANNEL 4 #else - hwReadConfigBlock((void *)transportPSK, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, - sizeof(transportPSK)); +#error >15 transports not supported :) #endif + +#define SIZE_CHANNELS_PER_BYTE ( 8 / SIZE_BITS_PER_CHANNEL) + +static uint8_t channelRoute[SIZE_CHANNEL_ROUTE / SIZE_CHANNELS_PER_BYTE]; + +transportChannelID_t transportGetChannel(const uint8_t nodeId) +{ + if (nodeId == BROADCAST_ADDRESS) { + return TRANSPORT_ALL_CHANNEL_ID; + } + const uint8_t arrayPos = nodeId / SIZE_CHANNELS_PER_BYTE; + const uint8_t bitPos = (nodeId % SIZE_CHANNELS_PER_BYTE) * SIZE_BITS_PER_CHANNEL; + const uint8_t channel = (channelRoute[arrayPos] >> bitPos) & ((1 << SIZE_BITS_PER_CHANNEL) - 1); + TRANSPORT_HAL_DEBUG(PSTR("THA:GCH:GET N=%" PRIu8 ",CH=%" PRIu8 "\n"), nodeId, channel); + return static_cast(channel); +} + +void transportUpdateChannel(const uint8_t nodeId, const transportChannelID_t channel) +{ + if (nodeId != BROADCAST_ADDRESS) { + const uint8_t arrayPos = nodeId / SIZE_CHANNELS_PER_BYTE; + const uint8_t bitPos = (nodeId % SIZE_CHANNELS_PER_BYTE) * SIZE_BITS_PER_CHANNEL; + uint8_t val = channelRoute[arrayPos]; + // clear pos + val &= ~(((1 << SIZE_BITS_PER_CHANNEL) - 1) << bitPos); + // set pos + val |= (static_cast(channel) & ((1 << SIZE_BITS_PER_CHANNEL) - 1)) << bitPos; + channelRoute[arrayPos] = val; + TRANSPORT_HAL_DEBUG(PSTR("THA:UCH:SET N=%" PRIu8 ",CH=%" PRIu8 "\n"), nodeId, channel); + } +} + +// reset channel routes +void transportResetChannels(void) +{ + for (uint16_t i = 0; i < sizeof(channelRoute); i++) { + channelRoute[i] = 0; + } +} + +void transportDebugChannels(void) +{ #if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) - hwDebugBuf2Str((const uint8_t *)transportPSK, sizeof(transportPSK)); - TRANSPORT_HAL_DEBUG(PSTR("THA:INIT:PSK=%s\n"),hwDebugPrintStr); + for (uint16_t i = 0; i < sizeof(channelRoute); i++) { + if (transportGetChannel(i) != TRANSPORT_ALL_CHANNEL_ID) { + TRANSPORT_HAL_DEBUG(PSTR("THA:DCH:%" PRIu8 ": %" PRIu8 "\n"), i, transportGetChannel(i)); + } + } +#endif +} + +#endif + +#if defined(MY_TRANSPORT_RX_QUEUE) +static RXQueuedMessage_t transportRxQueueStorage[RX_QUEUE_BUFFER_SIZE]; +static CircularBuffer transportRxQueue(transportRxQueueStorage, + RX_QUEUE_BUFFER_SIZE); + + +RXQueuedMessage_t *transportHALGetQueueBuffer(void) +{ + return transportRxQueue.getFront(); +} + +bool transportHALPushQueueBuffer(RXQueuedMessage_t *buffer) +{ + return transportRxQueue.pushFront(buffer); +} + #endif + +bool transportHALInit(void) +{ + TRANSPORT_HAL_DEBUG(PSTR("THA:INIT:TSP CNT=%" PRIu8 "\n"), MY_TRANSPORT_COUNT); + +#if (MY_TRANSPORT_COUNT > 1) + // initialize channel routing table + transportResetChannels(); #endif - bool result = transportInit(); #if defined(MY_TRANSPORT_ENCRYPTION) + uint8_t transportPSK[16]; + transportEncryptionInit(transportPSK, sizeof(transportPSK)); + #if defined(MY_RADIO_RFM69) - transportEncrypt((const char *)transportPSK); + RFM69_transportEncrypt(transportPSK); #else //set up AES-key AES128CBCInit(transportPSK); #endif +#endif + + bool result = true; +#if defined(MY_RADIO_RF24) + result &= RF24_transportInit(); +#endif +#if defined(MY_RADIO_RFM69) + result &= RFM69_transportInit(); +#endif +#if defined(MY_RADIO_RFM95) + result &= RFM95_transportInit(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + result &= NRF5_ESB_transportInit(); +#endif +#if defined(MY_RS485) + result &= RS485_transportInit(); +#endif + +#if defined(MY_TRANSPORT_ENCRYPTION) // Make sure it is purged from memory when set (void)memset((void *)transportPSK, 0, sizeof(transportPSK)); @@ -60,23 +160,90 @@ bool transportHALInit(void) return result; } +void transportHALHandler(void) +{ +#if defined(MY_RADIO_RF24) + RF24_transportTask(); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportTask(); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportTask(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportTask(); +#endif +#if defined(MY_RS485) + // not implemented yet +#endif +} + void transportHALSetAddress(const uint8_t address) { TRANSPORT_HAL_DEBUG(PSTR("THA:SAD:ADDR=%" PRIu8 "\n"), address); - transportSetAddress(address); +#if defined(MY_RADIO_RF24) + RF24_transportSetAddress(address); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportSetAddress(address); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportSetAddress(address); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportSetAddress(address); +#endif +#if defined(MY_RS485) + RS485_transportSetAddress(address); +#endif + // cppcheck + (void)address; } uint8_t transportHALGetAddress(void) { - uint8_t result = transportGetAddress(); + uint8_t result; + // only first match returned +#if defined(MY_RADIO_RF24) + result = RF24_transportGetAddress(); +#elif defined(MY_RADIO_RFM69) + result = RFM69_transportGetAddress(); +#elif defined(MY_RADIO_RFM95) + result = RFM95_transportGetAddress(); +#elif defined(MY_RADIO_NRF5_ESB) + result = NRF5_ESB_transportGetAddress(); +#elif defined(MY_RS485) + result = RS485_transportGetAddress(); +#else + result = AUTO; +#endif + TRANSPORT_HAL_DEBUG(PSTR("THA:GAD:ADDR=%" PRIu8 "\n"), result); return result; } bool transportHALDataAvailable(void) { - bool result = transportDataAvailable(); + bool result = true; +#if defined(MY_TRANSPORT_RX_QUEUE) + result &= !transportRxQueue.empty(); +#else +#if defined(MY_RADIO_RF24) + result = RF24_transportDataAvailable(); +#elif defined(MY_RADIO_RFM69) + result = RFM69_transportDataAvailable(); +#elif defined(MY_RADIO_RFM95) + result = RFM95_transportDataAvailable(); +#elif defined(MY_RADIO_NRF5_ESB) + result = NRF5_ESB_transportDataAvailable(); +#elif defined(MY_RS485) + result = RS485_transportDataAvailable(); +#endif +#endif + #if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + // cppcheck-suppress knownConditionTrueFalse if (result) { TRANSPORT_HAL_DEBUG(PSTR("THA:DATA:AVAIL\n")); } @@ -86,61 +253,94 @@ bool transportHALDataAvailable(void) bool transportHALSanityCheck(void) { - bool result = transportSanityCheck(); + bool result = true; +#if defined(MY_RADIO_RF24) + result &= RF24_transportSanityCheck(); +#endif +#if defined(MY_RADIO_RFM69) + result &= RFM69_transportSanityCheck(); +#endif +#if defined(MY_RADIO_RFM95) + result &= RFM95_transportSanityCheck(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + result &= NRF5_ESB_transportSanityCheck(); +#endif +#if defined(MY_RS485) + result &= RS485_transportSanityCheck(); +#endif TRANSPORT_HAL_DEBUG(PSTR("THA:SAN:RES=%" PRIu8 "\n"), result); return result; } bool transportHALReceive(MyMessage *inMsg, uint8_t *msgLength) { - // set pointer to first byte of data structure - uint8_t *rx_data = &inMsg->last; - uint8_t payloadLength = transportReceive((void *)rx_data); -#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) - hwDebugBuf2Str((const uint8_t *)rx_data, payloadLength); - TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:MSG=%s\n"), hwDebugPrintStr); + uint8_t payloadLength = 0; +#if defined(MY_TRANSPORT_RX_QUEUE) + TRANSPORT_HAL_DEBUG(PSTR("THA:MSG:RXQ,LEN=%" PRIu8 "\n"), transportRxQueue.available()); + RXQueuedMessage_t *newMessage = transportRxQueue.getBack(); + if (newMessage != NULL) { + payloadLength = newMessage->length; // check if buffer loading is OF protected! + (void)memcpy((void *)&inMsg->last, (void *)&newMessage->data, payloadLength); + (void)transportRxQueue.popBack(); + } +#else +#if defined(MY_RADIO_RF24) + payloadLength = RF24_transportReceive((void *)&inMsg->last, MAX_MESSAGE_SIZE); +#elif defined(MY_RADIO_RFM69) + payloadLength = RFM69_transportReceive((void *)&inMsg->last, MAX_MESSAGE_SIZE); +#elif defined(MY_RADIO_RFM95) + payloadLength = RFM95_transportReceive((void *)&inMsg->last, MAX_MESSAGE_SIZE); +#elif defined(MY_RADIO_NRF5_ESB) + payloadLength = NRF5_ESB_transportReceive((void *)&inMsg->last, MAX_MESSAGE_SIZE); +#elif defined(MY_RS485) + payloadLength = RS485_transportReceive((void *)&inMsg->last, MAX_MESSAGE_SIZE); #endif -#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) - TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:DECRYPT\n")); - // has to be adjusted, WIP! - uint8_t IV[16] = { 0 }; - // decrypt data - AES128CBCDecrypt(IV, (uint8_t *)rx_data, payloadLength); -#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) - hwDebugBuf2Str((const uint8_t *)rx_data, payloadLength); - TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:PLAIN=%s\n"), hwDebugPrintStr); #endif + +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) + hwDebugBuf2Str((const uint8_t *)&inMsg->last, payloadLength); + TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:MSG=%s\n"), hwDebugPrintStr); #endif + if (payloadLength < HEADER_SIZE) { + TRANSPORT_HAL_DEBUG(PSTR("!THA:RCV:HEADER\n")); + return false; + } // Reject messages with incorrect protocol version - MyMessage tmp = *inMsg; - if (!tmp.isProtocolVersionValid()) { + if (!inMsg->isProtocolVersionValid()) { setIndication(INDICATION_ERR_VERSION); TRANSPORT_HAL_DEBUG(PSTR("!THA:RCV:PVER=%" PRIu8 "\n"), - tmp.getVersion()); // protocol version mismatch + inMsg->getVersion()); // protocol version mismatch return false; } - *msgLength = tmp.getLength(); -#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) + *msgLength = inMsg->getLength(); + +#if (defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69)) || defined(MY_RFM69_ENABLE_SW_ENCRYPTION) // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check #else // Reject payloads with incorrect length - const uint8_t expectedMessageLength = tmp.getExpectedMessageSize(); + const uint8_t expectedMessageLength = inMsg->getExpectedMessageSize(); if (payloadLength != expectedMessageLength) { setIndication(INDICATION_ERR_LENGTH); - TRANSPORT_HAL_DEBUG(PSTR("!THA:RCV:LEN=%" PRIu8 ",EXP=%" PRIu8 "\n"), payloadLength, + TRANSPORT_HAL_DEBUG(PSTR("!THA:MSG:LEN=%" PRIu8 ",EXP=%" PRIu8 "\n"), payloadLength, expectedMessageLength); // invalid payload length return false; } + TRANSPORT_HAL_DEBUG(PSTR("THA:MSG:RCV,LEN=%" PRIu8 "\n"), payloadLength); #endif - TRANSPORT_HAL_DEBUG(PSTR("THA:RCV:MSG LEN=%" PRIu8 "\n"), payloadLength); + +#if (MY_TRANSPORT_COUNT > 1) + transportUpdateChannel(inMsg->last, newMessage->channel); +#endif + return true; } -bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, const uint8_t len, + +bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, uint8_t len, const bool noACK) { if (outMsg == NULL) { - // nothing to send return false; } @@ -149,93 +349,301 @@ bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, cons TRANSPORT_HAL_DEBUG(PSTR("THA:SND:MSG=%s\n"), hwDebugPrintStr); #endif -#if defined(MY_TRANSPORT_ENCRYPTION) && !defined(MY_RADIO_RFM69) - TRANSPORT_HAL_DEBUG(PSTR("THA:SND:ENCRYPT\n")); - uint8_t *tx_data[MAX_MESSAGE_SIZE]; - // copy input data because it is read-only - (void)memcpy((void *)tx_data, (void *)&outMsg->last, len); - // We us IV vector filled with zeros but randomize unused bytes in encryption block - uint8_t IV[16] = { 0 }; - const uint8_t finalLength = len > 16 ? 32 : 16; - // fill block with random data - for (uint8_t i = len; i < finalLength; i++) { - *((uint8_t *)tx_data + i) = random(256); - } - //encrypt data - AES128CBCEncrypt(IV, (uint8_t *)tx_data, finalLength); -#if defined(MY_DEBUG_VERBOSE_TRANSPORT_HAL) - hwDebugBuf2Str((const uint8_t *)tx_data, finalLength); - TRANSPORT_HAL_DEBUG(PSTR("THA:SND:CIP=%s\n"), hwDebugPrintStr); +#if (MY_TRANSPORT_COUNT > 1) + const transportChannelID_t channel = transportGetChannel(nextRecipient); +#else + // cppcheck-suppress unreadVariable + const transportChannelID_t channel = TRANSPORT_ALL_CHANNEL_ID; #endif -#else - const uint8_t *tx_data = &outMsg->last; - const uint8_t finalLength = len; + bool result = true; + +#if defined(MY_RADIO_RF24) + // cppcheck-suppress knownConditionTrueFalse + if (channel == TRANSPORT_RF24_CHANNEL_ID || channel == TRANSPORT_ALL_CHANNEL_ID) { + result &= RF24_transportSend(nextRecipient, (void *)&outMsg->last, len, + noACK); + } +#endif +#if defined(MY_RADIO_RFM69) + // cppcheck-suppress knownConditionTrueFalse + if (channel == TRANSPORT_RFM69_CHANNEL_ID || channel == TRANSPORT_ALL_CHANNEL_ID) { + result &= RFM69_transportSend(nextRecipient, (void *)&outMsg->last, len, noACK); + } +#endif +#if defined(MY_RADIO_RFM95) + // cppcheck-suppress knownConditionTrueFalse + if (channel == TRANSPORT_RFM95_CHANNEL_ID || channel == TRANSPORT_ALL_CHANNEL_ID) { + result &= RFM95_transportSend(nextRecipient, (void *)&outMsg->last, len, + noACK); + } +#endif +#if defined(MY_RADIO_NRF5_ESB) + // cppcheck-suppress knownConditionTrueFalse + if (channel == TRANSPORT_NRF5_ESB_CHANNEL_ID || channel == TRANSPORT_ALL_CHANNEL_ID) { + result &= NRF5_ESB_transportSend(nextRecipient, (void *)&outMsg->last, len, + noACK); + } +#endif +#if defined(MY_RS485) + // cppcheck-suppress knownConditionTrueFalse + if (channel == TRANSPORT_RS485_CHANNEL_ID || channel == TRANSPORT_ALL_CHANNEL_ID) { + result &= RS485_transportSend(nextRecipient, (void *)&outMsg->last, len, + noACK); + } +#endif +#if (MY_TRANSPORT_COUNT > 1) + // if we receive a hwACK (result==true && !noACK) and message is not BC (checked in transportUpdateChannel() ), update channel table accordingly + if (result && !noACK) { + transportUpdateChannel(nextRecipient, channel); + } #endif - bool result = transportSend(nextRecipient, (void *)tx_data, finalLength, noACK); - TRANSPORT_HAL_DEBUG(PSTR("THA:SND:MSG LEN=%" PRIu8 ",RES=%" PRIu8 "\n"), finalLength, result); - return result; + TRANSPORT_HAL_DEBUG(PSTR("THA:SND:MSG LEN=%" PRIu8 ",RES=%" PRIu8 "\n"), len, result); + return result; // or channel == TRANSPORT_ALL_CHANNEL_ID (broadcast / unknown route) } -void transportHALPowerDown(void) +int16_t transportHALGetSendingRSSI(void) { - transportPowerDown(); +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetSendingRSSI(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetSendingRSSI(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetSendingRSSI(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetSendingRSSI(); +#endif +#if defined(MY_RS485) + return RS485_transportGetSendingRSSI(); +#endif + + return FUNCTION_NOT_SUPPORTED; } -void transportHALPowerUp(void) +int16_t transportHALGetReceivingRSSI(void) { - transportPowerUp(); +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetReceivingRSSI(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetReceivingRSSI(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetReceivingRSSI(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetReceivingRSSI(); +#endif +#if defined(MY_RS485) + return RS485_transportGetReceivingRSSI(); +#endif + + return FUNCTION_NOT_SUPPORTED; } -void transportHALSleep(void) +int16_t transportHALGetSendingSNR(void) { - transportSleep(); +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetSendingSNR(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetSendingSNR(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetSendingSNR(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetSendingSNR(); +#endif +#if defined(MY_RS485) + return RS485_transportGetSendingSNR(); +#endif + return FUNCTION_NOT_SUPPORTED; } -void transportHALStandBy(void) +int16_t transportHALGetReceivingSNR(void) { - transportStandBy(); +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetReceivingSNR(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetReceivingSNR(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetReceivingSNR(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetReceivingSNR(); +#endif +#if defined(MY_RS485) + return RS485_transportGetReceivingSNR(); +#endif + return FUNCTION_NOT_SUPPORTED; } -int16_t transportHALGetSendingRSSI(void) +int16_t transportHALGetTxPowerPercent(void) { - int16_t result = transportGetSendingRSSI(); - return result; +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetTxPowerPercent(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetTxPowerPercent(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetTxPowerPercent(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetTxPowerPercent(); +#endif +#if defined(MY_RS485) + return RS485_transportGetTxPowerPercent(); +#endif + return FUNCTION_NOT_SUPPORTED; } -int16_t transportHALGetReceivingRSSI(void) +bool transportHALSetTxPowerPercent(const uint8_t powerPercent) { - int16_t result = transportGetReceivingRSSI(); - return result; +#if (MY_TRANSPORT_COUNT > 1) + (void)powerPercent; + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportSetTxPowerPercent(powerPercent); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportSetTxPowerPercent(powerPercent); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportSetTxPowerPercent(powerPercent); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportSetTxPowerPercent(powerPercent); +#endif +#if defined(MY_RS485) + return RS485_transportSetTxPowerPercent(powerPercent); +#endif + return FUNCTION_NOT_SUPPORTED; } -int16_t transportHALGetSendingSNR(void) +int16_t transportHALGetTxPowerLevel(void) { - int16_t result = transportGetSendingSNR(); - return result; +#if (MY_TRANSPORT_COUNT > 1) + return FUNCTION_NOT_SUPPORTED; +#endif +#if defined(MY_RADIO_RF24) + return RF24_transportGetTxPowerLevel(); +#endif +#if defined(MY_RADIO_RFM69) + return RFM69_transportGetTxPowerLevel(); +#endif +#if defined(MY_RADIO_RFM95) + return RFM95_transportGetTxPowerLevel(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + return NRF5_ESB_transportGetTxPowerLevel(); +#endif +#if defined(MY_RS485) + return RS485_transportGetTxPowerLevel(); +#endif + return FUNCTION_NOT_SUPPORTED; } -int16_t transportHALGetReceivingSNR(void) + +void transportHALPowerDown(void) { - int16_t result = transportGetReceivingSNR(); - return result; +#if defined(MY_RADIO_RF24) + RF24_transportPowerDown(); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportPowerDown(); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportPowerDown(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportPowerDown(); +#endif +#if defined(MY_RS485) + RS485_transportPowerDown(); +#endif + } -int16_t transportHALGetTxPowerPercent(void) + +void transportHALPowerUp(void) { - int16_t result = transportGetTxPowerPercent(); - return result; +#if defined(MY_RADIO_RF24) + RF24_transportPowerUp(); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportPowerUp(); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportPowerUp(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportPowerUp(); +#endif +#if defined(MY_RS485) + RS485_transportPowerUp(); +#endif + } -bool transportHALSetTxPowerPercent(const uint8_t powerPercent) +void transportHALSleep(void) { - bool result = transportSetTxPowerPercent(powerPercent); - return result; +#if defined(MY_RADIO_RF24) + RF24_transportSleep(); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportSleep(); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportSleep(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportSleep(); +#endif +#if defined(MY_RS485) + RS485_transportSleep(); +#endif } -int16_t transportHALGetTxPowerLevel(void) +void transportHALStandBy(void) { - int16_t result = transportGetTxPowerLevel(); - return result; +#if defined(MY_RADIO_RF24) + RF24_transportStandBy(); +#endif +#if defined(MY_RADIO_RFM69) + RFM69_transportStandBy(); +#endif +#if defined(MY_RADIO_RFM95) + RFM95_transportStandBy(); +#endif +#if defined(MY_RADIO_NRF5_ESB) + NRF5_ESB_transportStandBy(); +#endif +#if defined(MY_RS485) + RS485_transportStandBy(); +#endif + } diff --git a/hal/transport/MyTransportHAL.h b/hal/transport/MyTransportHAL.h index 22cd35022..80e53d4b7 100644 --- a/hal/transport/MyTransportHAL.h +++ b/hal/transport/MyTransportHAL.h @@ -28,6 +28,7 @@ * | | THA | SAN | RES=%%d | Transport sanity check, result (RES) * | | THA | RCV | MSG=%%s | Receive message (MSG) * | | THA | RCV | DECRYPT | Decrypt received message + * |!| THA | RCV | HEADER | Received message does not contain header * | | THA | RCV | PLAIN=%%s | Decrypted message (PLAIN) * |!| THA | RCV | PVER=%%d | Message protocol version (PVER) mismatch * |!| THA | RCV | LEN=%%d,EXP=%%d | Invalid message length (LEN), exptected length (EXP) @@ -37,34 +38,22 @@ * | | THA | SND | CIP=%%s | Ciphertext of encypted message (CIP) * | | THA | SND | MSG LEN=%%d,RES=%%d | Sending message with length (LEN), result (RES) * - * */ #ifndef MyTransportHAL_h #define MyTransportHAL_h +#include "drivers/CircularBuffer/CircularBuffer.h" + +#define RX_QUEUE_MAX_MSG_LENGTH 32 //!< RX_QUEUE_MAX_MSG_LENGTH +#define RX_QUEUE_BUFFER_SIZE 8 //!< RX_QUEUE_BUFFER_SIZE +#define SIZE_CHANNEL_ROUTE 256 //!< SIZE_CHANNEL_ROUTE + #define INVALID_SNR ((int16_t)-256) //!< INVALID_SNR #define INVALID_RSSI ((int16_t)-256) //!< INVALID_RSSI #define INVALID_PERCENT ((int16_t)-100) //!< INVALID_PERCENT #define INVALID_LEVEL ((int16_t)-256) //!< INVALID_LEVEL -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -#if defined(MY_RADIO_NRF5_ESB) -#error Receive message buffering not supported for NRF5 radio! Please define MY_NRF5_RX_BUFFER_SIZE -#endif -#if defined(MY_RADIO_RFM69) -#error Receive message buffering not supported for RFM69! -#endif -#if defined(MY_RADIO_RFM95) -#error Receive message buffering not supported for RFM95! -#endif -#if defined(MY_RS485) -#error Receive message buffering not supported for RS485! -#endif -#elif defined(MY_RX_MESSAGE_BUFFER_SIZE) -#error Receive message buffering requires message buffering feature enabled! -#endif - /** * @brief Signal report selector */ @@ -79,6 +68,71 @@ typedef enum { SR_NOT_DEFINED //!< SR_NOT_DEFINED } signalReport_t; +typedef enum { + TRANSPORT_ALL_CHANNEL_ID = 0, //!< TRANSPORT_ALL_CHANNEL_ID +#if defined(MY_RADIO_RF24) + TRANSPORT_RF24_CHANNEL_ID, //!< TRANSPORT_RF24_CHANNEL_ID +#endif +#if defined(MY_RADIO_RFM69) + TRANSPORT_RFM69_CHANNEL_ID, //!< TRANSPORT_RFM69_CHANNEL_ID +#endif +#if defined(MY_RADIO_RFM95) + TRANSPORT_RFM95_CHANNEL_ID, //!< TRANSPORT_RFM95_CHANNEL_ID +#endif +#if defined(MY_RADIO_NRF5_ESB) + TRANSPORT_NRF5_ESB_CHANNEL_ID, //!< TRANSPORT_NRF5_ESB_CHANNEL_ID +#endif +#if defined(MY_RS485) + TRANSPORT_RS485_CHANNEL_ID, //!< TRANSPORT_RS485_CHANNEL_ID +#endif +} transportChannelID_t; + +/** +* @brief RXQueuedMessage_t +*/ +typedef struct { + transportChannelID_t channel; //!< channel of origin + //bool encryptedMessage; //!< flag if message was encrypted + uint8_t length; //!< length of data + uint8_t data[RX_QUEUE_MAX_MSG_LENGTH]; //!< raw data +} RXQueuedMessage_t; + +/** +* @brief +* @return true +*/ + +RXQueuedMessage_t *transportHALGetQueueBuffer(void); +/** +* @brief +* @return true +*/ +bool transportHALPushQueueBuffer(RXQueuedMessage_t *buffer); + +/** +* @brief transportGetChannel +* @param nodeId +* @return transport channel ID +*/ +transportChannelID_t transportGetChannel(const uint8_t nodeId) __attribute__((unused)); + +/** +* @brief transportUpdateChannel +* @param nodeId +* @param channel +*/ +void transportUpdateChannel(const uint8_t nodeId, + const transportChannelID_t channel) __attribute__((unused)); + +/** +* @brief transportResetChannels +*/ +void transportResetChannels(void) __attribute__((unused)); + +/** +* @brief transportDebugChannels +*/ +void transportDebugChannels(void) __attribute__((unused)); /** * @brief Initialize transport HW @@ -87,16 +141,17 @@ typedef enum { bool transportHALInit(void); /** * @brief Set node address +* @param address */ void transportHALSetAddress(const uint8_t address); /** * @brief Retrieve node address */ -uint8_t transportHALGetAddress(void); +uint8_t transportHALGetAddress(void) __attribute__((unused)); /** * @brief Send message -* @param to recipient -* @param data message to be sent +* @param nextRecipient recipient +* @param outMsg message to be sent * @param len length of message (header + payload) * @param noACK do not wait for ACK * @return true if message sent successfully @@ -104,6 +159,10 @@ uint8_t transportHALGetAddress(void); bool transportHALSend(const uint8_t nextRecipient, const MyMessage *outMsg, const uint8_t len, const bool noACK); /** +* @brief transportHandler +*/ +void transportHALHandler(void) __attribute__((unused)); +/** * @brief Verify if RX FIFO has pending messages * @return true if message available in RX FIFO */ @@ -114,6 +173,11 @@ bool transportHALDataAvailable(void); */ bool transportHALSanityCheck(void); /** +* @brief transportHALRxCallback +*/ +void transportHALRxCallback(const void *data, const uint8_t len, + const transportChannelID_t channel); +/** * @brief Receive message from FIFO * @param inMsg * @param msgLength length of received message (header + payload) @@ -166,7 +230,7 @@ int16_t transportHALGetTxPowerPercent(void); * @param powerPercent power level in percent * @return True if power level set */ -bool transportHALSetTxPowerPercent(const uint8_t powerPercent); +bool transportHALSetTxPowerPercent(const uint8_t powerPercent) __attribute__((unused)); /** * @brief transportGetTxPowerLevel * @return TX power in dBm diff --git a/hal/transport/MyTransportQueue.cpp b/hal/transport/MyTransportQueue.cpp new file mode 100644 index 000000000..2b27a7081 --- /dev/null +++ b/hal/transport/MyTransportQueue.cpp @@ -0,0 +1,61 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyTransportQueue.h" + +// debug +#if defined(MY_DEBUG_VERBOSE_TRANSPORT_QUEUE) +#define TRANSPORT_QUEUE_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< debug +#else +#define TRANSPORT_QUEUE_DEBUG(x,...) //!< debug NULL +#endif + +static RXQueuedMessage_t transportRxQueueStorage[RX_QUEUE_BUFFER_SIZE]; +static CircularBuffer transportRxQueue(transportRxQueueStorage, + RX_QUEUE_BUFFER_SIZE); + +#define SIZE_CHANNEL_ROUTE 256 +static uint8_t channelRoute[SIZE_CHANNEL_ROUTE]; + +transportChannelID_t transportGetChannel(const uint8_t nodeId) +{ + if(nodeId == BROADCAST_ADDRESS) { + return TRANSPORT_ALL_CHANNEL_ID; + } + uint8_t channel = channelRoute[nodeId]; + TRANSPORT_QUEUE_DEBUG(PSTR("MTQ:GCH:GET N=%d,CH=%d\n"), nodeId, channel); + return static_cast(channel); +} + +void transportUpdateChannel(const uint8_t nodeId, const transportChannelID_t channel) +{ + if (nodeId != BROADCAST_ADDRESS) { + channelRoute[nodeId] = static_cast(channel); + TRANSPORT_QUEUE_DEBUG(PSTR("MTQ:UCH:SET N=%d,CH=%d\n"), nodeId, channel); + } +} + +// reset channel routes +void transportResetChannels(void) +{ + for (uint16_t i = 0; i < SIZE_CHANNEL_ROUTE; i++) { + transportUpdateChannel(i, TRANSPORT_ALL_CHANNEL_ID); + } +} + diff --git a/hal/transport/MyTransportQueue.h b/hal/transport/MyTransportQueue.h new file mode 100644 index 000000000..5169b2d0d --- /dev/null +++ b/hal/transport/MyTransportQueue.h @@ -0,0 +1,38 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyTransportQueue_h +#define MyTransportQueue_h + +#include "hal/transport/MyTransportHAL.h" +#include "drivers/CircularBuffer/CircularBuffer.h" + +#define RX_QUEUE_MAX_MSG_LENGTH 32 +#define RX_QUEUE_BUFFER_SIZE 4 + +/** +* @brief RXQueuedMessage_t +*/ +typedef struct { + transportChannelID_t channel; //!< channel of origin + uint8_t length; //!< length of data + uint8_t data[RX_QUEUE_MAX_MSG_LENGTH]; //!< raw data +} RXQueuedMessage_t; + +#endif // MyTransportQueue_h diff --git a/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp b/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp index 93785bf7c..0781bba36 100644 --- a/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp +++ b/hal/transport/NRF5_ESB/MyTransportNRF5_ESB.cpp @@ -22,97 +22,113 @@ #include "hal/transport/NRF5_ESB/driver/Radio.h" #include "hal/transport/NRF5_ESB/driver/Radio_ESB.h" -#include "drivers/CircularBuffer/CircularBuffer.h" - -bool transportInit(void) +bool NRF5_ESB_transportInit(void) { return NRF5_ESB_initialize(); } -void transportSetAddress(const uint8_t address) +void NRF5_ESB_transportSetAddress(const uint8_t address) { NRF5_ESB_setNodeAddress(address); NRF5_ESB_startListening(); } -uint8_t transportGetAddress(void) +uint8_t NRF5_ESB_transportGetAddress(void) { return NRF5_ESB_getNodeID(); } -bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +bool NRF5_ESB_transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { return NRF5_ESB_sendMessage(to, data, len, noACK); } -bool transportDataAvailable(void) +bool NRF5_ESB_transportDataAvailable(void) { return NRF5_ESB_isDataAvailable(); } -bool transportSanityCheck(void) +bool NRF5_ESB_transportSanityCheck(void) { return NRF5_ESB_sanityCheck(); } -uint8_t transportReceive(void *data) +uint8_t NRF5_ESB_transportReceive(void *data, const uint8_t maxBufSize) { uint8_t len = 0; - len = NRF5_ESB_readMessage(data); + len = NRF5_ESB_readMessage(data, maxBufSize); return len; } -void transportPowerDown(void) +void NRF5_ESB_transportTask(void) +{ +#if defined(MY_TRANSPORT_RX_QUEUE) + while (NRF5_ESB_isDataAvailable()) { + RXQueuedMessage_t *msgIn = transportHALGetQueueBuffer(); + if (msgIn != NULL) { + msgIn->channel = TRANSPORT_NRF5_ESB_CHANNEL_ID; + msgIn->length = NRF5_ESB_readMessage((void *)&msgIn->data, sizeof(msgIn->data)); + (void)transportHALPushQueueBuffer(msgIn); + } else { + // queue full, discard message + (void)NRF5_ESB_readMessage(NULL, MAX_MESSAGE_SIZE); + return; + } + } +#endif +} + +void NRF5_ESB_transportPowerDown(void) { NRF5_ESB_powerDown(); } -void transportPowerUp(void) +void NRF5_ESB_transportPowerUp(void) { NRF5_ESB_powerUp(); } -void transportSleep(void) +void NRF5_ESB_transportSleep(void) { NRF5_ESB_sleep(); } -void transportStandBy(void) +void NRF5_ESB_transportStandBy(void) { NRF5_ESB_standBy(); } -int16_t transportGetSendingRSSI(void) +int16_t NRF5_ESB_transportGetSendingRSSI(void) { return NRF5_ESB_getSendingRSSI(); } -int16_t transportGetReceivingRSSI(void) +int16_t NRF5_ESB_transportGetReceivingRSSI(void) { return NRF5_ESB_getReceivingRSSI(); } -int16_t transportGetSendingSNR(void) +int16_t NRF5_ESB_transportGetSendingSNR(void) { return INVALID_SNR; } -int16_t transportGetReceivingSNR(void) +int16_t NRF5_ESB_transportGetReceivingSNR(void) { return INVALID_SNR; } -int16_t transportGetTxPowerPercent(void) +int16_t NRF5_ESB_transportGetTxPowerPercent(void) { return NRF5_getTxPowerPercent(); } -int16_t transportGetTxPowerLevel(void) +int16_t NRF5_ESB_transportGetTxPowerLevel(void) { return static_cast(NRF5_getTxPowerLevel()); } -bool transportSetTxPowerPercent(const uint8_t powerPercent) +bool NRF5_ESB_transportSetTxPowerPercent(const uint8_t powerPercent) { return NRF5_setTxPowerPercent(powerPercent); } diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp index 538b53d16..449e0b4d5 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.cpp @@ -21,9 +21,7 @@ #include "Radio.h" #include "Radio_ESB.h" -#include "hal/architecture/NRF5/MyHwNRF5.h" #include "drivers/CircularBuffer/CircularBuffer.h" -#include // internal functions static uint8_t reverse_byte(uint8_t address); @@ -32,12 +30,12 @@ inline void _stopACK(); // RX Buffer static NRF5_ESB_Packet rx_circular_buffer_buffer[MY_NRF5_ESB_RX_BUFFER_SIZE]; -// Poiter to rx circular buffer +// Pointer to rx circular buffer static NRF5_ESB_Packet rx_buffer; // Circular buffer static CircularBuffer rx_circular_buffer(rx_circular_buffer_buffer, MY_NRF5_ESB_RX_BUFFER_SIZE); -// Dedect duplicate packages for every pipe available +// Detect duplicate packages for every pipe available static volatile uint32_t package_ids[8]; // TX Buffer @@ -63,6 +61,23 @@ static int8_t tx_power_level = (MY_NRF5_ESB_PA_LEVEL << RADIO_TXPOWER_TXPOWER_Po static bool NRF5_ESB_initialize() { NRF5_RADIO_DEBUG(PSTR("NRF5:INIT:ESB\n")); +#if defined(MY_NRF5_PA_LNA) + NRF5_RADIO_DEBUG(PSTR("NRF5:INIT:PA LNA\n")); +#if defined(MY_NRF5_PA_PIN) + hwPinMode(MY_NRF5_PA_PIN, OUTPUT); +#endif +#if defined(MY_NRF5_LNA_PIN) + hwPinMode(MY_NRF5_LNA_PIN, OUTPUT); +#endif +#if defined(MY_NRF5_CHL_PIN) + hwPinMode(MY_NRF5_CHL_PIN, OUTPUT); + hwDigitalWrite(MY_NRF5_CHL_PIN, MY_NRF5_CHL_MODE); // CHL = TX HIGH POWER +#endif +#if defined(MY_NRF5_CPS_PIN) + hwPinMode(MY_NRF5_CPS_PIN, OUTPUT); + hwDigitalWrite(MY_NRF5_CPS_PIN, MY_NRF5_CPS_MODE); // CPS = RX/TX BYPASS +#endif +#endif #if defined(SOFTDEVICE_PRESENT) // Disable the SoftDevice; requires NRF5 SDK available @@ -276,7 +291,10 @@ static void NRF5_ESB_startListening() if (NRF_RADIO->POWER == 0) { NRF5_ESB_initialize(); } - +#if defined(MY_NRF5_PA_LNA) + hwDigitalWrite(MY_NRF5_PA_PIN, MY_NRF5_PA_DISABLED); // disable PA + hwDigitalWrite(MY_NRF5_LNA_PIN, MY_NRF5_LNA_ENABLED); // enable LNA +#endif #ifdef NRF52 // Fix PAN#102 and PAN#106 *((volatile uint32_t *)0x40001774) = (*((volatile uint32_t *)0x40001774) & 0xFFFFFFFE) | 0x01000000; @@ -301,7 +319,7 @@ static bool NRF5_ESB_isDataAvailable() return rx_circular_buffer.available() > 0; } -static uint8_t NRF5_ESB_readMessage(void *data) +static uint8_t NRF5_ESB_readMessage(void *data, const uint8_t maxBufSize) { uint8_t ret = 0; @@ -310,7 +328,7 @@ static uint8_t NRF5_ESB_readMessage(void *data) // Nothing to read? if (buffer != NULL) { // copy content - memcpy(data, buffer->data, buffer->len); + (void)memcpy(data, (const void *)buffer->data, min(maxBufSize, buffer->len)); ret = buffer->len; rssi_rx = 0-buffer->rssi; @@ -331,6 +349,10 @@ static uint8_t NRF5_ESB_readMessage(void *data) void NRF5_ESB_endtx(); void NRF5_ESB_starttx() { +#if defined(MY_NRF5_PA_LNA) + hwDigitalWrite(MY_NRF5_PA_PIN, MY_NRF5_PA_ENABLED); // enable PA + hwDigitalWrite(MY_NRF5_LNA_PIN, MY_NRF5_LNA_ENABLED); // enable LNA for ACK +#endif if (tx_retries > 0) { // Prevent radio to write into TX memory while receiving if (NRF_RADIO->PACKETPTR != (uint32_t)&tx_buffer) { @@ -391,7 +413,7 @@ void NRF5_ESB_starttx() NRF_RADIO->TASKS_DISABLE = 1; } } else { - // finised TX + // finished TX NRF5_ESB_endtx(); } tx_retries--; @@ -399,6 +421,10 @@ void NRF5_ESB_starttx() void NRF5_ESB_endtx() { +#if defined(MY_NRF5_PA_LNA) + hwDigitalWrite(MY_NRF5_PA_PIN, MY_NRF5_PA_DISABLED); // disable PA + hwDigitalWrite(MY_NRF5_LNA_PIN, MY_NRF5_LNA_ENABLED); // enable LNA +#endif // Clear PPI NRF_PPI->CHENCLR = NRF5_ESB_PPI_BITS; // Enable Ready interrupt @@ -422,7 +448,6 @@ static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len if (NRF_RADIO->POWER == 0) { NRF5_ESB_initialize(); } - // check length and truncate data if (len > MAX_MESSAGE_SIZE) { len = MAX_MESSAGE_SIZE; @@ -632,9 +657,9 @@ extern "C" { NRF_RADIO->BCC = NRF5_ESB_BITCOUNTER; // End of RX packet - if ((NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) or - (NRF_RADIO->STATE == RADIO_STATE_STATE_RxIdle) or - (NRF_RADIO->STATE == RADIO_STATE_STATE_RxDisable) or + if ((NRF_RADIO->STATE == RADIO_STATE_STATE_Rx) || + (NRF_RADIO->STATE == RADIO_STATE_STATE_RxIdle) || + (NRF_RADIO->STATE == RADIO_STATE_STATE_RxDisable) || (NRF_RADIO->STATE == RADIO_STATE_STATE_TxRu)) { if (NRF_RADIO->CRCSTATUS) { // Ensure no ACK package is received diff --git a/hal/transport/NRF5_ESB/driver/Radio_ESB.h b/hal/transport/NRF5_ESB/driver/Radio_ESB.h index bcd37640b..3c7a32e43 100644 --- a/hal/transport/NRF5_ESB/driver/Radio_ESB.h +++ b/hal/transport/NRF5_ESB/driver/Radio_ESB.h @@ -23,7 +23,6 @@ #define __NRF5_ESB_H__ #include "Radio.h" -#include // Check maximum message length #if MAX_MESSAGE_SIZE > (32) @@ -130,7 +129,7 @@ static uint8_t NRF5_ESB_getNodeID(); static void NRF5_ESB_startListening(); static bool NRF5_ESB_isDataAvailable(); -static uint8_t NRF5_ESB_readMessage(void *data); +static uint8_t NRF5_ESB_readMessage(void *data, const uint8_t maxBufSize); static bool NRF5_ESB_sendMessage(uint8_t recipient, const void *buf, uint8_t len, const bool noACK); diff --git a/hal/transport/RF24/MyTransportRF24.cpp b/hal/transport/RF24/MyTransportRF24.cpp index ccac9274e..081482187 100644 --- a/hal/transport/RF24/MyTransportRF24.cpp +++ b/hal/transport/RF24/MyTransportRF24.cpp @@ -19,148 +19,167 @@ #include "hal/transport/RF24/driver/RF24.h" -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -#include "drivers/CircularBuffer/CircularBuffer.h" - -typedef struct _transportQueuedMessage { - uint8_t m_len; // Length of the data - uint8_t m_data[MAX_MESSAGE_SIZE]; // The raw data -} transportQueuedMessage; - -/** Buffer to store queued messages in. */ -static transportQueuedMessage transportRxQueueStorage[MY_RX_MESSAGE_BUFFER_SIZE]; -/** Circular buffer, which uses the transportRxQueueStorage and administers stored messages. */ -static CircularBuffer transportRxQueue(transportRxQueueStorage, - MY_RX_MESSAGE_BUFFER_SIZE); - -static volatile uint8_t transportLostMessageCount = 0; - -static void transportRxCallback(void) -{ - // Called for each message received by radio, from interrupt context. - // This function _must_ call RF24_readMessage() to de-assert interrupt line! - if (!transportRxQueue.full()) { - transportQueuedMessage* msg = transportRxQueue.getFront(); - msg->m_len = RF24_readMessage(msg->m_data); // Read payload & clear RX_DR - (void)transportRxQueue.pushFront(msg); - } else { - // Queue is full. Discard message. - (void)RF24_readMessage(NULL); // Read payload & clear RX_DR - // Keep track of messages lost. Max 255, prevent wrapping. - if (transportLostMessageCount < 255) { - ++transportLostMessageCount; - } - } -} +#if defined(MY_RF24_USE_INTERRUPTS) && !defined(MY_RF24_IRQ_PIN) +#error RF24 is using interrupts but MY_RF24_IRQ_PIN is not defined! #endif -bool transportInit(void) -{ -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - RF24_registerReceiveCallback( transportRxCallback ); +#if defined(MY_RF24_ATC) +uint8_t RF24_TX_POWER[256]; #endif - return RF24_initialize(); -} -void transportSetAddress(const uint8_t address) +void RF24_transportSetAddress(const uint8_t address) { RF24_setNodeAddress(address); RF24_startListening(); } -uint8_t transportGetAddress(void) +uint8_t RF24_transportGetAddress(void) { return RF24_getNodeID(); } -bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +bool RF24_transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { - return RF24_sendMessage(to, data, len, noACK); +#if defined(MY_RF24_ENABLE_ENCRYPTION) + uint8_t tx_data[MAX_MESSAGE_SIZE]; + (void)memcpy((void *)tx_data, data, len); + const uint8_t finalLength = transportEncryptionInsecureAESEncryption(tx_data, len); +#else + const uint8_t finalLength = len; + const void *tx_data = data; +#endif + +#if defined(MY_RF24_ATC) + RF24_setTxPowerLevel(RF24_TX_POWER[to]); + Serial.print("SET RF24 power = "); + Serial.println(RF24_TX_POWER[to]); +#endif + const bool result = RF24_sendMessage(to, tx_data, finalLength, noACK); +#if defined(MY_RF24_ATC) + // do not adjust for broadcasting + if (!noACK) { + const uint8_t TXval = RF24_getObserveTX() & 0xF; + Serial.print("RF24 OBS ="); + Serial.println(TXval); + uint8_t TXpower = RF24_TX_POWER[to]; + if ((TXval > 0) && (TXpower < 3)) { + TXpower++; + Serial.println("INC RF24"); + } else if ((TXval == 0) && (TXpower > 0)) { + TXpower--; + Serial.println("DEC RF24"); + } + RF24_TX_POWER[to] = TXpower; + } +#endif + return result; } -bool transportDataAvailable(void) +bool RF24_transportDataAvailable(void) { -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - (void)RF24_isDataAvailable; // Prevent 'defined but not used' warning - return !transportRxQueue.empty(); -#else return RF24_isDataAvailable(); +} + +uint8_t RF24_transportReceive(void *data, const uint8_t maxBufSize) +{ + (void)maxBufSize; // size alreeady limited to 32 in RF24 driver + const uint8_t len = RF24_readMessage(data); +#if defined(MY_RF24_ENABLE_ENCRYPTION) + transportEncryptionInsecureAESDecryption(data, len); +#endif + return len; +} + +void RF24_transportTask(void) +{ +#if defined(MY_TRANSPORT_RX_QUEUE) +#if defined(MY_RF24_USE_INTERRUPTS) + if (!RF24_isDataAvailable()) { // IRQ flag set? + return; + } +#endif + while (!RF24_isFIFOempty()) { + RXQueuedMessage_t *msgIn = transportHALGetQueueBuffer(); + if (msgIn != NULL) { + msgIn->channel = TRANSPORT_RF24_CHANNEL_ID; + msgIn->length = RF24_transportReceive((void *)&msgIn->data, sizeof(msgIn->data)); + (void)transportHALPushQueueBuffer(msgIn); + } else { + // queue full, discard message + (void)RF24_readMessage(NULL); + return; + } + } #endif } -bool transportSanityCheck(void) +bool RF24_transportSanityCheck(void) { return RF24_sanityCheck(); } -uint8_t transportReceive(void *data) +bool RF24_transportInit(void) { - uint8_t len = 0; -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - transportQueuedMessage* msg = transportRxQueue.getBack(); - if (msg) { - len = msg->m_len; - (void)memcpy(data, msg->m_data, len); - (void)transportRxQueue.popBack(); +#if defined(MY_RF24_ATC) + for (uint16_t i = 0; i < sizeof(RF24_TX_POWER); i++) { + RF24_TX_POWER[i] = MY_RF24_PA_LEVEL; } -#else - len = RF24_readMessage(data); #endif - return len; + return RF24_initialize(); } -void transportSleep(void) +void RF24_transportSleep(void) { RF24_sleep(); } -void transportStandBy(void) +void RF24_transportStandBy(void) { RF24_standBy(); } -void transportPowerDown(void) +void RF24_transportPowerDown(void) { RF24_powerDown(); } -void transportPowerUp(void) +void RF24_transportPowerUp(void) { RF24_powerUp(); } -int16_t transportGetSendingRSSI(void) +int16_t RF24_transportGetSendingRSSI(void) { return RF24_getSendingRSSI(); } -int16_t transportGetReceivingRSSI(void) +int16_t RF24_transportGetReceivingRSSI(void) { // not available, only bool RPD return INVALID_RSSI; } -int16_t transportGetSendingSNR(void) +int16_t RF24_transportGetSendingSNR(void) { return INVALID_SNR; } -int16_t transportGetReceivingSNR(void) +int16_t RF24_transportGetReceivingSNR(void) { return INVALID_SNR; } -int16_t transportGetTxPowerPercent(void) +int16_t RF24_transportGetTxPowerPercent(void) { return static_cast(RF24_getTxPowerPercent()); } -int16_t transportGetTxPowerLevel(void) +int16_t RF24_transportGetTxPowerLevel(void) { return static_cast(RF24_getTxPowerLevel()); } -bool transportSetTxPowerPercent(const uint8_t powerPercent) +bool RF24_transportSetTxPowerPercent(const uint8_t powerPercent) { return RF24_setTxPowerPercent(powerPercent); } diff --git a/hal/transport/RF24/driver/RF24.cpp b/hal/transport/RF24/driver/RF24.cpp index 38217b673..30f16cb7c 100644 --- a/hal/transport/RF24/driver/RF24.cpp +++ b/hal/transport/RF24/driver/RF24.cpp @@ -29,13 +29,13 @@ #define RF24_DEBUG(x,...) //!< DEBUG null #endif +#if defined(MY_RF24_USE_INTERRUPTS) +volatile bool RF24_irq; +#endif + LOCAL uint8_t RF24_BASE_ID[MY_RF24_ADDR_WIDTH] = { MY_RF24_BASE_RADIO_ID }; LOCAL uint8_t RF24_NODE_ADDRESS = RF24_BROADCAST_ADDRESS; -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -LOCAL RF24_receiveCallbackType RF24_receiveCallback = NULL; -#endif - #if defined(__linux__) uint8_t RF24_spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes) uint8_t RF24_spi_txbuff[32+1] @@ -97,6 +97,7 @@ LOCAL uint8_t RF24_spiMultiByteTransfer(const uint8_t cmd, uint8_t *buf, uint8_t status = *prx; // status is 1st byte of receive buffer } #else + // first byte retrieved is status byte status = RF24_SPI.transfer(cmd); while ( len-- ) { if (readMode) { @@ -314,12 +315,24 @@ LOCAL bool RF24_sendMessage(const uint8_t recipient, const void *buf, const uint RF24_flushTX(); if (noACK) { // noACK messages are only sent once - RF24_setRetries(RF24_SET_ARD, 0); + RF24_setRetries(RF24_SET_ARD, MY_RF24_BC_RETRIES); } // this command is affected in clones (e.g. Si24R1): flipped NoACK bit when using W_TX_PAYLOAD_NO_ACK / W_TX_PAYLOAD // AutoACK is disabled on the broadcasting pipe - NO_ACK prevents resending (void)RF24_spiMultiByteTransfer(RF24_CMD_WRITE_TX_PAYLOAD, (uint8_t *)buf, len, false); // go, TX starts after ~10us, CE high also enables PA+LNA on supported HW +#if defined(MY_RF24_USE_INTERRUPTS) + RF24_irq = false; + RF24_ce(HIGH); + const uint32_t enteringMS = hwMillis(); + while (!RF24_irq && (hwMillis() - enteringMS < RF24_TX_TIMEOUT_MS)) { + doYield(); + } + RF24_DEBUG(PSTR("RF24:TXM:ST=%" PRIu8 ",FI=%" PRIu8 ",IRQ=%" PRIu8 "\n"), RF24_getStatus(), + RF24_getFIFOStatus(), RF24_irq); // + // reset IRQ only if RX FIFO empty + RF24_irq = !RF24_isFIFOempty(); +#else RF24_ce(HIGH); // timeout counter to detect HW issues uint16_t timeout = 0xFFFF; @@ -327,6 +340,7 @@ LOCAL bool RF24_sendMessage(const uint8_t recipient, const void *buf, const uint doYield(); } // timeout value after successful TX on 16Mhz AVR ~ 65500, i.e. msg is transmitted after ~36 loop cycles +#endif RF24_ce(LOW); // reset interrupts const uint8_t RF24_status = RF24_setStatus(_BV(RF24_RX_DR) | _BV(RF24_TX_DS) | _BV(RF24_MAX_RT)); @@ -356,15 +370,25 @@ LOCAL uint8_t RF24_getDynamicPayloadSize(void) return result; } -LOCAL bool RF24_isDataAvailable(void) +LOCAL bool RF24_isFIFOempty(void) { // prevent debug message flooding -#if defined(MY_DEBUG_VERBOSE_RF24) +#if defined(MY_DEBUG_VERBOSE_RF24) && !defined(MY_RF24_USE_INTERRUPTS) const uint8_t value = RF24_spiMultiByteTransfer(RF24_CMD_READ_REGISTER | (RF24_REGISTER_MASK & (RF24_REG_FIFO_STATUS)), NULL, 1, true); - return (bool)(!(value & _BV(RF24_RX_EMPTY))); + return value & _BV(RF24_RX_EMPTY); #else - return (bool)(!(RF24_getFIFOStatus() & _BV(RF24_RX_EMPTY)) ); + return RF24_getFIFOStatus() & _BV(RF24_RX_EMPTY); +#endif +} + +LOCAL bool RF24_isDataAvailable(void) +{ +#if defined(MY_RF24_USE_INTERRUPTS) + return RF24_irq; +#else + // polling FIFO status + return (!RF24_isFIFOempty()); #endif } @@ -375,6 +399,10 @@ LOCAL uint8_t RF24_readMessage(void *buf) RF24_spiMultiByteTransfer(RF24_CMD_READ_RX_PAYLOAD, (uint8_t *)buf, len, true); // clear RX interrupt (void)RF24_setStatus(_BV(RF24_RX_DR)); +#if defined(MY_RF24_USE_INTERRUPTS) + // clear IRQ flag + RF24_irq = false; +#endif return len; } @@ -453,60 +481,10 @@ LOCAL bool RF24_getReceivedPowerDetector(void) // slightly different and takes at least 128us to become active. return (RF24_readByteRegister(RF24_REG_RPD) & _BV(RF24_RPD)) != 0; } - -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) +#if defined(MY_RF24_USE_INTERRUPTS) LOCAL void IRQ_HANDLER_ATTR RF24_irqHandler(void) { - if (RF24_receiveCallback) { -#if defined(MY_GATEWAY_SERIAL) && !defined(__linux__) - // Will stay for a while (several 100us) in this interrupt handler. Any interrupts from serial - // rx coming in during our stay will not be handled and will cause characters to be lost. - // As a workaround we re-enable interrupts to allow nested processing of other interrupts. - // Our own handler is disconnected to prevent recursive calling of this handler. - detachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN)); - interrupts(); -#endif - // Read FIFO until empty. - // Procedure acc. to datasheet (pg. 63): - // 1.Read payload, 2.Clear RX_DR IRQ, 3.Read FIFO_status, 4.Repeat when more data available. - // Datasheet (ch. 8.5) states, that the nRF de-asserts IRQ after reading STATUS. - -#if defined(__linux__) - // Start checking if RX-FIFO is not empty, as we might end up here from an interrupt - // for a message we've already read. - if (RF24_isDataAvailable()) { - do { - RF24_receiveCallback(); // Must call RF24_readMessage(), which will clear RX_DR IRQ ! - } while (RF24_isDataAvailable()); - } else { - // Occasionally interrupt is triggered but no data is available - clear RX interrupt only - RF24_setStatus(_BV(RF24_RX_DR)); - logNotice("RF24: Recovered from a bad interrupt trigger.\n"); - } -#else - // Start checking if RX-FIFO is not empty, as we might end up here from an interrupt - // for a message we've already read. - while (RF24_isDataAvailable()) { - RF24_receiveCallback(); // Must call RF24_readMessage(), which will clear RX_DR IRQ ! - } -#endif - -#if defined(MY_GATEWAY_SERIAL) && !defined(__linux__) - // Restore our interrupt handler. - noInterrupts(); - attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), RF24_irqHandler, FALLING); -#endif - } else { - // clear RX interrupt - RF24_setStatus(_BV(RF24_RX_DR)); - } -} - -LOCAL void RF24_registerReceiveCallback(RF24_receiveCallbackType cb) -{ - MY_CRITICAL_SECTION { - RF24_receiveCallback = cb; - } + RF24_irq = true; } #endif @@ -518,7 +496,7 @@ LOCAL bool RF24_initialize(void) hwPinMode(MY_RF24_POWER_PIN, OUTPUT); #endif RF24_powerUp(); -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) +#if defined(MY_RF24_USE_INTERRUPTS) hwPinMode(MY_RF24_IRQ_PIN,INPUT); #endif hwPinMode(MY_RF24_CE_PIN, OUTPUT); @@ -530,12 +508,9 @@ LOCAL bool RF24_initialize(void) // Initialize SPI RF24_SPI.begin(); -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) - // assure SPI can be used from interrupt context - // Note: ESP8266 & SoftSPI currently do not support interrupt usage for SPI, - // therefore it is unsafe to use MY_RF24_IRQ_PIN with ESP8266/SoftSPI! - RF24_SPI.usingInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN)); - // attach interrupt +#if defined(MY_RF24_USE_INTERRUPTS) + RF24_irq = false; + RF24_DEBUG(PSTR("RF24:INIT:IRQ=%" PRIu8 "\n"), MY_RF24_IRQ_PIN); attachInterrupt(digitalPinToInterrupt(MY_RF24_IRQ_PIN), RF24_irqHandler, FALLING); #endif // power up and standby @@ -572,6 +547,6 @@ LOCAL bool RF24_initialize(void) RF24_flushRX(); RF24_flushTX(); // reset interrupts - RF24_setStatus(_BV(RF24_TX_DS) | _BV(RF24_MAX_RT) | _BV(RF24_RX_DR)); + (void)RF24_setStatus(_BV(RF24_TX_DS) | _BV(RF24_MAX_RT) | _BV(RF24_RX_DR)); return true; } diff --git a/hal/transport/RF24/driver/RF24.h b/hal/transport/RF24/driver/RF24.h index e578e6b9f..cf909ff3b 100644 --- a/hal/transport/RF24/driver/RF24.h +++ b/hal/transport/RF24/driver/RF24.h @@ -37,6 +37,7 @@ * |E| SYS | SUB | Message | Comment * |-|------|------|----------------------|--------------------------------------------------------------------- * | | RF24 | INIT | PIN,CE=%%d,CS=%%d | Initialise RF24 radio, pin configuration: chip enable (CE), chip select (CS) +* | | RF24 | INIT | IRQ=%%d | IRQ handler attached to pin (IRQ) * |!| RF24 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module * | | RF24 | SPP | PCT=%%d,TX LEVEL=%%d | Set TX level, input TX percent (PCT) * | | RF24 | RBR | REG=%%d,VAL=%%d | Read register (REG), value=(VAL) @@ -49,7 +50,7 @@ * | | RF24 | SLP | | Set radio to sleep * | | RF24 | SBY | | Set radio to standby * | | RF24 | TXM | TO=%%d,LEN=%%d | Transmit message to=(TO), length=(LEN) -* |!| RF24 | TXM | MAX_RT | Max TX retries, no ACK received +* |?| RF24 | TXM | MAX_RT | Max TX retries, no ACK received (if non-BC message) * |!| RF24 | GDP | PYL INV | Invalid payload size * | | RF24 | RXM | LEN=%%d | Read message, length=(LEN) * | | RF24 | STX | LEVEL=%%d | Set TX level, level=(LEVEL) @@ -75,7 +76,6 @@ #define DEFAULT_RF24_CE_PIN (27) //!< DEFAULT_RF24_CE_PIN #elif defined(LINUX_ARCH_RASPBERRYPI) #define DEFAULT_RF24_CE_PIN (22) //!< DEFAULT_RF24_CE_PIN -//#define DEFAULT_RF24_CS_PIN (24) //!< DEFAULT_RF24_CS_PIN #elif defined(ARDUINO_ARCH_STM32F1) #define DEFAULT_RF24_CE_PIN (PB0) //!< DEFAULT_RF24_CE_PIN #elif defined(TEENSYDUINO) @@ -90,49 +90,20 @@ #define LOCAL static //!< static // SPI settings -#define RF24_SPI_DATA_ORDER MSBFIRST //!< RF24_SPI_DATA_ORDER -#define RF24_SPI_DATA_MODE SPI_MODE0 //!< RF24_SPI_DATA_MODE - -#define RF24_BROADCAST_ADDRESS (255u) //!< RF24_BROADCAST_ADDRESS - -// verify RF24 IRQ defs -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -#if !defined(MY_RF24_IRQ_PIN) -#error Message buffering feature requires MY_RF24_IRQ_PIN to be defined! -#endif -// SoftSPI does not support usingInterrupt() -#ifdef MY_SOFTSPI -#error RF24 IRQ usage cannot be used with Soft SPI -#endif -// ESP8266 does not support usingInterrupt() -#ifdef ARDUINO_ARCH_ESP8266 -#error RF24 IRQ usage cannot be used with ESP8266 -#endif -#ifndef SPI_HAS_TRANSACTION -#error RF24 IRQ usage requires transactional SPI support -#endif -#else -#ifdef MY_RX_MESSAGE_BUFFER_SIZE -#error Receive message buffering requires RF24 IRQ usage -#endif -#endif +#define RF24_SPI_DATA_ORDER MSBFIRST //!< RF24_SPI_DATA_ORDER +#define RF24_SPI_DATA_MODE SPI_MODE0 //!< RF24_SPI_DATA_MODE +#define RF24_BROADCAST_ADDRESS (255u) //!< RF24_BROADCAST_ADDRESS // RF24 settings -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -#define RF24_CONFIGURATION (uint8_t) ((RF24_CRC_16 << 2) | (1 << RF24_MASK_TX_DS) | (1 << RF24_MASK_MAX_RT)) //!< MY_RF24_CONFIGURATION -#else -#define RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 2) //!< RF24_CONFIGURATION -#endif -#define RF24_FEATURE (uint8_t)( _BV(RF24_EN_DPL)) //!< RF24_FEATURE -#define RF24_RF_SETUP (uint8_t)(( ((MY_RF24_DATARATE & 0b10 ) << 4) | ((MY_RF24_DATARATE & 0b01 ) << 3) | (MY_RF24_PA_LEVEL << 1) ) + 1) //!< RF24_RF_SETUP, +1 for Si24R1 and LNA - -// powerup delay -#define RF24_POWERUP_DELAY_MS (100u) //!< Power up delay, allow VCC to settle, transport to become fully operational - -// pipes -#define RF24_BROADCAST_PIPE (1u) //!< RF24_BROADCAST_PIPE -#define RF24_NODE_PIPE (0u) //!< RF24_NODE_PIPE +#define RF24_CONFIGURATION (uint8_t) (RF24_CRC_16 << 2) //!< RF24_CONFIGURATION +#define RF24_FEATURE (uint8_t)(_BV(RF24_EN_DPL)) //!< RF24_FEATURE +#define RF24_RF_SETUP (uint8_t)(( ((MY_RF24_DATARATE & 0b10 ) << 4) | ((MY_RF24_DATARATE & 0b01 ) << 3) | (MY_RF24_PA_LEVEL << 1) ) + 1) //!< RF24_RF_SETUP, +1 for Si24R1 and LNA +#define RF24_POWERUP_DELAY_MS (100u) //!< Power up delay, allow VCC to settle, transport to become fully operational +#define RF24_TX_TIMEOUT_MS (1000u) //!< TX timeout +#define MY_RF24_BC_RETRIES (0u) //!< TX retries for BC messages +#define RF24_BROADCAST_PIPE (1u) //!< RF24_BROADCAST_PIPE +#define RF24_NODE_PIPE (0u) //!< RF24_NODE_PIPE // functions /** @@ -192,7 +163,7 @@ LOCAL void RF24_flushTX(void); * @brief RF24_getStatus * @return */ -LOCAL uint8_t RF24_getStatus(void); +LOCAL uint8_t RF24_getStatus(void) __attribute__((unused)); /** * @brief RF24_getFIFOStatus * @return @@ -243,6 +214,11 @@ LOCAL bool RF24_sendMessage(const uint8_t recipient, const void *buf, const uint */ LOCAL uint8_t RF24_getDynamicPayloadSize(void); /** +* @brief RF24_isFIFOempty +* @return True if FIFO empty +*/ +LOCAL bool RF24_isFIFOempty(void) __attribute__((unused)); +/** * @brief RF24_isDataAvailable * @return */ @@ -386,22 +362,6 @@ LOCAL void RF24_disableConstantCarrierWave(void) __attribute__((unused)); */ LOCAL bool RF24_getReceivedPowerDetector(void) __attribute__((unused)); -#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) -/** -* @brief Callback type -*/ -typedef void (*RF24_receiveCallbackType)(void); -/** -* @brief RF24_registerReceiveCallback -* Register a callback, which will be called (from interrupt context) for every message received. -* @note When a callback is registered, it _must_ retrieve the message from the nRF24 -* by calling RF24_readMessage(). Otherwise the interrupt will not get deasserted -* and message reception will stop. -* @param cb -*/ -LOCAL void RF24_registerReceiveCallback(RF24_receiveCallbackType cb); -#endif - #endif // __RF24_H__ /** @}*/ diff --git a/hal/transport/RF24/driver/RF24registers.h b/hal/transport/RF24/driver/RF24registers.h index ab30f00c7..d1a0b08ec 100644 --- a/hal/transport/RF24/driver/RF24registers.h +++ b/hal/transport/RF24/driver/RF24registers.h @@ -44,7 +44,7 @@ #define RF24_SET_ARD (5) //=1500us // ARD, auto retry count -#define RF24_SET_ARC (15) +#define RF24_SET_ARC (10) // nRF24L01(+) register definitions #define RF24_REG_NRF_CONFIG (0x00) diff --git a/hal/transport/RFM69/MyTransportRFM69.cpp b/hal/transport/RFM69/MyTransportRFM69.cpp index e005582af..3cfc0aca5 100644 --- a/hal/transport/RFM69/MyTransportRFM69.cpp +++ b/hal/transport/RFM69/MyTransportRFM69.cpp @@ -21,7 +21,7 @@ #include "hal/transport/RFM69/driver/new/RFM69_new.h" -bool transportInit(void) +bool RFM69_transportInit(void) { const bool result = RFM69_initialise(MY_RFM69_FREQUENCY); #if defined(MY_GATEWAY_FEATURE) || defined(MY_RFM69_ATC_MODE_DISABLED) @@ -30,86 +30,87 @@ bool transportInit(void) #else RFM69_ATCmode(true, MY_RFM69_ATC_TARGET_RSSI_DBM); #endif - -#ifdef MY_RFM69_ENABLE_ENCRYPTION - uint8_t RFM69_psk[16]; -#ifdef MY_ENCRYPTION_SIMPLE_PASSWD - (void)memset(RFM69_psk, 0, 16); - (void)memcpy(RFM69_psk, MY_ENCRYPTION_SIMPLE_PASSWD, strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, 16)); -#else - hwReadConfigBlock((void *)RFM69_psk, (void*)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); -#endif - RFM69_encrypt((const char *)RFM69_psk); - (void)memset(RFM69_psk, 0, 16); // Make sure it is purged from memory when set -#else - (void)RFM69_encrypt; -#endif return result; } -void transportSetAddress(const uint8_t address) +void RFM69_transportSetAddress(const uint8_t address) { RFM69_setAddress(address); } -uint8_t transportGetAddress(void) +uint8_t RFM69_transportGetAddress(void) { return RFM69_getAddress(); } -bool transportSend(const uint8_t to, const void *data, uint8_t len, const bool noACK) +bool RFM69_transportSend(const uint8_t to, const void *data, uint8_t len, const bool noACK) { return RFM69_sendWithRetry(to, data, len, noACK); } -bool transportDataAvailable(void) +bool RFM69_transportDataAvailable(void) { - RFM69_handler(); return RFM69_available(); } -bool transportSanityCheck(void) +void RFM69_transportTask(void) { - return RFM69_sanityCheck(); + RFM69_handling(); +#if defined(MY_TRANSPORT_RX_QUEUE) + if (RFM69_available()) { + RXQueuedMessage_t *msgIn = transportHALGetQueueBuffer(); + if (msgIn != NULL) { + msgIn->channel = TRANSPORT_RFM69_CHANNEL_ID; + msgIn->length = RFM69_receive((uint8_t *)&msgIn->data, + sizeof(msgIn->data)); + (void)transportHALPushQueueBuffer(msgIn); + } + } +#endif +} + +void RFM69_transportEncrypt(const uint8_t *key) +{ + RFM69_HWencryption((const char *)key); } -uint8_t transportReceive(void *data) +bool RFM69_transportSanityCheck(void) { - return RFM69_receive((uint8_t *)data, MAX_MESSAGE_SIZE); + return RFM69_sanityCheck(); } -void transportEncrypt(const char *key) +uint8_t RFM69_transportReceive(void *data, const uint8_t maxBufSize) { - RFM69_encrypt(key); + return RFM69_receive((uint8_t *)data, maxBufSize); } -void transportSleep(void) +void RFM69_transportSleep(void) { (void)RFM69_sleep(); } -void transportStandBy(void) +void RFM69_transportStandBy(void) { (void)RFM69_standBy(); } -void transportPowerDown(void) +void RFM69_transportPowerDown(void) { (void)RFM69_powerDown(); } -void transportPowerUp(void) +void RFM69_transportPowerUp(void) { (void)RFM69_powerUp(); } -bool transportSetTxPowerLevel(const uint8_t powerLevel) +bool RFM69_transportSetTxPowerLevel(const uint8_t powerLevel) { // range 0..23 return RFM69_setTxPowerLevel(powerLevel); } -void transportSetTargetRSSI(const int16_t targetSignalStrength) +void RFM69_transportSetTargetRSSI(const int16_t targetSignalStrength) { #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RFM69_ATC_MODE_DISABLED) RFM69_ATCmode(true, targetSignalStrength); @@ -118,37 +119,37 @@ void transportSetTargetRSSI(const int16_t targetSignalStrength) #endif } -int16_t transportGetSendingRSSI(void) +int16_t RFM69_transportGetSendingRSSI(void) { return RFM69_getSendingRSSI(); } -int16_t transportGetReceivingRSSI(void) +int16_t RFM69_transportGetReceivingRSSI(void) { return RFM69_getReceivingRSSI(); } -int16_t transportGetSendingSNR(void) +int16_t RFM69_transportGetSendingSNR(void) { return INVALID_SNR; } -int16_t transportGetReceivingSNR(void) +int16_t RFM69_transportGetReceivingSNR(void) { return INVALID_SNR; } -int16_t transportGetTxPowerPercent(void) +int16_t RFM69_transportGetTxPowerPercent(void) { return RFM69_getTxPowerPercent(); } -int16_t transportGetTxPowerLevel(void) +int16_t RFM69_transportGetTxPowerLevel(void) { return RFM69_getTxPowerLevel(); } -bool transportSetTxPowerPercent(const uint8_t powerPercent) +bool RFM69_transportSetTxPowerPercent(const uint8_t powerPercent) { return RFM69_setTxPowerPercent(powerPercent); } @@ -160,7 +161,7 @@ bool transportSetTxPowerPercent(const uint8_t powerPercent) RFM69 _radio(MY_RFM69_CS_PIN, MY_RFM69_IRQ_PIN, MY_RFM69HW, MY_RFM69_IRQ_NUM); uint8_t _address; -bool transportInit(void) +bool RFM69_transportInit(void) { #if defined(MY_RFM69_POWER_PIN) //hwPinMode(MY_RFM69_POWER_PIN, OUTPUT); @@ -170,39 +171,28 @@ bool transportInit(void) #endif // Start up the radio library (_address will be set later by the MySensors library) if (_radio.initialize(MY_RFM69_FREQUENCY, _address, MY_RFM69_NETWORKID)) { -#ifdef MY_RFM69_ENABLE_ENCRYPTION - uint8_t RFM69_psk[16]; -#ifdef MY_ENCRYPTION_SIMPLE_PASSWD - (void)memset(RFM69_psk, 0, 16); - (void)memcpy(RFM69_psk, MY_ENCRYPTION_SIMPLE_PASSWD, strnlen(MY_ENCRYPTION_SIMPLE_PASSWD, 16)); -#else - hwReadConfigBlock((void *)RFM69_psk, (void *)EEPROM_RF_ENCRYPTION_AES_KEY_ADDRESS, 16); -#endif - _radio.encrypt((const char *)RFM69_psk); - (void)memset(RFM69_psk, 0, 16); // Make sure it is purged from memory when set -#endif return true; } return false; } -void transportEncrypt(const char *key) +void RFM69_transportEncrypt(const uint8_t *key) { _radio.encrypt(key); } -void transportSetAddress(const uint8_t address) +void RFM69_transportSetAddress(const uint8_t address) { _address = address; _radio.setAddress(address); } -uint8_t transportGetAddress(void) +uint8_t RFM69_transportGetAddress(void) { return _address; } -bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +bool RFM69_transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { if (noACK) { (void)_radio.sendWithRetry(to, data, len, 0, 0); @@ -210,19 +200,23 @@ bool transportSend(const uint8_t to, const void *data, const uint8_t len, const } return _radio.sendWithRetry(to, data, len); } - -bool transportDataAvailable(void) +void RFM69_transportTask(void) +{ + // NOP +} +bool RFM69_transportDataAvailable(void) { return _radio.receiveDone(); } -bool transportSanityCheck(void) +bool RFM69_transportSanityCheck(void) { return _radio.sanityCheck(); } -uint8_t transportReceive(void *data) +uint8_t RFM69_transportReceive(void *data, const uint8_t maxBufSize) { + (void)maxBufSize; // save payload length const uint8_t dataLen = _radio.DATALEN < MAX_MESSAGE_SIZE ? _radio.DATALEN : MAX_MESSAGE_SIZE; (void)memcpy((void *)data, (void *)_radio.DATA, dataLen); @@ -233,57 +227,64 @@ uint8_t transportReceive(void *data) return dataLen; } -void transportSleep(void) +void RFM69_transportSleep(void) { _radio.sleep(); } -void transportStandBy(void) +void RFM69_transportStandBy(void) { _radio.standBy(); } -void transportPowerDown(void) +void RFM69_transportPowerDown(void) { _radio.powerDown(); } -void transportPowerUp(void) +void RFM69_transportPowerUp(void) { _radio.powerUp(); } -int16_t transportGetSendingRSSI(void) +int16_t RFM69_transportGetSendingRSSI(void) { return INVALID_RSSI; } -int16_t transportGetReceivingRSSI(void) +int16_t RFM69_transportGetReceivingRSSI(void) { return _radio.RSSI; } -int16_t transportGetSendingSNR(void) +int16_t RFM69_transportGetSendingSNR(void) { return INVALID_SNR; } -int16_t transportGetReceivingSNR(void) +int16_t RFM69_transportGetReceivingSNR(void) { return INVALID_SNR; } -int16_t transportGetTxPowerPercent(void) +int16_t RFM69_transportGetTxPowerPercent(void) { return INVALID_PERCENT; } -int16_t transportGetTxPowerLevel(void) +bool RFM69_transportSetTxPowerPercent(const uint8_t powerPercent) +{ + // not implemented + (void)powerPercent; + return false; +} + +int16_t RFM69_transportGetTxPowerLevel(void) { return INVALID_LEVEL; } -bool transportSetTxPowerLevel(const uint8_t powerLevel) +bool RFM69_transportSetTxPowerLevel(const uint8_t powerLevel) { // not implemented (void)powerLevel; diff --git a/hal/transport/RFM69/driver/new/RFM69_new.cpp b/hal/transport/RFM69/driver/new/RFM69_new.cpp index 64d1f4efc..bfcd0e5ab 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.cpp +++ b/hal/transport/RFM69/driver/new/RFM69_new.cpp @@ -23,7 +23,7 @@ * - Automatic Transmit Power Control class derived from RFM69 library. * Discussion and details in this forum post: https://lowpowerlab.com/forum/index.php/topic,688.0.html * Copyright Thomas Studwell (2014,2015) - * - MySensors generic radio driver implementation Copyright (C) 2017, 2018 Olivier Mauti + * - MySensors generic radio driver implementation Copyright (C) 2017-2019 Olivier Mauti * * Changes by : @tekka, @scalz, @marceloagno * @@ -44,6 +44,7 @@ rfm69_internal_t RFM69; //!< internal variables volatile uint8_t RFM69_irq; //!< rfm69 irq flag + #if defined(__linux__) // SPI RX and TX buffers (max packet len + 1 byte for the command) uint8_t RFM69_spi_rxbuff[RFM69_MAX_PACKET_LEN + 1]; @@ -65,10 +66,12 @@ LOCAL void RFM69_prepareSPITransaction(void) RFM69_SPI.beginTransaction(SPISettings(MY_RFM69_SPI_SPEED, RFM69_SPI_DATA_ORDER, RFM69_SPI_DATA_MODE)); #endif + RFM69_csn(LOW); } LOCAL void RFM69_concludeSPITransaction(void) { + RFM69_csn(HIGH); #if !defined(MY_SOFTSPI) && defined(SPI_HAS_TRANSACTION) RFM69_SPI.endTransaction(); #endif @@ -81,7 +84,6 @@ LOCAL uint8_t RFM69_spiMultiByteTransfer(const uint8_t cmd, uint8_t *buf, uint8_ uint8_t *current = buf; RFM69_prepareSPITransaction(); - RFM69_csn(LOW); #if defined(__linux__) uint8_t *prx = RFM69_spi_rxbuff; @@ -119,11 +121,10 @@ LOCAL uint8_t RFM69_spiMultiByteTransfer(const uint8_t cmd, uint8_t *buf, uint8_ *current++ = status; } } else { - status = RFM69_SPI.transfer(*current++); + (void)RFM69_SPI.transfer(*current++); } } #endif - RFM69_csn(HIGH); RFM69_concludeSPITransaction(); return status; } @@ -172,7 +173,6 @@ LOCAL inline int16_t RFM69_internalToRSSI(const rfm69_RSSI_t internalRSSI) LOCAL bool RFM69_initialise(const uint32_t frequencyHz) { - RFM69_DEBUG(PSTR("RFM69:INIT\n")); // power up radio if power pin defined #if defined(MY_RFM69_POWER_PIN) hwPinMode(MY_RFM69_POWER_PIN, OUTPUT); @@ -202,10 +202,11 @@ LOCAL bool RFM69_initialise(const uint32_t frequencyHz) RFM69.ackReceived = false; RFM69.txSequenceNumber = 0; // initialise TX sequence counter RFM69.powerLevel = MY_RFM69_TX_POWER_DBM + 1; // will be overwritten when set - RFM69.radioMode = RFM69_RADIO_MODE_SLEEP; + //RFM69.radioMode = RFM69_RADIO_MODE_SLEEP; ==> no need to initialize RFM69.ATCenabled = false; RFM69.ATCtargetRSSI = RFM69_RSSItoInternal(MY_RFM69_ATC_TARGET_RSSI_DBM); - + RFM69.lastPacket.sender = RFM69_BROADCAST_ADDRESS; + RFM69.lastPacket.sequenceNumber = 0xFF; // SPI init #if !defined(__linux__) hwDigitalWrite(MY_RFM69_CS_PIN, HIGH); @@ -220,8 +221,6 @@ LOCAL bool RFM69_initialise(const uint32_t frequencyHz) #if defined(MY_DEBUG_VERBOSE_RFM69_REGISTERS) RFM69_readAllRegs(); -#else - (void)RFM69_readAllRegs; #endif //RFM69_DEBUG(PSTR("RFM69:INIT:HWV=%" PRIu8 "\n"),RFM69_readReg(RFM69_REG_VERSION)); @@ -237,10 +236,6 @@ LOCAL bool RFM69_initialise(const uint32_t frequencyHz) return true; } -LOCAL void RFM69_clearFIFO(void) -{ - (void)RFM69_writeReg(RFM69_REG_IRQFLAGS2, RFM69_IRQFLAGS2_FIFOOVERRUN); -} // IRQ handler: PayloadReady (RX) & PacketSent (TX) mapped to DI0 LOCAL void IRQ_HANDLER_ATTR RFM69_interruptHandler(void) { @@ -248,83 +243,109 @@ LOCAL void IRQ_HANDLER_ATTR RFM69_interruptHandler(void) RFM69_irq = true; } -LOCAL void RFM69_interruptHandling(void) +LOCAL void RFM69_RXFIFOHandling(void) { - const uint8_t regIrqFlags2 = RFM69_readReg(RFM69_REG_IRQFLAGS2); - if (RFM69.radioMode == RFM69_RADIO_MODE_RX && (regIrqFlags2 & RFM69_IRQFLAGS2_PAYLOADREADY)) { - (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); - // use the fifo level irq as indicator if header bytes received - if (regIrqFlags2 & RFM69_IRQFLAGS2_FIFOLEVEL) { - RFM69_prepareSPITransaction(); - RFM69_csn(LOW); + // reset flags + RFM69.ackReceived = false; + RFM69.dataReceived = false; + RFM69.msgOK = true; + RFM69_prepareSPITransaction(); #if defined(__linux__) - char data[RFM69_MAX_PACKET_LEN + 1]; // max packet len + 1 byte for the command - data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; - RFM69_SPI.transfern(data, 3); + char data[RFM69_MAX_PACKET_LEN + 1]; // max packet len + 1 byte for the command + data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; + RFM69_SPI.transfern(data, 3); - RFM69.currentPacket.header.packetLen = data[1]; - RFM69.currentPacket.header.recipient = data[2]; + RFM69.currentPacket.header.packetLen = data[1]; + RFM69.currentPacket.header.recipient = data[2]; - if (RFM69.currentPacket.header.packetLen > RFM69_MAX_PACKET_LEN) { - RFM69.currentPacket.header.packetLen = RFM69_MAX_PACKET_LEN; - } + if (RFM69.currentPacket.header.packetLen > RFM69_MAX_PACKET_LEN) { + RFM69.currentPacket.header.packetLen = RFM69_MAX_PACKET_LEN; + } - data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; - //SPI.transfern(data, RFM69.currentPacket.header.packetLen - 1); //TODO: Wrong packetLen? - RFM69_SPI.transfern(data, RFM69.currentPacket.header.packetLen); + data[0] = RFM69_REG_FIFO & RFM69_READ_REGISTER; + //SPI.transfern(data, RFM69.currentPacket.header.packetLen - 1); //TODO: Wrong packetLen? + RFM69_SPI.transfern(data, RFM69.currentPacket.header.packetLen); - //(void)memcpy((void *)&RFM69.currentPacket.data[2], (void *)&data[1], RFM69.currentPacket.header.packetLen - 2); //TODO: Wrong packetLen? - (void)memcpy((void *)&RFM69.currentPacket.data[2], (void *)&data[1], - RFM69.currentPacket.header.packetLen - 1); + //(void)memcpy((void *)&RFM69.currentPacket.data[2], (void *)&data[1], RFM69.currentPacket.header.packetLen - 2); //TODO: Wrong packetLen? + (void)memcpy((void *)&RFM69.currentPacket.data[2], (void *)&data[1], + RFM69.currentPacket.header.packetLen - 1); + if (RFM69.currentPacket.header.version >= RFM69_MIN_PACKET_HEADER_VERSION) { + RFM69.currentPacket.payloadLen = min(RFM69.currentPacket.header.packetLen - (RFM69_HEADER_LEN - 1), + RFM69_MAX_PACKET_LEN); + RFM69.ackReceived = RFM69_getACKReceived(RFM69.currentPacket.header.controlFlags); + RFM69.dataReceived = !RFM69.ackReceived; + } +#else + (void)RFM69_SPI.transfer(RFM69_REG_FIFO & RFM69_READ_REGISTER); + // set reading pointer + uint8_t *current = (uint8_t *)&RFM69.currentPacket; + bool headerRead = false; + // first read header + uint8_t readingLength = RFM69_HEADER_LEN; + while (readingLength--) { + *current++ = RFM69_SPI.transfer((uint8_t)RFM69_NOP); + if (!readingLength && !headerRead) { + // header read + headerRead = true; if (RFM69.currentPacket.header.version >= RFM69_MIN_PACKET_HEADER_VERSION) { - RFM69.currentPacket.payloadLen = min(RFM69.currentPacket.header.packetLen - (RFM69_HEADER_LEN - 1), - RFM69_MAX_PACKET_LEN); + // read payload + readingLength = min(RFM69.currentPacket.header.packetLen - (sizeof(rfm69_header_t) - + 1), /// double check this! + RFM69_MAX_PACKET_LEN); // adjust to max payload buffer size !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // save payload length + RFM69.currentPacket.payloadLen = readingLength; RFM69.ackReceived = RFM69_getACKReceived(RFM69.currentPacket.header.controlFlags); RFM69.dataReceived = !RFM69.ackReceived; } -#else - (void)RFM69_SPI.transfer(RFM69_REG_FIFO & RFM69_READ_REGISTER); - // set reading pointer - uint8_t *current = (uint8_t *)&RFM69.currentPacket; - bool headerRead = false; - // first read header - uint8_t readingLength = RFM69_HEADER_LEN; - while (readingLength--) { - *current++ = RFM69_SPI.transfer((uint8_t)RFM69_NOP); - if (!readingLength && !headerRead) { - // header read - headerRead = true; - if (RFM69.currentPacket.header.version >= RFM69_MIN_PACKET_HEADER_VERSION) { - // read payload - readingLength = min(RFM69.currentPacket.header.packetLen - (RFM69_HEADER_LEN - 1), - RFM69_MAX_PACKET_LEN); - // save payload length - RFM69.currentPacket.payloadLen = readingLength; - RFM69.ackReceived = RFM69_getACKReceived(RFM69.currentPacket.header.controlFlags); - RFM69.dataReceived = !RFM69.ackReceived; - } - } - } + } + } +#endif + RFM69_concludeSPITransaction(); + if (RFM69_getACKRequested(RFM69.currentPacket.header.controlFlags)) { +#if (F_CPU > 16*1000000ul) + // delay for fast nodes + delay(5); // checked on SDR with 8Mhz AVR node + debug enabled #endif - RFM69_csn(HIGH); - RFM69_concludeSPITransaction(); + RFM69_sendACK(RFM69.currentPacket.header.sender, RFM69.currentPacket.header.sequenceNumber, + RFM69.currentPacket.RSSI); + // radio in RX + }; + // primitive deduplication + if (RFM69.dataReceived) { + if (RFM69.currentPacket.header.sender == RFM69.lastPacket.sender && + RFM69.currentPacket.header.sequenceNumber == RFM69.lastPacket.sequenceNumber) { + // same packet received + RFM69.dataReceived = false; + RFM69_DEBUG(PSTR("!RFM69:PKT DD\n")); // packet de-duplication + } else { + // new packet received + RFM69_DEBUG(PSTR("RFM69:NEW PKT\n")); + RFM69.lastPacket.sender = RFM69.currentPacket.header.sender; + RFM69.lastPacket.sequenceNumber = RFM69.currentPacket.header.sequenceNumber; } - RFM69.currentPacket.RSSI = RFM69_readRSSI(); - // radio remains in stdby until packet read - } else { - // back to RX - (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); } + // radio remains in stdby until packet read + // ==> process the packet, i.e. fill to queue if not ack ****************************************************************************** + } -LOCAL void RFM69_handler(void) +LOCAL void RFM69_handling(void) { + //RFM69_DEBUG(PSTR("RFM69:IRQ1=%" PRIu8 ", IRQ2=%" PRIu8 ", IRQFlag=%" PRIu8 "\n"), RFM69_readReg(RFM69_REG_IRQFLAGS1), RFM69_readReg(RFM69_REG_IRQFLAGS2), RFM69_irq); if (RFM69_irq) { - // radio is in STDBY + RFM69.currentPacket.RSSI = RFM69_readRSSI(); // clear flag, 8bit - no need for critical section - RFM69_irq = false; - RFM69_interruptHandling(); + //RFM69_irq = false; ==> flag cleared when transition to RX or TX + RFM69_DEBUG(PSTR("RFM69:IRQ\n")); + // radio: RX + const uint8_t regIrqFlags2 = RFM69_readReg(RFM69_REG_IRQFLAGS2); + if (regIrqFlags2 & RFM69_IRQFLAGS2_PAYLOADREADY) { + (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); + RFM69_RXFIFOHandling(); + } else { + RFM69_DEBUG(PSTR("!RFM69:IRQ NH, IRQ2=% " PRIu8 "\n"), regIrqFlags2); // not handled IRQ + } } } @@ -333,36 +354,22 @@ LOCAL bool RFM69_available(void) if (RFM69.dataReceived) { // data received - we are still in STDBY return true; - } else if (RFM69.radioMode == RFM69_RADIO_MODE_TX) { - // still in TX - return false; - } else if (RFM69.radioMode != RFM69_RADIO_MODE_RX) { // adding this check speeds up loop() :) + } else if (RFM69.radioMode != RFM69_RADIO_MODE_RX) { // no data received and not in RX (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); } return false; } -LOCAL uint8_t RFM69_receive(uint8_t *buf, const uint8_t maxBufSize) +LOCAL uint8_t RFM69_receive(uint8_t *buf, const uint8_t maxBufSize/*, bool *encryptedMessage*/) { - const uint8_t payloadLen = min(RFM69.currentPacket.payloadLen, maxBufSize); - const uint8_t sender = RFM69.currentPacket.header.sender; - const rfm69_sequenceNumber_t sequenceNumber = RFM69.currentPacket.header.sequenceNumber; - const uint8_t controlFlags = RFM69.currentPacket.header.controlFlags; - const rfm69_RSSI_t RSSI = RFM69.currentPacket.RSSI; - - if (buf != NULL) { - (void)memcpy((void *)buf, (void *)&RFM69.currentPacket.payload, payloadLen); + if (buf == NULL) { + return 0; } // clear data flag RFM69.dataReceived = false; - if (RFM69_getACKRequested(controlFlags) && !RFM69_getACKReceived(controlFlags)) { -#if defined(MY_GATEWAY_FEATURE) && (F_CPU>16*1000000ul) - // delay for fast GW and slow nodes - delay(50); -#endif - RFM69_sendACK(sender, sequenceNumber, RSSI); - } + const uint8_t payloadLen = min(RFM69.currentPacket.payloadLen, maxBufSize); + (void)memcpy((void *)buf, (const void *)&RFM69.currentPacket.payload, payloadLen); return payloadLen; } @@ -374,58 +381,59 @@ LOCAL bool RFM69_channelFree(void) return (RSSI > RFM69_RSSItoInternal(MY_RFM69_CSMA_LIMIT_DBM)); } -LOCAL bool RFM69_sendFrame(rfm69_packet_t *packet, const bool increaseSequenceCounter) +LOCAL bool RFM69_send(const rfm69_packet_t *packet) { - // ensure we are in RX for correct RSSI sampling, dirty hack to enforce rx restart :) - RFM69.radioMode = RFM69_RADIO_MODE_STDBY; + RFM69_DEBUG(PSTR("RFM69:SND:LEN=%" PRIu8 "\n"), packet->header.packetLen + 1); +#if defined(MY_DEBUG_VERBOSE_RFM69) + hwDebugBuf2Str((const uint8_t *)&packet->data, packet->header.packetLen + 1); + RFM69_DEBUG(PSTR("RFM69:SND:RAW=%s\n"), hwDebugPrintStr); +#endif + // radio in RX (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); - delay(1); // timing for correct RSSI sampling + delay(1); // timing for correct RSSI sampling, see datasheet page 37 const uint32_t CSMA_START_MS = hwMillis(); - while (!RFM69_channelFree() && - ((hwMillis() - CSMA_START_MS) < MY_RFM69_CSMA_TIMEOUT_MS)) { + bool channelFree; + do { + channelFree = RFM69_channelFree(); doYield(); + } while (!channelFree && (hwMillis() - CSMA_START_MS < MY_RFM69_CSMA_TIMEOUT_MS)); + if (!channelFree) { + return false; } - // set radio to standby to load fifo - (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); - if (increaseSequenceCounter) { - // increase sequence counter, overflow is ok - RFM69.txSequenceNumber++; + /* + // check if incoming message + const uint8_t regIrqFlags2 = RFM69_readReg(RFM69_REG_IRQFLAGS2); + if ((regIrqFlags2 & RFM69_IRQFLAGS2_PAYLOADREADY) || (regIrqFlags2 & RFM69_IRQFLAGS2_FIFOLEVEL)) { + const uint32_t RX_EXTENED_MS = hwMillis(); + RFM69_DEBUG(PSTR("RFM69:SND:MSG RX\n")); + while (!RFM69_irq && ((hwMillis() - RX_EXTENED_MS) < 2000)) { + doYield(); + } + RFM69_handling(); } - // clear FIFO and flags - RFM69_clearFIFO(); - // assign sequence number - packet->header.sequenceNumber = RFM69.txSequenceNumber; - - // write packet - const uint8_t finalLen = packet->payloadLen + RFM69_HEADER_LEN; // including length byte - (void)RFM69_burstWriteReg(RFM69_REG_FIFO, packet->data, finalLen); + */ - // send message + // radio is in RX: RX->TX switch clears FIFO (void)RFM69_setRadioMode(RFM69_RADIO_MODE_TX); // irq upon txsent + // write packet to FIFO + (void)RFM69_burstWriteReg(RFM69_REG_FIFO, packet->data, packet->header.packetLen + 1); const uint32_t txStartMS = hwMillis(); + // send message while (!RFM69_irq && (hwMillis() - txStartMS < MY_RFM69_TX_TIMEOUT_MS)) { doYield(); }; - return RFM69_irq; -} - -LOCAL bool RFM69_send(const uint8_t recipient, uint8_t *data, const uint8_t len, - const rfm69_controlFlags_t flags, const bool increaseSequenceCounter) -{ - // assemble packet - rfm69_packet_t packet; - packet.header.version = RFM69_PACKET_HEADER_VERSION; - packet.header.sender = RFM69.address; - packet.header.recipient = recipient; - packet.payloadLen = min(len, (uint8_t)RFM69_MAX_PAYLOAD_LEN); - packet.header.controlFlags = flags; - (void)memcpy((void *)&packet.payload, (void *)data, packet.payloadLen); // copy payload - packet.header.packetLen = packet.payloadLen + (RFM69_HEADER_LEN - 1); // -1 length byte - return RFM69_sendFrame(&packet, increaseSequenceCounter); + // reset IRQ + //RFM69_irq = false; + // read IRQ register + //const bool result = RFM69_readReg(RFM69_REG_IRQFLAGS2) & RFM69_IRQFLAGS2_PACKETSENT; + // immediately switch to RX to prevent extended carrier signal + (void)RFM69_setRadioMode(RFM69_RADIO_MODE_RX); + return true; } LOCAL void RFM69_setFrequency(const uint32_t frequencyHz) { + RFM69_DEBUG(PSTR("RFM69:INIT:FREQ=%" PRIu32 "\n"), frequencyHz); const uint32_t freqHz = (uint32_t)(frequencyHz / RFM69_FSTEP); RFM69_writeReg(RFM69_REG_FRFMSB, (uint8_t)((freqHz >> 16) & 0xFF)); RFM69_writeReg(RFM69_REG_FRFMID, (uint8_t)((freqHz >> 8) & 0xFF)); @@ -435,7 +443,7 @@ LOCAL void RFM69_setFrequency(const uint32_t frequencyHz) LOCAL void RFM69_setHighPowerRegs(const bool onOff) { #if defined(RFM69_VERSION_HW) - RFM69_writeReg(RFM69_REG_OCP, (onOff ? RFM69_OCP_OFF : RFM69_OCP_ON ) | RFM69_OCP_TRIM_95); + RFM69_writeReg(RFM69_REG_OCP, (onOff ? RFM69_OCP_OFF : RFM69_OCP_ON) | RFM69_OCP_TRIM_95); RFM69_writeReg(RFM69_REG_TESTPA1, onOff ? 0x5D : 0x55); RFM69_writeReg(RFM69_REG_TESTPA2, onOff ? 0x7C : 0x70); #else @@ -473,7 +481,7 @@ LOCAL bool RFM69_setTxPowerLevel(rfm69_powerlevel_t newPowerLevel) } #endif RFM69_writeReg(RFM69_REG_PALEVEL, palevel); - RFM69_DEBUG(PSTR("RFM69:PTX:LEVEL=%" PRIi8 " dBm\n"),newPowerLevel); + RFM69_DEBUG(PSTR("RFM69:PTX:LEVEL=%" PRIi8 " dBm\n"), newPowerLevel); return true; } @@ -491,20 +499,45 @@ LOCAL uint8_t RFM69_getAddress(void) LOCAL bool RFM69_setRadioMode(const rfm69_radio_mode_t newRadioMode) { - if (RFM69.radioMode == newRadioMode) { - // no change - return false; - } - - uint8_t regMode; + // FIFO status when switching radio modes: + // STD -> SLP : not cleared + // SLP -> STD : not cleared + // STD/SLP -> TX : not cleared + // STD/SLP -> RX : cleared + // RX -> TX : cleared + // RX -> STD/SLP : not cleared + // TX -> ANY : cleared + /* + // are we currently in RX, FIFO not empty and no payload ready? + if (RFM69.radioMode == RFM69_RADIO_MODE_RX) { + // FIFO not empty and payload not ready + const uint32_t enter_ms = hwMillis(); + uint8_t FIFOstatus; + do { + FIFOstatus = RFM69_readReg(RFM69_REG_IRQFLAGS2); + doYield(); + } while (!(FIFOstatus & RFM69_IRQFLAGS2_PAYLOADREADY) && + (FIFOstatus & RFM69_IRQFLAGS2_FIFONOTEMPTY) && ((hwMillis() - enter_ms) < 3000)); + // if message received, handler will switch to standby + if (FIFOstatus & RFM69_IRQFLAGS2_PAYLOADREADY) { + RFM69_DEBUG(PSTR("RFM69:SRM:MSG AVAIL\n")); + // set radio to standby + RFM69_writeReg(RFM69_REG_OPMODE, RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY); + //(void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); no recursive calls! + RFM69_interruptHandling(); + RFM69_irq = false; + } + } + */ + uint8_t regMode = RFM69_RADIO_MODE_STDBY; + RFM69.radioMode = newRadioMode; + RFM69_irq = false; if (newRadioMode == RFM69_RADIO_MODE_STDBY) { regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY; } else if (newRadioMode == RFM69_RADIO_MODE_SLEEP) { regMode = RFM69_OPMODE_SEQUENCER_OFF | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_SLEEP; } else if (newRadioMode == RFM69_RADIO_MODE_RX) { - RFM69.dataReceived = false; - RFM69.ackReceived = false; regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_RECEIVER; RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01); // Interrupt on PayloadReady, DIO0 // disable high power settings @@ -515,10 +548,6 @@ LOCAL bool RFM69_setRadioMode(const rfm69_radio_mode_t newRadioMode) regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_TRANSMITTER; RFM69_writeReg(RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_00); // Interrupt on PacketSent, DIO0 RFM69_setHighPowerRegs(RFM69.powerLevel >= (rfm69_powerlevel_t)RFM69_HIGH_POWER_DBM); - } else if (newRadioMode == RFM69_RADIO_MODE_SYNTH) { - regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_SYNTHESIZER; - } else { - regMode = RFM69_OPMODE_SEQUENCER_ON | RFM69_OPMODE_LISTEN_OFF | RFM69_OPMODE_STANDBY; } // set new mode @@ -531,7 +560,7 @@ LOCAL bool RFM69_setRadioMode(const rfm69_radio_mode_t newRadioMode) return false; } } - RFM69.radioMode = newRadioMode; + RFM69_DEBUG(PSTR("RFM69:SRM:MODE=%" PRIu8 "\n"), newRadioMode); return true; } @@ -568,15 +597,21 @@ LOCAL bool RFM69_standBy(void) LOCAL void RFM69_sendACK(const uint8_t recipient, const rfm69_sequenceNumber_t sequenceNumber, const rfm69_RSSI_t RSSI) { - RFM69_DEBUG(PSTR("RFM69:SAC:SEND ACK,TO=%" PRIu8 ",RSSI=%" PRIi16 "\n"),recipient, + RFM69_DEBUG(PSTR("RFM69:SAC:TO=%" PRIu8 ",SEQ=%" PRIu8 ",RSSI=%" PRIi16 "\n"),recipient, + sequenceNumber, RFM69_internalToRSSI(RSSI)); - rfm69_ack_t ACK; - ACK.sequenceNumber = sequenceNumber; - ACK.RSSI = RSSI; - rfm69_controlFlags_t flags = 0u; // reset flags - RFM69_setACKReceived(flags, true); - RFM69_setACKRSSIReport(flags, true); - (void)RFM69_send(recipient, (uint8_t *)&ACK, sizeof(rfm69_ack_t), flags); + rfm69_packet_t packet; + packet.ACK.RSSI = RSSI; + packet.ACK.sequenceNumber = sequenceNumber; + packet.header.version = RFM69_PACKET_HEADER_VERSION; + packet.header.sender = RFM69.address; + packet.header.recipient = recipient; + packet.header.packetLen = sizeof(rfm69_ack_t) + (sizeof(rfm69_header_t) - 1); + packet.header.sequenceNumber = ++RFM69.txSequenceNumber; + packet.header.controlFlags = 0; + RFM69_setACKReceived(packet.header.controlFlags, true); + RFM69_setACKRSSIReport(packet.header.controlFlags, true); + (void)RFM69_send(&packet); } LOCAL bool RFM69_executeATC(const rfm69_RSSI_t currentRSSI, const rfm69_RSSI_t targetRSSI) @@ -611,21 +646,40 @@ LOCAL void RFM69_ATCmode(const bool onOff, const int16_t targetRSSI) LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void *buffer, const uint8_t bufferSize, const bool noACK) { - for (uint8_t retry = 0; retry < RFM69_RETRIES; retry++) { - RFM69_DEBUG(PSTR("RFM69:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RETRY=%" PRIu8 "\n"), recipient, - RFM69.txSequenceNumber,retry); - rfm69_controlFlags_t flags = 0u; // reset all flags - RFM69_setACKRequested(flags, !noACK); - RFM69_setACKRSSIReport(flags, RFM69.ATCenabled); - (void)RFM69_send(recipient, (uint8_t *)buffer, bufferSize, flags, !retry); + rfm69_packet_t packet; + packet.header.version = RFM69_PACKET_HEADER_VERSION; + packet.header.sender = RFM69.address; + packet.header.recipient = recipient; + packet.header.sequenceNumber = + ++RFM69.txSequenceNumber; // increase sequence counter, overflow is ok + packet.header.controlFlags = 0; // reset all flags + RFM69_setACKRequested(packet.header.controlFlags, !noACK); + RFM69_setACKRSSIReport(packet.header.controlFlags, RFM69.ATCenabled); + (void)memcpy((void *)packet.payload, buffer, bufferSize); + packet.payloadLen = bufferSize; + + packet.header.packetLen = packet.payloadLen + (sizeof(rfm69_header_t) - 1); // -1 length byte + + for (uint8_t TXAttempt = 0; TXAttempt < RFM69_TX_ATTEMPTS; TXAttempt++) { + RFM69_DEBUG(PSTR("RFM69:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu8 ",TX=%" PRIu8 "\n"), recipient, + RFM69.txSequenceNumber, TXAttempt + 1); + + if (!RFM69_send(&packet)) { + // CSMA check fails + continue; + } + // radio is in RX if (noACK) { // no ACK requested return true; } - // radio is in RX const uint32_t enterMS = hwMillis(); - while (hwMillis() - enterMS < RFM69_RETRY_TIMEOUT_MS && !RFM69.dataReceived) { - RFM69_handler(); + // progressive wait time with random component + const uint32_t effectiveWaitingTimeMS = RFM69_RETRY_TIMEOUT_MS + (hwMillis() & 0x3Fu) + + (TXAttempt * 50u); + RFM69_DEBUG(PSTR("RFM69:SWR:ACK WAIT=%" PRIu32 "\n"), effectiveWaitingTimeMS); + while (!RFM69.dataReceived && (hwMillis() - enterMS < effectiveWaitingTimeMS)) { + RFM69_handling(); if (RFM69.ackReceived) { // radio is in stdby const uint8_t ACKsender = RFM69.currentPacket.header.sender; @@ -647,8 +701,17 @@ LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void *buffer, return true; } // seq check } + doYield(); + } + if (RFM69.dataReceived) { + return false; + } + RFM69_DEBUG(PSTR("!RFM69:SWR:NACK,SEQ=%" PRIu8 "\n"), RFM69.txSequenceNumber); + const uint32_t enterCSMAMS = hwMillis(); + const uint16_t randDelayCSMA = enterMS % 100; + while (hwMillis() - enterCSMAMS < randDelayCSMA) { + doYield(); } - RFM69_DEBUG(PSTR("!RFM69:SWR:NACK\n")); } return false; } @@ -721,6 +784,8 @@ LOCAL void RFM69_setConfiguration(void) { RFM69_REG_RXBW, rfm69_modem_config[5] }, { RFM69_REG_AFCBW, rfm69_modem_config[5] }, // same as rxbw, experimental, based on datasheet //{ RFM69_REG_DIOMAPPING1, RFM69_DIOMAPPING1_DIO0_01 }, + // enabling AFC may lock up the receiver due to freq. glitches - only enable together with timeout IRQ + { RFM69_REG_AFCFEI, RFM69_AFCFEI_AFCAUTOCLEAR_OFF | RFM69_AFCFEI_AFCAUTO_OFF }, { RFM69_REG_DIOMAPPING2, RFM69_DIOMAPPING2_CLKOUT_OFF }, { RFM69_REG_IRQFLAGS2, RFM69_IRQFLAGS2_FIFOOVERRUN }, // clear FIFO and flags { RFM69_REG_RSSITHRESH, RFM69_RSSITHRESH_VALUE }, @@ -730,7 +795,7 @@ LOCAL void RFM69_setConfiguration(void) { RFM69_REG_SYNCVALUE1, RFM69_SYNCVALUE1 }, { RFM69_REG_SYNCVALUE2, MY_RFM69_NETWORKID }, { RFM69_REG_PACKETCONFIG1, rfm69_modem_config[6] }, - { RFM69_REG_PAYLOADLENGTH, RFM69_MAX_PACKET_LEN }, // in variable length mode: the max frame size, not used in TX + { RFM69_REG_PAYLOADLENGTH, RFM69_FIFO_SIZE }, // set to max of 66 bytes { RFM69_REG_NODEADRS, RFM69_BROADCAST_ADDRESS }, // init { RFM69_REG_BROADCASTADRS, RFM69_BROADCAST_ADDRESS }, { RFM69_REG_FIFOTHRESH, RFM69_FIFOTHRESH_TXSTART_FIFOTHRESH | (RFM69_HEADER_LEN - 1) }, // start transmitting when rfm69 header loaded, fifo level irq when header bytes received (irq asserted when n bytes exceeded) @@ -751,7 +816,7 @@ LOCAL bool RFM69_isModeReady(void) return (bool)timeout; } -LOCAL void RFM69_encrypt(const char *key) +LOCAL void RFM69_HWencryption(const char *key) { (void)RFM69_setRadioMode(RFM69_RADIO_MODE_STDBY); if (key != NULL) { diff --git a/hal/transport/RFM69/driver/new/RFM69_new.h b/hal/transport/RFM69/driver/new/RFM69_new.h index 8e2613466..c42134990 100644 --- a/hal/transport/RFM69/driver/new/RFM69_new.h +++ b/hal/transport/RFM69/driver/new/RFM69_new.h @@ -49,9 +49,10 @@ * | | RFM69 | INIT | PIN,CS=%%d,IQP=%%d,IQN=%%d[,RST=%%d] | Pin configuration: chip select (CS), IRQ pin (IQP), IRQ number (IQN), Reset (RST) * | | RFM69 | INIT | HWV=%%d | HW version, see datasheet chapter 9 * |!| RFM69 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module +* |!| RFM69 | SRM | MODE=%%d | Set radio mode, see #rfm69_radio_mode_t * | | RFM69 | PTX | NO ADJ | TX power level, no adjustment * | | RFM69 | PTX | LEVEL=%%d dbM | TX power level, set to (LEVEL) dBm -* | | RFM69 | SAC | SEND ACK,TO=%%d,RSSI=%%d | ACK sent to (TO), RSSI of incoming message (RSSI) +* | | RFM69 | SAC | TO=%%d,RSSI=%%d | Send ACK to (TO), RSSI of incoming message (RSSI) * | | RFM69 | ATC | ADJ TXL,cR=%%d,tR=%%d..%%d,TXL=%%d | Adjust TX level, current RSSI (cR), target RSSI range (tR), TX level (TXL) * | | RFM69 | SWR | SEND,TO=%%d,SEQ=%%d,RETRY=%%d | Send to (TO), sequence number (SWQ), retry if no ACK received (RETRY) * | | RFM69 | SWR | ACK,FROM=%%d,SEQ=%%d,RSSI=%%d | ACK received from (FROM), sequence nr (SEQ), ACK RSSI (RSSI) @@ -126,13 +127,14 @@ #endif #endif -#define RFM69_FIFO_SIZE (0xFFu) //!< Max number of bytes the Rx/Tx FIFO can hold -#define RFM69_MAX_PACKET_LEN (0x40u) //!< This is the maximum number of bytes that can be carried +#define RFM69_FIFO_SIZE (0x42u) //!< Max number of bytes the Rx/Tx FIFO can hold +#define RFM69_MAX_PACKET_LEN (RFM69_FIFO_SIZE) //!< This is the maximum number of bytes that can be carried + #define RFM69_ATC_TARGET_RANGE_DBM (2u) //!< ATC target range +/- dBm #define RFM69_PACKET_HEADER_VERSION (1u) //!< RFM69 packet header version #define RFM69_MIN_PACKET_HEADER_VERSION (1u) //!< Minimal RFM69 packet header version -#define RFM69_RETRIES (5u) //!< Retries in case of failed transmission +#define RFM69_TX_ATTEMPTS (5u) //!< TX attempts #define RFM69_RETRY_TIMEOUT_MS (200ul) //!< Timeout for ACK, adjustments needed if modem configuration changed (air time different) #define RFM69_MODE_READY_TIMEOUT_MS (50ul) //!< Timeout for mode ready @@ -140,6 +142,11 @@ #define RFM69_ACK_RECEIVED (6u) //!< RFM69 header, controlFlag, bit 6 #define RFM69_ACK_RSSI_REPORT (5u) //!< RFM69 header, controlFlag, bit 5 +#define RFM69_IV_SIZE_SHORT (8u) //!< RFM69_IV_SIZE_SHORT +#define RFM69_HMAC_SIZE_SHORT (20u) //!< RFM69_HMAC_SIZE_SHORT +#define RFM69_IV_SIZE_LONG (16u) //!< RFM69_IV_SIZE_LONG +#define RFM69_HMAC_SIZE_LONG (28u) //!< RFM69_HMAC_SIZE_LONG + #define RFM69_BROADCAST_ADDRESS (255u) //!< Broadcasting address #define RFM69_TARGET_RSSI_DBM (-75) //!< RSSI target #define RFM69_HIGH_POWER_DBM (18u) //!< High power threshold, dBm @@ -170,12 +177,12 @@ #define RFM69_FSTEP (RFM69_FXOSC / 524288.0f) //!< FXOSC / 2^19 = 32MHz / 2^19 (p13 in datasheet) // helper macros -#define RFM69_getACKRequested(__value) ((bool)bitRead(__value,RFM69_ACK_REQUESTED)) //!< getACKRequested -#define RFM69_setACKRequested(__value, __flag) bitWrite(__value,RFM69_ACK_REQUESTED,__flag) //!< setACKRequested -#define RFM69_getACKReceived(__value) ((bool)bitRead(__value,RFM69_ACK_RECEIVED)) //!< getACKReceived -#define RFM69_setACKReceived(__value, __flag) bitWrite(__value,RFM69_ACK_RECEIVED,__flag) //!< setACKReceived -#define RFM69_setACKRSSIReport(__value, __flag) bitWrite(__value,RFM69_ACK_RSSI_REPORT,__flag)//!< setACKRSSIReport -#define RFM69_getACKRSSIReport(__value) ((bool)bitRead(__value,RFM69_ACK_RSSI_REPORT)) //!< getACKRSSIReport +#define RFM69_getACKRequested(__value) ((bool)bitRead(__value, RFM69_ACK_REQUESTED)) //!< getACKRequested +#define RFM69_setACKRequested(__value, __flag) bitWrite(__value, RFM69_ACK_REQUESTED, __flag) //!< setACKRequested +#define RFM69_getACKReceived(__value) ((bool)bitRead(__value, RFM69_ACK_RECEIVED)) //!< getACKReceived +#define RFM69_setACKReceived(__value, __flag) bitWrite(__value, RFM69_ACK_RECEIVED, __flag) //!< setACKReceived +#define RFM69_setACKRSSIReport(__value, __flag) bitWrite(__value, RFM69_ACK_RSSI_REPORT, __flag)//!< setACKRSSIReport +#define RFM69_getACKRSSIReport(__value) ((bool)bitRead(__value, RFM69_ACK_RSSI_REPORT)) //!< getACKRSSIReport // Register access #define RFM69_READ_REGISTER (0x7Fu) //!< reading register @@ -312,6 +319,14 @@ typedef struct { rfm69_RSSI_t RSSI; //!< RSSI of current packet, RSSI = value - 137 } __attribute__((packed)) rfm69_packet_t; +/** +* @brief RFM69 last packet info +*/ +typedef struct { + rfm69_sequenceNumber_t sequenceNumber; //!< sequenceNumber + uint8_t sender; //!< sender +} rfm69_last_packet_t; + /** * @brief RFM69 internal variables */ @@ -321,25 +336,23 @@ typedef struct { rfm69_sequenceNumber_t txSequenceNumber; //!< RFM69_txSequenceNumber rfm69_powerlevel_t powerLevel; //!< TX power level dBm uint8_t ATCtargetRSSI; //!< ATC: target RSSI + rfm69_last_packet_t lastPacket; //!< Variable for deduplication mechanism // 8 bit rfm69_radio_mode_t radioMode : 3; //!< current transceiver state bool dataReceived : 1; //!< data received bool ackReceived : 1; //!< ACK received bool ATCenabled : 1; //!< ATC enabled - uint8_t reserved : 2; //!< Reserved +bool msgOK : + 1; //!< Flag if received message is ok or failed verification + uint8_t reserved : 1; //!< Reserved } rfm69_internal_t; #define LOCAL static //!< static /** -* @brief RFM69_handler +* @brief RFM69_handling */ -LOCAL void RFM69_handler(void); - -/** -* @brief Clear flags and FIFO -*/ -LOCAL void RFM69_clearFIFO(void); +LOCAL void RFM69_handling(void); /** * @brief Check for channel activity @@ -350,7 +363,7 @@ LOCAL bool RFM69_channelFree(void); /** * @brief RFM69_interruptHandling */ -LOCAL void RFM69_interruptHandling(void); +LOCAL void RFM69_RXFIFOHandling(void); /** * @brief Initialise the driver transport hardware and software @@ -386,24 +399,11 @@ LOCAL bool RFM69_available(void); LOCAL uint8_t RFM69_receive(uint8_t *buf, const uint8_t maxBufSize); /** -* @brief RFM69_sendFrame +* @brief RFM69_send * @param packet -* @param increaseSequenceCounter * @return True if packet sent */ -LOCAL bool RFM69_sendFrame(rfm69_packet_t *packet, const bool increaseSequenceCounter = true); - -/** -* @brief RFM69_send -* @param recipient -* @param data -* @param len -* @param flags -* @param increaseSequenceCounter -* @return True if frame sent -*/ -LOCAL bool RFM69_send(const uint8_t recipient, uint8_t *data, const uint8_t len, - const rfm69_controlFlags_t flags, const bool increaseSequenceCounter = true); +LOCAL bool RFM69_send(const rfm69_packet_t *packet); /** * @brief Sets the transmitter and receiver center frequency @@ -456,7 +456,7 @@ LOCAL void RFM69_powerUp(void); * @brief RFM69_sendACK * @param recipient * @param sequenceNumber -* @param RSSI (rfm95_RSSI_t) +* @param RSSI (rfm69_RSSI_t) */ LOCAL void RFM69_sendACK(const uint8_t recipient, const rfm69_sequenceNumber_t sequenceNumber, const rfm69_RSSI_t RSSI); @@ -470,8 +470,7 @@ LOCAL void RFM69_sendACK(const uint8_t recipient, const rfm69_sequenceNumber_t s * @return True if packet successfully sent */ LOCAL bool RFM69_sendWithRetry(const uint8_t recipient, const void *buffer, - const uint8_t bufferSize, - const bool noACK); + const uint8_t bufferSize, const bool noACK); /** * @brief RFM69_setRadioMode @@ -527,7 +526,7 @@ LOCAL bool RFM69_sanityCheck(void); * @brief RFM69_encrypt Set encryption mode * @param key if key is null, encryption is disabled. Key has to be 16 bytes! */ -LOCAL void RFM69_encrypt(const char *key); +LOCAL void RFM69_HWencryption(const char *key) __attribute__((unused)); /** * @brief RFM69_setHighPowerRegs @@ -554,7 +553,7 @@ LOCAL void RFM69_ATCmode(const bool onOff, const int16_t targetRSSI = RFM69_TARG * Read and display all RFM69 register contents. * @note define RFM69_REGISTER_DETAIL for register content decoding. */ -LOCAL void RFM69_readAllRegs(void); +LOCAL void RFM69_readAllRegs(void) __attribute__((unused)); #endif diff --git a/hal/transport/RFM69/driver/old/RFM69_old.cpp b/hal/transport/RFM69/driver/old/RFM69_old.cpp index 9d840a3dd..814d281f4 100644 --- a/hal/transport/RFM69/driver/old/RFM69_old.cpp +++ b/hal/transport/RFM69/driver/old/RFM69_old.cpp @@ -505,7 +505,7 @@ bool RFM69::receiveDone() // To enable encryption: radio.encrypt("ABCDEFGHIJKLMNOP"); // To disable encryption: radio.encrypt(null) or radio.encrypt(0) // KEY HAS TO BE 16 bytes !!! -void RFM69::encrypt(const char* key) +void RFM69::encrypt(const uint8_t* key) { setMode(RFM69_MODE_STANDBY); if (key != 0) { diff --git a/hal/transport/RFM69/driver/old/RFM69_old.h b/hal/transport/RFM69/driver/old/RFM69_old.h index 4b3f95245..a8f50d3cd 100644 --- a/hal/transport/RFM69/driver/old/RFM69_old.h +++ b/hal/transport/RFM69/driver/old/RFM69_old.h @@ -179,7 +179,7 @@ class RFM69 virtual void sendACK(const void* buffer = "", uint8_t bufferSize=0); //!< sendACK uint32_t getFrequency(); //!< getFrequency void setFrequency(uint32_t freqHz); //!< setFrequency - void encrypt(const char* key); //!< encrypt + void encrypt(const uint8_t* key); //!< encrypt void setCS(uint8_t newSPISlaveSelect); //!< setCS int16_t readRSSI(bool forceTrigger=false); //!< readRSSI void promiscuous(bool onOff=true); //!< promiscuous diff --git a/hal/transport/RFM95/MyTransportRFM95.cpp b/hal/transport/RFM95/MyTransportRFM95.cpp index 8a03e84df..d5b8155cd 100644 --- a/hal/transport/RFM95/MyTransportRFM95.cpp +++ b/hal/transport/RFM95/MyTransportRFM95.cpp @@ -19,12 +19,9 @@ #include "hal/transport/RFM95/driver/RFM95.h" -bool transportInit(void) +bool RFM95_transportInit(void) { const bool result = RFM95_initialise(MY_RFM95_FREQUENCY); -#if defined(MY_RFM95_TCXO) - RFM95_enableTCXO(); -#endif #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RFM95_ATC_MODE_DISABLED) // only enable ATC mode in nodes RFM95_ATCmode(true, MY_RFM95_ATC_TARGET_RSSI); @@ -32,94 +29,109 @@ bool transportInit(void) return result; } -void transportSetAddress(const uint8_t address) +void RFM95_transportSetAddress(const uint8_t address) { RFM95_setAddress(address); } -uint8_t transportGetAddress(void) +uint8_t RFM95_transportGetAddress(void) { return RFM95_getAddress(); } -bool transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +bool RFM95_transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) { return RFM95_sendWithRetry(to, data, len, noACK); } -bool transportDataAvailable(void) +bool RFM95_transportDataAvailable(void) { - RFM95_handler(); + RFM95_handling(); return RFM95_available(); } -bool transportSanityCheck(void) +void RFM95_transportTask(void) +{ + RFM95_handling(); +#if defined(MY_TRANSPORT_RX_QUEUE) + if (RFM69_available()) { + RXQueuedMessage_t *msgIn = transportHALGetQueueBuffer(); + if (msgIn != NULL) { + msgIn->channel = TRANSPORT_RFM95_CHANNEL_ID; + msgIn->length = RFM69_receive((uint8_t *)&msgIn->data, MAX_MESSAGE_LENGTH); + (void)transportHALPushQueueBuffer(msgIn); + } + } +#endif +} + +bool RFM95_transportSanityCheck(void) { return RFM95_sanityCheck(); } -uint8_t transportReceive(void *data) +uint8_t RFM95_transportReceive(void *data, const uint8_t maxBufSize) { - uint8_t len = RFM95_receive((uint8_t *)data, MAX_MESSAGE_SIZE); + uint8_t len = RFM95_receive((uint8_t *)data, maxBufSize); return len; } -void transportSleep(void) +void RFM95_transportSleep(void) { (void)RFM95_sleep(); } -void transportStandBy(void) +void RFM95_transportStandBy(void) { (void)RFM95_standBy(); } -void transportPowerDown(void) +void RFM95_transportPowerDown(void) { RFM95_powerDown(); } -void transportPowerUp(void) +void RFM95_transportPowerUp(void) { RFM95_powerUp(); } -void transportToggleATCmode(const bool OnOff, const int16_t targetRSSI) +void RFM95_transportToggleATCmode(const bool OnOff, const int16_t targetRSSI) { RFM95_ATCmode(OnOff, targetRSSI); } -int16_t transportGetSendingRSSI(void) +int16_t RFM95_transportGetSendingRSSI(void) { return RFM95_getSendingRSSI(); } -int16_t transportGetReceivingRSSI(void) +int16_t RFM95_transportGetReceivingRSSI(void) { return RFM95_getReceivingRSSI(); } -int16_t transportGetSendingSNR(void) +int16_t RFM95_transportGetSendingSNR(void) { return RFM95_getSendingSNR(); } -int16_t transportGetReceivingSNR(void) +int16_t RFM95_transportGetReceivingSNR(void) { return RFM95_getReceivingSNR(); } -int16_t transportGetTxPowerPercent(void) +int16_t RFM95_transportGetTxPowerPercent(void) { return RFM95_getTxPowerPercent(); } -int16_t transportGetTxPowerLevel(void) +int16_t RFM95_transportGetTxPowerLevel(void) { return RFM95_getTxPowerLevel(); } -bool transportSetTxPowerPercent(const uint8_t powerPercent) +bool RFM95_transportSetTxPowerPercent(const uint8_t powerPercent) { return RFM95_setTxPowerPercent(powerPercent); } diff --git a/hal/transport/RFM95/MyTransportRFM95_RFM69.cpp b/hal/transport/RFM95/MyTransportRFM95_RFM69.cpp new file mode 100644 index 000000000..4f81f725a --- /dev/null +++ b/hal/transport/RFM95/MyTransportRFM95_RFM69.cpp @@ -0,0 +1,142 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "driver/RFM95_RFM69.h" + +bool RFM95_transportInit(void) +{ + const bool result = RFM95_initialise(MY_RFM95_FREQUENCY); +#if !defined(MY_GATEWAY_FEATURE) && !defined(MY_RFM95_ATC_MODE_DISABLED) + // only enable ATC mode in nodes + RFM95_ATCmode(true, MY_RFM95_ATC_TARGET_RSSI); +#endif + return result; +} + +void RFM95_transportSetAddress(const uint8_t address) +{ + RFM95_setAddress(address); +} + +uint8_t RFM95_transportGetAddress(void) +{ + return RFM95_getAddress(); +} + +bool RFM95_transportSend(const uint8_t to, const void *data, const uint8_t len, const bool noACK) +{ + return RFM95_sendWithRetry(to, data, len, noACK); +} + +bool RFM95_transportDataAvailable(void) +{ + return RFM95_available(); +} + +void RFM95_transportTask(void) +{ + RFM95_handling(); +#if defined(MY_TRANSPORT_RX_QUEUE) + if (RFM95_available()) { + RXQueuedMessage_t *msgIn = transportHALGetQueueBuffer(); + if (msgIn != NULL) { + msgIn->channel = TRANSPORT_RFM95_CHANNEL_ID; + msgIn->length = RFM95_receive((uint8_t *)&msgIn->data, MAX_MESSAGE_SIZE); + (void)transportHALPushQueueBuffer(msgIn); + } + } +#endif +} + +void RFM95_transportEncrypt(const char *key) +{ + // for RFM69 compatibility + RFM95_encrypt(key); +} + +bool RFM95_transportSanityCheck(void) +{ + return RFM95_sanityCheck(); +} + +uint8_t RFM95_transportReceive(void *data, const uint8_t maxBufSize) +{ + return RFM95_receive((uint8_t *)data, maxBufSize); +} + +void RFM95_transportSleep(void) +{ + (void)RFM95_sleep(); +} + +void RFM95_transportStandBy(void) +{ + (void)RFM95_standBy(); +} + +void RFM95_transportPowerDown(void) +{ + RFM95_powerDown(); +} + +void RFM95_transportPowerUp(void) +{ + RFM95_powerUp(); +} + +void RFM95_transportToggleATCmode(const bool OnOff, const int16_t targetRSSI) +{ + RFM95_ATCmode(OnOff, targetRSSI); +} + +int16_t RFM95_transportGetSendingRSSI(void) +{ + return RFM95_getSendingRSSI(); +} + +int16_t RFM95_transportGetReceivingRSSI(void) +{ + return RFM95_getReceivingRSSI(); +} + +int16_t RFM95_transportGetSendingSNR(void) +{ + return RFM95_getSendingSNR(); +} + +int16_t RFM95_transportGetReceivingSNR(void) +{ + return RFM95_getReceivingSNR(); +} + +int16_t RFM95_transportGetTxPowerPercent(void) +{ + return RFM95_getTxPowerPercent(); +} + +int16_t RFM95_transportGetTxPowerLevel(void) +{ + return RFM95_getTxPowerLevel(); +} + +bool RFM95_transportSetTxPowerPercent(const uint8_t powerPercent) +{ + return RFM95_setTxPowerPercent(powerPercent); +} + diff --git a/hal/transport/RFM95/driver/RFM95.cpp b/hal/transport/RFM95/driver/RFM95.cpp index f32017642..2744ed4b6 100644 --- a/hal/transport/RFM95/driver/RFM95.cpp +++ b/hal/transport/RFM95/driver/RFM95.cpp @@ -22,7 +22,7 @@ * RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti * * Definitions for HopeRF LoRa radios: - * http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf + * https://www.hoperf.com/data/upload/portal/20190611/RFM95W-V1.1.pdf * */ @@ -125,39 +125,38 @@ LOCAL uint8_t RFM95_RAW_writeByteRegister(const uint8_t address, uint8_t value) } // helper functions -LOCAL inline uint8_t RFM95_readReg(const uint8_t reg) +LOCAL uint8_t RFM95_readReg(const uint8_t reg) { return RFM95_RAW_readByteRegister(reg & RFM95_READ_REGISTER); } -LOCAL inline uint8_t RFM95_writeReg(const uint8_t reg, const uint8_t value) +LOCAL uint8_t RFM95_writeReg(const uint8_t reg, const uint8_t value) { return RFM95_RAW_writeByteRegister(reg | RFM95_WRITE_REGISTER, value); } -LOCAL inline uint8_t RFM95_burstReadReg(const uint8_t reg, void *buf, uint8_t len) +LOCAL uint8_t RFM95_burstReadReg(const uint8_t reg, void *buf, uint8_t len) { return RFM95_spiMultiByteTransfer(reg & RFM95_READ_REGISTER, (uint8_t *)buf, len, true); } -LOCAL inline uint8_t RFM95_burstWriteReg(const uint8_t reg, const void *buf, uint8_t len) +LOCAL uint8_t RFM95_burstWriteReg(const uint8_t reg, const void *buf, uint8_t len) { return RFM95_spiMultiByteTransfer(reg | RFM95_WRITE_REGISTER, (uint8_t *)buf, len, false); } -LOCAL inline rfm95_RSSI_t RFM95_RSSItoInternal(const int16_t externalRSSI) +LOCAL rfm95_RSSI_t RFM95_RSSItoInternal(const int16_t externalRSSI) { return static_cast(externalRSSI + RFM95_RSSI_OFFSET); } -LOCAL inline int16_t RFM95_internalToRSSI(const rfm95_RSSI_t internalRSSI) +LOCAL int16_t RFM95_internalToRSSI(const rfm95_RSSI_t internalRSSI) { return static_cast(internalRSSI - RFM95_RSSI_OFFSET); } LOCAL bool RFM95_initialise(const uint32_t frequencyHz) { - RFM95_DEBUG(PSTR("RFM95:INIT\n")); // power pin, if defined #if defined(MY_RFM95_POWER_PIN) hwPinMode(MY_RFM95_POWER_PIN, OUTPUT); @@ -198,16 +197,9 @@ LOCAL bool RFM95_initialise(const uint32_t frequencyHz) RFM95_SPI.begin(); // Set LoRa mode (during sleep mode) - (void)RFM95_writeReg(RFM95_REG_01_OP_MODE, RFM95_MODE_SLEEP | RFM95_LONG_RANGE_MODE); + (void)RFM95_writeReg(RFM95_REG_01_OP_MODE, RFM95_MODE_SLEEP | RFM95_LORA_MODE); delay(10); // Wait for sleep mode to take over - // TCXO init, if present -#if defined(MY_RFM95_TCXO) - RFM95_enableTCXO(); -#else - (void)RFM95_enableTCXO; -#endif - // Set up FIFO, 256 bytes: LoRa max message 64 bytes, set half RX half TX (default) (void)RFM95_writeReg(RFM95_REG_0F_FIFO_RX_BASE_ADDR, RFM95_RX_FIFO_ADDR); (void)RFM95_writeReg(RFM95_REG_0E_FIFO_TX_BASE_ADDR, RFM95_TX_FIFO_ADDR); @@ -219,6 +211,10 @@ LOCAL bool RFM95_initialise(const uint32_t frequencyHz) RFM95_setPreambleLength(RFM95_PREAMBLE_LENGTH); RFM95_setFrequency(frequencyHz); (void)RFM95_setTxPowerLevel(MY_RFM95_TX_POWER_DBM); + // TCXO init, if present +#if defined(MY_RFM95_TCXO) + RFM95_enableTCXO(); +#endif if (!RFM95_sanityCheck()) { // sanity check failed, check wiring or replace module @@ -244,18 +240,22 @@ LOCAL void RFM95_interruptHandling(void) { // read interrupt register const uint8_t irqFlags = RFM95_readReg(RFM95_REG_12_IRQ_FLAGS); - if (RFM95.radioMode == RFM95_RADIO_MODE_RX && (irqFlags & RFM95_RX_DONE)) { + if ((RFM95.radioMode == RFM95_RADIO_MODE_RX) && (irqFlags & RFM95_RX_DONE)) { // RXSingle mode: Radio goes automatically to STDBY after packet received (void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); - // Check CRC flag - if (!(irqFlags & RFM95_PAYLOAD_CRC_ERROR)) { + // Check CRC presence and flag + if (!(irqFlags & RFM95_PAYLOAD_CRC_ERROR) && + (RFM95_readReg(RFM95_REG_1C_HOP_CHANNEL) & RFM95_RX_PAYLOAD_CRC_IS_ON)) { const uint8_t bufLen = min(RFM95_readReg(RFM95_REG_13_RX_NB_BYTES), (uint8_t)RFM95_MAX_PACKET_LEN); if (bufLen >= RFM95_HEADER_LEN) { - // Reset the fifo read ptr to the beginning of the packet + // Reset the FIFO read ptr to the beginning of the packet (void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_readReg(RFM95_REG_10_FIFO_RX_CURRENT_ADDR)); + // read packet (void)RFM95_burstReadReg(RFM95_REG_00_FIFO, RFM95.currentPacket.data, bufLen); + // read RSSI RFM95.currentPacket.RSSI = static_cast(RFM95_readReg( - RFM95_REG_1A_PKT_RSSI_VALUE)); // RSSI of latest packet received + RFM95_REG_1A_PKT_RSSI_VALUE)); + // read SNR RFM95.currentPacket.SNR = static_cast(RFM95_readReg(RFM95_REG_19_PKT_SNR_VALUE)); RFM95.currentPacket.payloadLen = bufLen - RFM95_HEADER_LEN; if ((RFM95.currentPacket.header.version >= RFM95_MIN_PACKET_HEADER_VERSION) && @@ -270,12 +270,14 @@ LOCAL void RFM95_interruptHandling(void) } else { // CRC error RFM95_DEBUG(PSTR("!RFM95:IRH:CRC ERROR\n")); - // FIFO is cleared when switch from STDBY to RX or TX + // FIFO is cleared when switching from STDBY to RX or TX (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); } } else if (RFM95.radioMode == RFM95_RADIO_MODE_TX && (irqFlags & RFM95_TX_DONE) ) { + // packet sent, back to RX (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); } else if (RFM95.radioMode == RFM95_RADIO_MODE_CAD && (irqFlags & RFM95_CAD_DONE) ) { + // CAD done, back to STDBY RFM95.channelActive = irqFlags & RFM95_CAD_DETECTED; (void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); } @@ -283,7 +285,7 @@ LOCAL void RFM95_interruptHandling(void) RFM95_writeReg(RFM95_REG_12_IRQ_FLAGS, RFM95_CLEAR_IRQ); } -LOCAL void RFM95_handler(void) +LOCAL void RFM95_handling(void) { if (RFM95_irq) { RFM95_irq = false; @@ -329,17 +331,13 @@ LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize) return payloadLen; } -LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet, const bool increaseSequenceCounter) +LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet) { // Check channel activity if (!RFM95_waitCAD()) { return false; } // radio is in STDBY - if (increaseSequenceCounter) { - // increase sequence counter, overflow is ok - RFM95.txSequenceNumber++; - } packet->header.sequenceNumber = RFM95.txSequenceNumber; // Position at the beginning of the TX FIFO (void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_TX_FIFO_ADDR); @@ -360,7 +358,7 @@ LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet, const bool increaseSequenceCo } LOCAL bool RFM95_send(const uint8_t recipient, uint8_t *data, const uint8_t len, - const rfm95_controlFlags_t flags, const bool increaseSequenceCounter) + const rfm95_controlFlags_t flags) { rfm95_packet_t packet; packet.header.version = RFM95_PACKET_HEADER_VERSION; @@ -369,7 +367,7 @@ LOCAL bool RFM95_send(const uint8_t recipient, uint8_t *data, const uint8_t len, packet.payloadLen = min(len, (uint8_t)RFM95_MAX_PAYLOAD_LEN); packet.header.controlFlags = flags; (void)memcpy((void *)&packet.payload, (void *)data, packet.payloadLen); - return RFM95_sendFrame(&packet, increaseSequenceCounter); + return RFM95_sendFrame(&packet); } LOCAL void RFM95_setFrequency(const uint32_t frequencyHz) @@ -409,6 +407,7 @@ LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel) LOCAL void RFM95_enableTCXO(void) { while ((RFM95_readReg(RFM95_REG_4B_TCXO) & RFM95_TCXO_TCXO_INPUT_ON) != RFM95_TCXO_TCXO_INPUT_ON) { + delay(1); (void)RFM95_writeReg(RFM95_REG_4B_TCXO, (RFM95_readReg(RFM95_REG_4B_TCXO) | RFM95_TCXO_TCXO_INPUT_ON)); } @@ -451,17 +450,20 @@ LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode) regMode = RFM95_MODE_SLEEP; } else if (newRadioMode == RFM95_RADIO_MODE_CAD) { regMode = RFM95_MODE_CAD; - (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x80); // Interrupt on CadDone, DIO0 + (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, + RFM95_DIOMAPPING1_DIO0_10); // Interrupt on CadDone, DIO0 } else if (newRadioMode == RFM95_RADIO_MODE_RX) { RFM95.dataReceived = false; RFM95.ackReceived = false; - regMode = RFM95_MODE_RXCONTINUOUS; - (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x00); // Interrupt on RxDone, DIO0 + regMode = RFM95_MODE_RX; + (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, + RFM95_DIOMAPPING1_DIO0_00); // Interrupt on RxDone, DIO0 (void)RFM95_writeReg(RFM95_REG_0D_FIFO_ADDR_PTR, RFM95_RX_FIFO_ADDR); // set FIFO ptr to beginning of RX FIFO address } else if (newRadioMode == RFM95_RADIO_MODE_TX) { regMode = RFM95_MODE_TX; - (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, 0x40); // Interrupt on TxDone, DIO0 + (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, + RFM95_DIOMAPPING1_DIO0_01); // Interrupt on TxDone, DIO0 } else { return false; } @@ -514,6 +516,8 @@ LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_sequenceNumber_t s rfm95_controlFlags_t flags = 0u; RFM95_setACKReceived(flags, true); RFM95_setACKRSSIReport(flags, true); + // increase sequence counter, overflow is ok + RFM95.txSequenceNumber++; (void)RFM95_send(recipient, (uint8_t *)&ACK, sizeof(rfm95_ack_t), flags); } @@ -541,6 +545,8 @@ LOCAL bool RFM95_executeATC(const rfm95_RSSI_t currentRSSI, const rfm95_RSSI_t t LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, const uint8_t bufferSize, const bool noACK) { + // increase sequence counter, overflow is ok + RFM95.txSequenceNumber++; for (uint8_t retry = 0; retry < RFM95_RETRIES; retry++) { RFM95_DEBUG(PSTR("RFM95:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RETRY=%" PRIu8 "\n"), recipient, RFM95.txSequenceNumber, @@ -548,7 +554,7 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, rfm95_controlFlags_t flags = 0u; RFM95_setACKRequested(flags, !noACK); // send packet - if (!RFM95_send(recipient, (uint8_t *)buffer, bufferSize, flags, !retry)) { + if (!RFM95_send(recipient, (uint8_t *)buffer, bufferSize, flags)) { return false; } (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); @@ -557,12 +563,12 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, } const uint32_t enterMS = hwMillis(); while (hwMillis() - enterMS < RFM95_RETRY_TIMEOUT_MS && !RFM95.dataReceived) { - RFM95_handler(); + RFM95_handling(); if (RFM95.ackReceived) { const uint8_t sender = RFM95.currentPacket.header.sender; const rfm95_sequenceNumber_t ACKsequenceNumber = RFM95.currentPacket.ACK.sequenceNumber; - const rfm95_controlFlags_t flag = RFM95.currentPacket.header.controlFlags; - const rfm95_RSSI_t RSSI = RFM95.currentPacket.ACK.RSSI; + const rfm95_controlFlags_t ACKflags = RFM95.currentPacket.header.controlFlags; + const rfm95_RSSI_t ACKRSSI = RFM95.currentPacket.ACK.RSSI; //const rfm95_SNR_t SNR = RFM95.currentPacket.ACK.SNR; RFM95.ackReceived = false; // packet read, back to RX @@ -571,11 +577,11 @@ LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, (ACKsequenceNumber == RFM95.txSequenceNumber)) { RFM95_DEBUG(PSTR("RFM95:SWR:ACK FROM=%" PRIu8 ",SEQ=%" PRIu16 ",RSSI=%" PRIi16 "\n"),sender, ACKsequenceNumber, - RFM95_internalToRSSI(RSSI)); + RFM95_internalToRSSI(ACKRSSI)); //RFM95_clearRxBuffer(); // ATC - if (RFM95.ATCenabled && RFM95_getACKRSSIReport(flag)) { - (void)RFM95_executeATC(RSSI, RFM95.ATCtargetRSSI); + if (RFM95.ATCenabled && RFM95_getACKRSSIReport(ACKflags)) { + (void)RFM95_executeATC(ACKRSSI, RFM95.ATCtargetRSSI); } return true; } // seq check @@ -605,7 +611,7 @@ LOCAL bool RFM95_waitCAD(void) const uint32_t enterMS = hwMillis(); while (RFM95.radioMode == RFM95_RADIO_MODE_CAD && (hwMillis() - enterMS < RFM95_CAD_TIMEOUT_MS) ) { doYield(); - RFM95_handler(); + RFM95_handling(); } return !RFM95.channelActive; } diff --git a/hal/transport/RFM95/driver/RFM95.h b/hal/transport/RFM95/driver/RFM95.h index f8159460c..83f639980 100644 --- a/hal/transport/RFM95/driver/RFM95.h +++ b/hal/transport/RFM95/driver/RFM95.h @@ -26,7 +26,7 @@ * - ATC control * * Definitions for HopeRF LoRa radios: - * http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf + * https://www.hoperf.com/data/upload/portal/20190611/RFM95W-V1.1.pdf * */ @@ -145,7 +145,11 @@ #define RFM95_FIFO_SIZE (0xFFu) //!< Max number of bytes the LORA Rx/Tx FIFO can hold #define RFM95_RX_FIFO_ADDR (0x00u) //!< RX FIFO addr pointer #define RFM95_TX_FIFO_ADDR (0x80u) //!< TX FIFO addr pointer -#define RFM95_MAX_PACKET_LEN (0x40u) //!< This is the maximum number of bytes that can be carried by the LORA +#if !defined(MY_RFM95_ENABLE_SW_ENCRYPTION) +#define RFM95_MAX_PACKET_LEN (MAX_MESSAGE_SIZE + RFM95_HEADER_LEN) //!< This is the maximum number of bytes that can be carried by the LORA +#else +#define RFM95_MAX_PACKET_LEN (16 + 32 + 32 + RFM95_HEADER_LEN) //!< This is the maximum number of bytes that can be carried by the LORA +#endif #define RFM95_PREAMBLE_LENGTH (8u) //!< Preamble length, default=8 #define RFM95_CAD_TIMEOUT_MS (2*1000ul) //!< channel activity detection timeout #define RFM95_POWERUP_DELAY_MS (100u) //!< Power up delay, allow VCC to settle, transport to become fully operational @@ -324,18 +328,16 @@ LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize); * @param data * @param len * @param flags -* @param increaseSequenceCounter * @return True if packet sent */ LOCAL bool RFM95_send(const uint8_t recipient, uint8_t *data, const uint8_t len, - const rfm95_controlFlags_t flags, const bool increaseSequenceCounter = true); + const rfm95_controlFlags_t flags); /** * @brief RFM95_sendFrame * @param packet -* @param increaseSequenceCounter * @return True if frame sent */ -LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet, const bool increaseSequenceCounter = true); +LOCAL bool RFM95_sendFrame(rfm95_packet_t *packet); /** * @brief RFM95_setPreambleLength * @param preambleLength @@ -368,7 +370,7 @@ LOCAL bool RFM95_setTxPowerPercent(const uint8_t newPowerPercent); * clock frequency especially when using narrow bandwidths. * @note Has to be called while radio is in sleep mode. */ -LOCAL void RFM95_enableTCXO(void); +LOCAL void RFM95_enableTCXO(void) __attribute__((unused)); /** * @brief Sets the radio into low-power sleep mode @@ -430,9 +432,9 @@ LOCAL void RFM95_interruptHandler(void); LOCAL void RFM95_interruptHandling(void); /** -* @brief RFM95_handler +* @brief RFM95_handling */ -LOCAL void RFM95_handler(void); +LOCAL void RFM95_handling(void); /** * @brief RFM95_getSendingRSSI * @return RSSI Signal strength of last packet received diff --git a/hal/transport/RFM95/driver/RFM95_RFM69.cpp b/hal/transport/RFM95/driver/RFM95_RFM69.cpp new file mode 100644 index 000000000..4d936779f --- /dev/null +++ b/hal/transport/RFM95/driver/RFM95_RFM69.cpp @@ -0,0 +1,726 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2019 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley + * Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html + * + * RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2019 Olivier Mauti + * + * Definitions for HopeRF LoRa radios: + * https://www.hoperf.com/data/upload/portal/20190611/RFM95W-V1.1.pdf + * + */ + +#include "RFM95_RFM69.h" + +// debug +#if defined(MY_DEBUG_VERBOSE_RFM95) +#define RFM95_DEBUG(x,...) DEBUG_OUTPUT(x, ##__VA_ARGS__) //!< Debug print +#else +#define RFM95_DEBUG(x,...) //!< DEBUG null +#endif + +rfm95_internal_t RFM95; //!< internal variables +volatile bool RFM95_irq; // this triggers RX restart + (void)RFM95_writeReg(RFM95_REG_10_RSSI_THRESHOLD, RFM95_RSSItoInternal(-90)); + // single side RxBw = FxOsc/(RxBwMant*2^(RxBwExp+2)), i.e. for 250kHz total Bw => RxBw should be 125kHz, i.e. FxOSC=32Mhz, RxBwMant=16, RxBwExp=2 + (void)RFM95_writeReg(RFM95_REG_12_RXBW, RFM95_RXBW_MANT_16 | RFM95_RXBW_EXP_2); + (void)RFM95_writeReg(RFM95_REG_13_AFCBW, RFM95_RXBW_MANT_16 | RFM95_RXBW_EXP_2); + (void)RFM95_writeReg(RFM95_REG_1A_AFCFEI, RFM95_AFCFEI_AFCAUTOCLEAR_ON); + (void)RFM95_writeReg(RFM95_REG_24_OSC, RFM95_OSC_CLKOUT_OFF); + (void)RFM95_writeReg(RFM95_REG_25_PREAMBLE_MSB, RFM95_PREAMBLESIZE_MSB_VALUE); + (void)RFM95_writeReg(RFM95_REG_26_PREAMBLE_LSB, RFM95_PREAMBLESIZE_LSB_VALUE); + (void)RFM95_writeReg(RFM95_REG_27_SYNC_CONFIG, + RFM95_SYNC_AUTORXRESTART_NO_PLL | RFM95_SYNC_PREAMBLE_POLARITY_AA | RFM95_SYNC_ON | + RFM95_SYNC_FIFO_FILL_AUTO | RFM95_SYNC_SIZE_2); + (void)RFM95_writeReg(RFM95_REG_28_SYNC_VALUE1, RFM95_SYNCVALUE1); + (void)RFM95_writeReg(RFM95_REG_29_SYNC_VALUE2, RFM95_SYNCVALUE2); + (void)RFM95_writeReg(RFM95_REG_30_PACKET_CONFIG1, RFM95_CONFIG_WHITE); + (void)RFM95_writeReg(RFM95_REG_31_PACKET_CONFIG2, RFM95_CONFIG_PACKET); + (void)RFM95_writeReg(RFM95_REG_32_PAYLOAD_LENGTH, RFM95_MAX_PACKET_LEN); + (void)RFM95_writeReg(RFM95_REG_33_NODE_ADDR, RFM95_BROADCAST_ADDRESS); + (void)RFM95_writeReg(RFM95_REG_34_BROADCAST_ADDR, RFM95_BROADCAST_ADDRESS); + (void)RFM95_writeReg(RFM95_REG_35_FIFO_THRESHOLD, + RFM95_TXSTART_CONDITION_FIFO_THRESHOLD | (RFM95_HEADER_LEN - 1)); + (void)RFM95_writeReg(RFM95_REG_36_SEQ_CONFIG1, + RFM95_SEQ_CONFIG1_SEQUENCER_STOP); // disable sequencer + (void)RFM95_writeReg(RFM95_REG_3B_IMAGECAL, RFM95_IMAGECAL_TEMPTHRESHOLD_10); + + // IRQ on packet sent (TX mode) and payload ready (RX mode) + (void)RFM95_writeReg(RFM95_REG_40_DIO_MAPPING1, + RFM95_DIOMAPPING1_DIO0_00 | RFM95_DIOMAPPING1_DIO1_11 | RFM95_DIOMAPPING1_DIO2_00); + RFM95_setFrequency(frequencyHz); + + // calibrate image rejection mixer at used frequency (automatical calibration after POR at 434MHz) + (void)RFM95_writeReg(RFM95_REG_3B_IMAGECAL, RFM95_IMAGECAL_IMAGECAL_START); + while (RFM95_readReg(RFM95_REG_3B_IMAGECAL) & RFM95_IMAGECAL_IMAGECAL_RUNNING) {}; + + (void)RFM95_setTxPowerLevel(MY_RFM95_TX_POWER_DBM); + + if (!RFM95_sanityCheck()) { + // sanity check failed, check wiring or replace module + RFM95_DEBUG(PSTR("!RFM95:INIT:SANCHK FAIL\n")); + return false; + } + + // IRQ + RFM95_irq = false; + hwPinMode(MY_RFM95_IRQ_PIN, INPUT); + attachInterrupt(MY_RFM95_IRQ_NUM, RFM95_interruptHandler, RISING); + + return true; +} + +#if defined(ARDUINO_ARCH_ESP32) +#define IRQ_HANDLER_ATTR IRAM_ATTR +#elif defined(ARDUINO_ARCH_ESP8266) +#define IRQ_HANDLER_ATTR ICACHE_RAM_ATTR +#else +#define IRQ_HANDLER_ATTR +#endif +LOCAL void IRQ_HANDLER_ATTR RFM95_interruptHandler(void) +{ + // set flag + RFM95_irq = true; +} + +LOCAL void RFM95_handling(void) +{ + if (RFM95_irq) { + RFM95.currentPacket.RSSI = static_cast(RFM95_readReg(RFM95_REG_11_RSSI_VALUE)); + // clear flag, 8bit - no need for critical section + //RFM69_irq = false; ==> flag cleared when transition to RX or TX + RFM95_DEBUG(PSTR("RFM95:IRQ\n")); + // radio: RX + const uint8_t regIrqFlags2 = RFM95_readReg(RFM95_REG_3F_IRQ_FLAGS2); + if (regIrqFlags2 & RFM95_IRQ2_PAYLOAD_READY) { + (void)RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); + RFM95_RXFIFOHandling(); + } else { + RFM95_DEBUG(PSTR("!RFM95:IRQ NH, IRQ2=% " PRIu8 "\n"), regIrqFlags2); // not handled IRQ + } + } +} + +// RxDone, TxDone +LOCAL void RFM95_RXFIFOHandling(void) +{ + // reset flags + RFM95.ackReceived = false; + RFM95.dataReceived = false; + RFM95_prepareSPITransaction(); + (void)RFM95_SPI.transfer(RFM95_REG_00_FIFO & RFM95_READ_REGISTER); + // set reading pointer + uint8_t *current = (uint8_t *)&RFM95.currentPacket; + bool headerRead = false; + // first read header + uint8_t readingLength = RFM95_HEADER_LEN; + while (readingLength--) { + *current++ = RFM95_SPI.transfer((uint8_t)RFM95_NOP); + if (!readingLength && !headerRead) { + // header read + headerRead = true; + if (RFM95.currentPacket.header.version >= RFM95_MIN_PACKET_HEADER_VERSION) { + // read payload + readingLength = min(RFM95.currentPacket.header.packetLen - (RFM95_HEADER_LEN - 1), + RFM95_MAX_PACKET_LEN); + // save payload length + RFM95.currentPacket.payloadLen = readingLength; + RFM95.ackReceived = RFM95_getACKReceived(RFM95.currentPacket.header.controlFlags); + RFM95.dataReceived = !RFM95.ackReceived; + } + } + } + RFM95_concludeSPITransaction(); + + // ACK handling + if (RFM95_getACKRequested(RFM95.currentPacket.header.controlFlags)) { +#if (F_CPU > 16*1000000ul) + // delay for fast nodes + delay(5); +#endif + RFM95_sendACK(RFM95.currentPacket.header.sender, RFM95.currentPacket.header.sequenceNumber, + RFM95.currentPacket.RSSI); + // radio in RX + } + + // primitive deduplication + if (RFM95.dataReceived) { + if (RFM95.currentPacket.header.sender == RFM95.lastPacket.sender && + RFM95.currentPacket.header.sequenceNumber == RFM95.lastPacket.sequenceNumber) { + // same packet received + RFM95.dataReceived = false; + RFM95_DEBUG(PSTR("!RFM95:PKT DD\n")); // packet de-duplication + } else { + // new packet received + RFM95_DEBUG(PSTR("RFM95:NEW PKT\n")); + RFM95.lastPacket.sender = RFM95.currentPacket.header.sender; + RFM95.lastPacket.sequenceNumber = RFM95.currentPacket.header.sequenceNumber; + } + } + +} + +LOCAL bool RFM95_available(void) +{ + if (RFM95.dataReceived) { + // data received - we are still in STDBY from IRQ handler + return true; + } else if (RFM95.radioMode != RFM95_RADIO_MODE_RX) { + // we are not in RX, not CAD, and no data received + (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); + } + return false; +} + +LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize) +{ + if (buf == NULL) { + return 0; + } + // clear data flag + RFM95.dataReceived = false; + const uint8_t payloadLen = min(RFM95.currentPacket.payloadLen, maxBufSize); + (void)memcpy((void *)buf, (void *)&RFM95.currentPacket.payload, payloadLen); + return payloadLen; +} + +LOCAL bool RFM95_send(const rfm95_packet_t *packet) +{ + RFM95_DEBUG(PSTR("RFM95:SND:LEN=%" PRIu8 "\n"), packet->header.packetLen + 1); +#if defined(MY_DEBUG_VERBOSE_RFM95) + hwDebugBuf2Str((const uint8_t *)&packet->data, packet->header.packetLen + 1); + RFM95_DEBUG(PSTR("RFM95:SND:RAW=%s\n"), hwDebugPrintStr); +#endif + + (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); + delay(3); // timing for RF startup until RSSI sampling + const uint32_t CSMA_START_MS = hwMillis(); + bool channelFree; + do { + channelFree = RFM95_channelFree(); + doYield(); + } while (!channelFree && (hwMillis() - CSMA_START_MS < MY_RFM95_CSMA_TIMEOUT_MS)); + if (!channelFree) { + return false; + } + (void)RFM95_setRadioMode(RFM95_RADIO_MODE_TX); + (void)RFM95_burstWriteReg(RFM95_REG_00_FIFO, packet->data, + packet->header.packetLen + 1); // + 1 for length byte + const uint32_t startTX_MS = hwMillis(); + // send message, if sent, irq fires and radio returns to standby + while (!RFM95_irq && (hwMillis() - startTX_MS < MY_RFM95_TX_TIMEOUT_MS)) { + doYield(); + } + // back to RX + //RFM95_irq = false; ==> IRQ flag cleared in setRadioMode() + //const bool result = RFM95_readReg(RFM95_REG_3F_IRQ_FLAGS2) & RFM95_IRQ2_PACKET_SENT; + (void)RFM95_setRadioMode(RFM95_RADIO_MODE_RX); + //return result; + return true; +} + +LOCAL bool RFM95_encrypt(const char *key) +{ + return false; +} + +LOCAL void RFM95_setFrequency(const uint32_t frequencyHz) +{ + RFM95_DEBUG(PSTR("RFM95:INIT:FREQ=%" PRIu32 "\n"), frequencyHz); + const uint32_t freqReg = (uint32_t)(frequencyHz / RFM95_FSTEP); + (void)RFM95_writeReg(RFM95_REG_06_FRF_MSB, (uint8_t)((freqReg >> 16) & 0xff)); + (void)RFM95_writeReg(RFM95_REG_07_FRF_MID, (uint8_t)((freqReg >> 8) & 0xff)); + (void)RFM95_writeReg(RFM95_REG_08_FRF_LSB, (uint8_t)(freqReg & 0xff)); +} + +LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel) +{ + // RFM95/96/97/98 does not have RFO pins connected to anything. Only PA_BOOST + newPowerLevel = max((int8_t)RFM95_MIN_POWER_LEVEL_DBM, newPowerLevel); + newPowerLevel = min((int8_t)RFM95_MAX_POWER_LEVEL_DBM, newPowerLevel); + if (newPowerLevel != RFM95.powerLevel) { + RFM95.powerLevel = newPowerLevel; + uint8_t val; + if (newPowerLevel > 20) { + // enable DAC, adds 3dBm + // The documentation is pretty confusing on this topic: PaSelect says the max power is 20dBm, + // but OutputPower claims it would be 17dBm. Measurements show 20dBm is correct + (void)RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_ENABLE); + val = newPowerLevel - 8; + } else { + (void)RFM95_writeReg(RFM95_REG_4D_PA_DAC, RFM95_PA_DAC_DISABLE); + val = newPowerLevel - 5; + } + (void)RFM95_writeReg(RFM95_REG_09_PA_CONFIG, RFM95_PA_SELECT | val); + RFM95_DEBUG(PSTR("RFM95:PTX:LEVEL=%" PRIi8 "\n"), newPowerLevel); + return true; + } + return false; + +} + +LOCAL void RFM95_enableTCXO(void) +{ + while ((RFM95_readReg(RFM95_REG_4B_TCXO) & RFM95_TCXO_TCXO_INPUT_ON) != RFM95_TCXO_TCXO_INPUT_ON) { + (void)RFM95_writeReg(RFM95_REG_4B_TCXO, + (RFM95_readReg(RFM95_REG_4B_TCXO) | RFM95_TCXO_TCXO_INPUT_ON)); + } +} + +LOCAL void RFM95_setAddress(const uint8_t addr) +{ + RFM95.address = addr; + (void)RFM95_writeReg(RFM95_REG_33_NODE_ADDR, addr); +} + +LOCAL uint8_t RFM95_getAddress(void) +{ + return RFM95.address; +} + +LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode) +{ + uint8_t regMode = RFM95_RADIO_MODE_STDBY; + RFM95.radioMode = newRadioMode; + RFM95_irq = false; + + if (newRadioMode == RFM95_RADIO_MODE_STDBY) { + regMode = RFM95_MODE_STDBY; + } else if (newRadioMode == RFM95_RADIO_MODE_SLEEP) { + regMode = RFM95_MODE_SLEEP; + } else if (newRadioMode == RFM95_RADIO_MODE_RX) { + regMode = RFM95_MODE_RX; + } else if (newRadioMode == RFM95_RADIO_MODE_TX) { + regMode = RFM95_MODE_TX; + } + (void)RFM95_writeReg(RFM95_REG_01_OP_MODE, + RFM95_FSK_OOK_MODE | RFM95_MODULATION_FSK | RFM95_LOW_FREQUENCY_REG | regMode); + // wait until mode ready + // while (!(RFM95_readReg(RFM95_REG_3E_IRQ_FLAGS1) & RFM95_IRQ1_MODE_READY)) {}; + RFM95.radioMode = newRadioMode; + RFM95_DEBUG(PSTR("RFM95:SRM:MODE=%" PRIu8 "\n"), newRadioMode); + return true; +} + +LOCAL void RFM95_powerUp(void) +{ +#if defined(MY_RFM95_POWER_PIN) + RFM95_DEBUG(PSTR("RFM95:PWU\n")); // power up radio + hwDigitalWrite(MY_RFM95_POWER_PIN, HIGH); + delay(RFM95_POWERUP_DELAY_MS); +#endif +} +LOCAL void RFM95_powerDown(void) +{ +#if defined(MY_RFM95_POWER_PIN) + RFM95_DEBUG(PSTR("RFM95:PWD\n")); // power down radio + hwDigitalWrite(MY_RFM95_POWER_PIN, LOW); +#endif +} + +LOCAL bool RFM95_sleep(void) +{ + RFM95_DEBUG(PSTR("RFM95:RSL\n")); // put radio to sleep + RFM95_setRadioMode(RFM95_RADIO_MODE_SLEEP); + return true; +} + +LOCAL bool RFM95_standBy(void) +{ + RFM95_DEBUG(PSTR("RFM95:RSB\n")); // put radio to standby + RFM95_setRadioMode(RFM95_RADIO_MODE_STDBY); + return true; +} + +LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_rfm69_sequenceNumber_t sequenceNumber, + const rfm95_RSSI_t RSSI) +{ + RFM95_DEBUG(PSTR("RFM95:SAC:SEND ACK,TO=%" PRIu8 ",SEQ=%" PRIu16 ",RSSI=%" PRIi16 "\n"),recipient, + sequenceNumber, + RFM95_internalToRSSI(RSSI)); + rfm95_packet_t packet; + packet.ACK.RSSI = RSSI; + packet.ACK.sequenceNumber = sequenceNumber; + packet.header.version = RFM95_PACKET_HEADER_VERSION; + packet.header.sender = RFM95.address; + packet.header.recipient = recipient; + packet.header.packetLen = sizeof(rfm95_ack_t) + (sizeof(rfm95_header_t) - 1); + packet.header.sequenceNumber = ++RFM95.txSequenceNumber; + packet.header.controlFlags = 0; + RFM95_setACKReceived(packet.header.controlFlags, true); + RFM95_setACKRSSIReport(packet.header.controlFlags, true); + (void)RFM95_send(&packet); +} + +LOCAL bool RFM95_executeATC(const rfm95_RSSI_t currentRSSI, const rfm95_RSSI_t targetRSSI) +{ + rfm95_powerLevel_t newPowerLevel = RFM95.powerLevel; + const int16_t ownRSSI = RFM95_internalToRSSI(currentRSSI); + const int16_t uRange = RFM95_internalToRSSI(targetRSSI) + RFM95_ATC_TARGET_RANGE_DBM; + const int16_t lRange = RFM95_internalToRSSI(targetRSSI) - RFM95_ATC_TARGET_RANGE_DBM; + if (ownRSSI < lRange && RFM95.powerLevel < RFM95_MAX_POWER_LEVEL_DBM) { + // increase transmitter power + newPowerLevel++; + } else if (ownRSSI > uRange && RFM95.powerLevel > RFM95_MIN_POWER_LEVEL_DBM) { + // decrease transmitter power + newPowerLevel--; + } else { + // nothing to adjust + return false; + } + RFM95_DEBUG(PSTR("RFM95:ATC:ADJ TXL,cR=%" PRIi16 ",tR=%" PRIi16 "..%" PRIi16 ",TXL=%" PRIi8 "\n"), + ownRSSI, lRange, uRange, RFM95.powerLevel); + return RFM95_setTxPowerLevel(newPowerLevel); +} + +LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, + const uint8_t bufferSize, const bool noACK) +{ + rfm95_packet_t packet; + packet.header.version = RFM95_PACKET_HEADER_VERSION; + packet.header.sender = RFM95.address; + packet.header.recipient = recipient; + packet.header.sequenceNumber = + ++RFM95.txSequenceNumber; // increase sequence counter, overflow is ok + packet.header.controlFlags = 0; // reset all flags + RFM95_setACKRequested(packet.header.controlFlags, !noACK); + RFM95_setACKRSSIReport(packet.header.controlFlags, RFM95.ATCenabled); + (void)memcpy((void *)packet.payload, buffer, bufferSize); + packet.payloadLen = bufferSize; + + packet.header.packetLen = packet.payloadLen + (sizeof(rfm95_header_t) - 1); // -1 length byte + + for (uint8_t TXAttempt = 0; TXAttempt < RFM95_TX_ATTEMPTS; TXAttempt++) { + RFM95_DEBUG(PSTR("RFM95:SWR:SEND,TO=%" PRIu8 ",SEQ=%" PRIu8 ",TX=%" PRIu8 "\n"), recipient, + RFM95.txSequenceNumber, TXAttempt + 1); + + if (!RFM95_send(&packet)) { + // CSMA check failed + continue; + } + // radio is in RX + if (noACK) { + // no ACK requested + return true; + } + const uint32_t enterMS = hwMillis(); + // progressive wait time with random component + const uint32_t effectiveWaitingTimeMS = RFM95_RETRY_TIMEOUT_MS + (hwMillis() & 0x3Fu) + + (TXAttempt * 50u); + RFM95_DEBUG(PSTR("RFM95:SWR:ACK WAIT=%" PRIu32 "\n"), effectiveWaitingTimeMS); + while (!RFM95.dataReceived && (hwMillis() - enterMS < effectiveWaitingTimeMS)) { + RFM95_handling(); + if (RFM95.ackReceived) { + const uint8_t ACKsender = RFM95.currentPacket.header.sender; + const rfm95_rfm69_sequenceNumber_t ACKsequenceNumber = RFM95.currentPacket.ACK.sequenceNumber; + const rfm95_controlFlags_t ACKflags = RFM95.currentPacket.header.controlFlags; + const rfm95_RSSI_t ACKRSSI = RFM95.currentPacket.ACK.RSSI; + RFM95.ackReceived = false; + // packet read, back to RX + RFM95_setRadioMode(RFM95_RADIO_MODE_RX); + if (ACKsender == recipient && + (ACKsequenceNumber == RFM95.txSequenceNumber)) { + RFM95_DEBUG(PSTR("RFM95:SWR:ACK FROM=%" PRIu8 ",SEQ=%" PRIu8 ",RSSI=%" PRIi16 "\n"), ACKsender, + ACKsequenceNumber, + RFM95_internalToRSSI(ACKRSSI)); + // ATC + if (RFM95.ATCenabled && RFM95_getACKRSSIReport(ACKflags)) { + (void)RFM95_executeATC(ACKRSSI, RFM95.ATCtargetRSSI); + } + return true; + } // seq check + } + doYield(); + } + if (RFM95.dataReceived) { + return false; + } + RFM95_DEBUG(PSTR("!RFM95:SWR:NACK,SEQ=%" PRIu8 "\n"), RFM95.txSequenceNumber); + const uint32_t enterCSMAMS = hwMillis(); + const uint16_t randDelayCSMA = enterMS % 100; + while (hwMillis() - enterCSMAMS < randDelayCSMA) { + doYield(); + } + } + return false; +} + +LOCAL bool RFM95_channelFree(void) +{ + // returns true if channel activity under RFM95_CSMA_LIMIT_DBM + const rfm95_RSSI_t RSSI = RFM95_readReg(RFM95_REG_11_RSSI_VALUE); + const uint8_t RSSIflag = RFM95_readReg(RFM95_REG_3E_IRQ_FLAGS1) & 0x08; + RFM95_DEBUG(PSTR("RFM95:CSMA:RSSI=%" PRIi16 ",REG1=%" PRIu8 "\n"), RFM95_internalToRSSI(RSSI), + RSSIflag); + (void)RSSIflag; + //RFM95_writeReg(RFM95_REG_3E_IRQ_FLAGS1, 0x08); // clear RSSI flag + + return (RSSI > RFM95_RSSItoInternal(-90/*MY_RFM95_CSMA_LIMIT_DBM*/)); + //return !RSSIflag; +} + +LOCAL void RFM95_ATCmode(const bool OnOff, const int16_t targetRSSI) +{ + RFM95.ATCenabled = OnOff; + RFM95.ATCtargetRSSI = RFM95_RSSItoInternal(targetRSSI); +} + +LOCAL bool RFM95_sanityCheck(void) +{ + bool result = true; + result &= RFM95_readReg(RFM95_REG_28_SYNC_VALUE1) == RFM95_SYNCVALUE1; + result &= RFM95_readReg(RFM95_REG_29_SYNC_VALUE2) == RFM95_SYNCVALUE2; + result &= RFM95_readReg(RFM95_REG_30_PACKET_CONFIG1) == RFM95_CONFIG_WHITE; + return result; +} + + +LOCAL int16_t RFM95_getSendingRSSI(void) +{ + // own RSSI, as measured by the recipient - ACK part + if (RFM95_getACKRSSIReport(RFM95.currentPacket.header.controlFlags)) { + return RFM95_internalToRSSI(RFM95.currentPacket.ACK.RSSI); + } else { + // not possible + return INVALID_RSSI; + } +} + +LOCAL int16_t RFM95_getSendingSNR(void) +{ + return INVALID_SNR; +} + +LOCAL int16_t RFM95_getReceivingRSSI(void) +{ + // RSSI from last received packet + return static_cast(RFM95_internalToRSSI(RFM95.currentPacket.RSSI)); +} + +LOCAL int16_t RFM95_getReceivingSNR(void) +{ + return INVALID_SNR; +} + +LOCAL uint8_t RFM95_getTxPowerLevel(void) +{ + return RFM95.powerLevel; +} + +LOCAL uint8_t RFM95_getTxPowerPercent(void) +{ + // report TX level in % + const uint8_t result = static_cast(100.0f * (RFM95.powerLevel - + RFM95_MIN_POWER_LEVEL_DBM) / + (RFM95_MAX_POWER_LEVEL_DBM + - RFM95_MIN_POWER_LEVEL_DBM)); + return result; +} +LOCAL bool RFM95_setTxPowerPercent(const uint8_t newPowerPercent) +{ + const rfm95_powerLevel_t newPowerLevel = static_cast + (RFM95_MIN_POWER_LEVEL_DBM + (RFM95_MAX_POWER_LEVEL_DBM + - RFM95_MIN_POWER_LEVEL_DBM) * (newPowerPercent / 100.0f)); + RFM95_DEBUG(PSTR("RFM95:SPP:PCT=%" PRIu8 ",TX LEVEL=%" PRIi8 "\n"), newPowerPercent,newPowerLevel); + return RFM95_setTxPowerLevel(newPowerLevel); +} + diff --git a/hal/transport/RFM95/driver/RFM95_RFM69.h b/hal/transport/RFM95/driver/RFM95_RFM69.h new file mode 100644 index 000000000..65d10fc54 --- /dev/null +++ b/hal/transport/RFM95/driver/RFM95_RFM69.h @@ -0,0 +1,485 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2018 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley + * Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html + * + * RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti + * + * Changelog: + * - ACK with sequenceNumber + * - ATC control + * + * Definitions for HopeRF LoRa radios: + * https://www.hoperf.com/data/upload/portal/20190611/RFM95W-V1.1.pdf + * + */ + +/** +* @file RFM95_RFM69.h +* +* @defgroup RFM95RFM69grp RFM95_RFM69 +* @ingroup internals +* @{ +* +* RFM95 driver-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE +* - [!] Exclamation mark is prepended in case of error +* +* |E| SYS | SUB | Message | Comment +* |-|-------|------|----------------------------------------|----------------------------------------------------------------------------------- +* | | RFM95 | INIT | | Initialise RFM95 radio +* | | RFM95 | INIT | PIN,CS=%%d,IQP=%%d,IQN=%%d[,RST=%%d] | Pin configuration: chip select (CS), IRQ pin (IQP), IRQ number (IQN), Reset (RST) +* |!| RFM95 | INIT | SANCHK FAIL | Sanity check failed, check wiring or replace module +* |!| RFM95 | IRH | CRC FAIL | Incoming packet has CRC error, skip +* | | RFM95 | RCV | SEND ACK | ACK request received, sending ACK back +* | | RFM95 | PTC | LEVEL=%%d | Set TX power level +* | | RFM95 | SAC | SEND ACK,TO=%%d,RSSI=%%d | Send ACK to node (TO), RSSI of received message (RSSI) +* | | RFM95 | ATC | ADJ TXL,cR=%%d,tR=%%d..%%d,TXL=%%d | Adjust TX level, current RSSI (cR), target RSSI range (tR), TX level (TXL) +* | | RFM95 | SWR | SEND,TO=%%d,RETRY=%%d | Send message to (TO), NACK retry counter (RETRY) +* | | RFM95 | SWR | ACK FROM=%%d,SEQ=%%d,RSSI=%%d | ACK received from node (FROM), seq ID (SEQ), (RSSI) +* |!| RFM95 | SWR | NACK | No ACK received +* | | RFM95 | SPP | PCT=%%d,TX LEVEL=%%d | Set TX level percent (PCT), TX level (LEVEL) +* | | RFM95 | PWD | | Power down radio +* | | RFM95 | PWU | | Power up radio +* +* RFM95 modem configuration +* +* BW = Bandwidth in kHz +* CR = Error correction code +* SF = Spreading factor, chips / symbol +* +* | CONFIG | BW | CR | SF | Comment | air-time (15 bytes) +* |------------------|-------|-----|------|-----------------------|------------------------ +* | BW125CR45SF128 | 125 | 4/5 | 128 | Default, medium range | 50ms +* | BW500CR45SF128 | 500 | 4/5 | 128 | Fast, short range | 15ms +* | BW31_25CR48SF512 | 31.25 | 4/8 | 512 | Slow, long range | 900ms +* | BW125CR48SF4096 | 125 | 4/8 | 4096 | Slow, long range | 1500ms +* +* See here for air-time calculation: https://docs.google.com/spreadsheets/d/1voGAtQAjC1qBmaVuP1ApNKs1ekgUjavHuVQIXyYSvNc +* +* @brief API declaration for RFM95_RFM69 compatibility mode +* +*/ + +#ifndef _RFM95_h +#define _RFM95_h + +#include "RFM95registers.h" + +#if !defined(RFM95_SPI) +#define RFM95_SPI hwSPI //!< default SPI +#endif + +// default PIN assignments, can be overridden +#if defined(ARDUINO_ARCH_AVR) +#if defined(__AVR_ATmega32U4__) +#define DEFAULT_RFM95_IRQ_PIN (3) //!< DEFAULT_RFM95_IRQ_PIN +#else +#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN +#endif +#elif defined(ARDUINO_ARCH_ESP8266) +#define DEFAULT_RFM95_IRQ_PIN (5) //!< DEFAULT_RFM95_IRQ_PIN +#elif defined(ARDUINO_ARCH_ESP32) +#define DEFAULT_RFM95_IRQ_PIN (16) //!< DEFAULT_RFM95_IRQ_PIN +#define DEFAULT_RFM95_IRQ_NUM digitalPinToInterrupt(DEFAULT_RFM95_IRQ_PIN) //!< DEFAULT_RFM95_IRQ_NUM +#elif defined(ARDUINO_ARCH_SAMD) +#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN +#elif defined(LINUX_ARCH_RASPBERRYPI) +#define DEFAULT_RFM95_IRQ_PIN (22) //!< DEFAULT_RFM95_IRQ_PIN +#elif defined(ARDUINO_ARCH_STM32F1) +#define DEFAULT_RFM95_IRQ_PIN (PA3) //!< DEFAULT_RFM95_IRQ_PIN +#elif defined(TEENSYDUINO) +#define DEFAULT_RFM95_IRQ_PIN (8) //!< DEFAULT_RFM95_IRQ_PIN +#else +#define DEFAULT_RFM95_IRQ_PIN (2) //!< DEFAULT_RFM95_IRQ_PIN +#endif + +#define DEFAULT_RFM95_CS_PIN (SS) //!< DEFAULT_RFM95_CS_PIN + +// SPI settings +#define RFM95_SPI_DATA_ORDER MSBFIRST //!< SPI data order +#define RFM95_SPI_DATA_MODE SPI_MODE0 //!< SPI mode + +// Modem configuration section +#define RFM95_CONFIG_NOWHITE (RFM95_PACKET1_FORMAT_VARIABLE | RFM95_PACKET1_DCFREE_OFF | RFM95_PACKET1_CRC_ON | RFM95_PACKET1_CRCAUTOCLEAR_ON | RFM95_PACKET1_ADRSFILTERING_NODEBROADCAST | RFM95_PACKET1_CRC_WHITENING_CCITT) //!< RFM95_CONFIG_NOWHITE +#define RFM95_CONFIG_WHITE (RFM95_PACKET1_FORMAT_VARIABLE | RFM95_PACKET1_DCFREE_WHITENING | RFM95_PACKET1_CRC_ON | RFM95_PACKET1_CRCAUTOCLEAR_ON | RFM95_PACKET1_ADRSFILTERING_NODEBROADCAST | RFM95_PACKET1_CRC_WHITENING_CCITT) //!< RFM95_CONFIG_WHITE +#define RFM95_CONFIG_MANCHESTER (RFM95_PACKET1_FORMAT_VARIABLE | RFM95_PACKET1_DCFREE_MANCHESTER | RFM95_PACKET1_CRC_ON | RFM95_PACKET1_CRCAUTOCLEAR_ON | RFM95_PACKET1_ADRSFILTERING_NODEBROADCAST | RFM95_PACKET1_CRC_WHITENING_CCITT) //!< RFM95_CONFIG_MANCHESTER + +#define RFM95_CONFIG_PACKET (RFM95_PACKET2_DATA_MODE_PACKET | RFM95_PACKET2_IOHOME_OFF | RFM95_PACKET2_IOHOME_POWER_FRAME_OFF | RFM95_PACKET2_BEACON_OFF) //!< RFM95_CONFIG_PACKET + +#define RFM95_SYNCVALUE1 (0x2D) //!< RFM95_SYNCVALUE1 +#define RFM95_SYNCVALUE2 (100) //!< RFM95_SYNCVALUE1, refers to MY_RFM69_NETWORKID + +// available frequency bands, non trivial values to avoid misconfiguration +#define RFM95_315MHZ (315000000ul) //!< RFM95_315MHZ +#define RFM95_433MHZ (433920000ul) //!< RFM95_433MHZ, center frequency 433.92 MHz +#define RFM95_865MHZ (865500000ul) //!< RFM95_865MHZ, center frequency 865.5 MHz +#define RFM95_868MHZ (868000000ul) //!< RFM95_868MHZ +#define RFM95_915MHZ (915000000ul) //!< RFM95_915MHZ + +#if !defined(RFM95_RETRY_TIMEOUT_MS) +// air-time approximation for timeout, 1 hop ~15 bytes payload - adjust if needed +// BW125/SF128: 50ms +// BW500/SF128: 15ms +// BW31.25/SF512: 900ms +// BW125/SF4096: 1500ms +#define RFM95_RETRY_TIMEOUT_MS (500ul) //!< Timeout for ACK, adjustments needed if modem configuration changed (air time different) +#endif + +#if !defined(MY_RFM95_TX_TIMEOUT_MS) +#define MY_RFM95_TX_TIMEOUT_MS (5*1000ul) //!< TX timeout +#endif + +#define RFM95_TX_ATTEMPTS (5u) //!< TX attempts + +// Frequency definitions +#define RFM95_169MHZ (169000000ul) //!< 169 Mhz +#define RFM95_315MHZ (315000000ul) //!< 315 Mhz +#define RFM95_434MHZ (433920000ul) //!< 433.92 Mhz +#define RFM95_868MHZ (868000000ul) //!< 868 Mhz +#define RFM95_915MHZ (915000000ul) //!< 915 Mhz + +#define RFM95_RETRIES (5u) //!< Retries in case of failed transmission +#define RFM95_FIFO_SIZE (0xFFu) //!< Max number of bytes the LORA Rx/Tx FIFO can hold +#define RFM95_RX_FIFO_ADDR (0x00u) //!< RX FIFO addr pointer +#define RFM95_TX_FIFO_ADDR (0x80u) //!< TX FIFO addr pointer +#define RFM95_MAX_PACKET_LEN (0x40u) //!< This is the maximum number of bytes that can be carried by the LORA +#define RFM95_PREAMBLE_LENGTH (8u) //!< Preamble length, default=8 +#define RFM95_CAD_TIMEOUT_MS (2*1000ul) //!< channel activity detection timeout +#define RFM95_POWERUP_DELAY_MS (10u) //!< Power up delay, allow VCC to settle, transport to become fully operational +#define RFM95_RESET_DELAY_US (100u) //!< Reset delay in uS + +#if !defined(MY_RFM95_CSMA_LIMIT_DBM) +#define MY_RFM95_CSMA_LIMIT_DBM (-80) //!< upper RX signal sensitivity threshold in dBm for carrier sense access +#endif +#if !defined(MY_RFM95_CSMA_TIMEOUT_MS) +#define MY_RFM95_CSMA_TIMEOUT_MS (500ul) //!< CSMA timeout +#endif + +#define RFM95_PACKET_HEADER_VERSION (1u) //!< RFM95 packet header version +#define RFM95_MIN_PACKET_HEADER_VERSION (1u) //!< Minimal RFM95 packet header version +#define RFM95_BIT_ACK_REQUESTED (7u) //!< RFM95 header, controlFlag, bit 7 +#define RFM95_BIT_ACK_RECEIVED (6u) //!< RFM95 header, controlFlag, bit 6 +#define RFM95_BIT_ACK_RSSI_REPORT (5u) //!< RFM95 header, controlFlag, bit 5 + +#define RFM95_BROADCAST_ADDRESS (255u) //!< Broadcasting address +#define RFM95_ATC_TARGET_RANGE_DBM (2u) //!< ATC target range +/- dBm +#define RFM95_RSSI_OFFSET (137u) //!< RSSI offset +#define RFM95_TARGET_RSSI (-70) //!< RSSI target +#define RFM95_PROMISCUOUS (false) //!< RFM95 promiscuous mode + +#define RFM95_FXOSC (32*1000000ul) //!< The crystal oscillator frequency of the module +#define RFM95_FSTEP (RFM95_FXOSC / 524288.0f) //!< The Frequency Synthesizer step + +// helper macros +#define RFM95_getACKRequested(__value) ((bool)bitRead(__value, RFM95_BIT_ACK_REQUESTED)) //!< getACKRequested +#define RFM95_setACKRequested(__value, __flag) bitWrite(__value, RFM95_BIT_ACK_REQUESTED,__flag) //!< setACKRequested +#define RFM95_getACKReceived(__value) ((bool)bitRead(__value, RFM95_BIT_ACK_RECEIVED)) //!< getACKReceived +#define RFM95_setACKReceived(__value, __flag) bitWrite(__value, RFM95_BIT_ACK_RECEIVED,__flag) //!< setACKReceived +#define RFM95_setACKRSSIReport(__value, __flag) bitWrite(__value, RFM95_BIT_ACK_RSSI_REPORT,__flag) //!< setACKRSSIReport +#define RFM95_getACKRSSIReport(__value) ((bool)bitRead(__value, RFM95_BIT_ACK_RSSI_REPORT)) //!< getACKRSSIReport + +#define RFM95_MIN_POWER_LEVEL_DBM ((rfm95_powerLevel_t)5u) //!< min. power level +#if defined(MY_RFM95_MAX_POWER_LEVEL_DBM) +#define RFM95_MAX_POWER_LEVEL_DBM MY_RFM95_MAX_POWER_LEVEL_DBM //!< MY_RFM95_MAX_POWER_LEVEL_DBM +#else +#define RFM95_MAX_POWER_LEVEL_DBM ((rfm95_powerLevel_t)23u) //!< max. power level +#endif +/** +* @brief Radio modes +*/ +typedef enum { + RFM95_RADIO_MODE_RX = 0, //!< RX mode + RFM95_RADIO_MODE_TX = 1, //!< TX mode + RFM95_RADIO_MODE_CAD = 2, //!< CAD mode + RFM95_RADIO_MODE_SLEEP = 3, //!< SLEEP mode + RFM95_RADIO_MODE_STDBY = 4, //!< STDBY mode + RFM95_RADIO_MODE_SMART_RX = 5, //!< SMART RX mode + RFM95_RADIO_MODE_SMART_TX = 6 //!< SMART TX mode + +} rfm95_radioMode_t; + +/** +* @brief RFM95 modem config registers +*/ +typedef struct { + uint8_t reg_1d; //!< Value for register REG_1D_MODEM_CONFIG1 + uint8_t reg_1e; //!< Value for register REG_1E_MODEM_CONFIG2 + uint8_t reg_26; //!< Value for register REG_26_MODEM_CONFIG3 +} rfm95_modemConfig_t; + +/** +* @brief Sequence number data type +*/ +typedef uint8_t rfm95_rfm69_sequenceNumber_t; // changed, not compatible with lora +/** +* @brief RSSI data type +*/ +typedef uint8_t rfm95_RSSI_t; +/** +* @brief Control flag data type +*/ +typedef uint8_t rfm95_controlFlags_t; +/** +* @brief Power level in dBm +*/ +typedef int8_t rfm95_powerLevel_t; +/** +* @brief RFM95 LoRa header +*/ +typedef struct { + uint8_t packetLen; //!< packet length + uint8_t recipient; //!< Payload recipient + uint8_t version; //!< Header version + uint8_t sender; //!< Payload sender + rfm95_controlFlags_t controlFlags; //!< Control flags, used for ACK + rfm95_rfm69_sequenceNumber_t sequenceNumber; //!< Packet sequence number, used for ACK +} __attribute__((packed)) rfm95_header_t; + +/** +* @brief RFM95 LoRa ACK packet structure +*/ +typedef struct { + rfm95_rfm69_sequenceNumber_t sequenceNumber; //!< sequence number + rfm95_RSSI_t RSSI; //!< RSSI +} __attribute__((packed)) rfm95_ack_t; + + +#define RFM95_HEADER_LEN sizeof(rfm95_header_t) //!< Size header inside LoRa payload +#define RFM95_MAX_PAYLOAD_LEN (RFM95_MAX_PACKET_LEN - RFM95_HEADER_LEN) //!< Max payload length + +/** +* @brief LoRa packet structure +*/ +typedef struct { + union { + struct { + rfm95_header_t header; //!< FSK/OOK header + union { + uint8_t payload[RFM95_MAX_PAYLOAD_LEN]; //!< Payload, i.e. MySensors message + rfm95_ack_t ACK; //!< Union: ACK + }; + }; + uint8_t data[RFM95_MAX_PACKET_LEN]; //!< RAW + }; + uint8_t payloadLen; //!< Length of payload (excluding header) + rfm95_RSSI_t RSSI; //!< RSSI of current packet, RSSI = value - 137 +} __attribute__((packed)) rfm95_packet_t; + +/** +* @brief RFM69 last packet info +*/ +typedef struct { + rfm95_rfm69_sequenceNumber_t sequenceNumber; //!< sequenceNumber + uint8_t sender; //!< sender +} rfm95_last_packet_t; + +/** +* @brief RFM95 internal variables +*/ +typedef struct { + uint8_t address; //!< Node address + rfm95_packet_t currentPacket; //!< Buffer for current packet + rfm95_rfm69_sequenceNumber_t txSequenceNumber; //!< RFM95_txSequenceNumber + rfm95_powerLevel_t powerLevel; //!< TX power level dBm + rfm95_RSSI_t ATCtargetRSSI; //!< ATC: target RSSI + rfm95_last_packet_t lastPacket; //!< Last packet + // 8 bit + rfm95_radioMode_t radioMode : 3; //!< current transceiver state + bool channelActive : 1; //!< RFM95_cad + bool ATCenabled : 1; //!< ATC enabled + bool ackReceived : 1; //!< ACK received + bool dataReceived : 1; //!< Data received + bool reserved : 1; //!< unused +} rfm95_internal_t; + +#define LOCAL static //!< static + +/** +* @brief Initialise the driver transport hardware and software +* @param frequencyHz Transmitter frequency in Hz +* @return True if initialisation succeeded +*/ +LOCAL bool RFM95_initialise(const uint32_t frequencyHz); +/** +* @brief Set the driver/node address +* @param addr +*/ +LOCAL void RFM95_setAddress(const uint8_t addr); +/** +* @brief Get driver/node address +* @return Node address +*/ +LOCAL uint8_t RFM95_getAddress(void); +/** +* @brief Tests whether a new message is available +* @return True if a new, complete, error-free uncollected message is available to be retreived by @ref RFM95_receive() +*/ +LOCAL bool RFM95_available(void); +/** +* @brief If a valid message is received, copy it to buf and return length. 0 byte messages are permitted. +* @param buf Location to copy the received message +* @param maxBufSize Max buffer size +* @return Number of bytes +*/ +LOCAL uint8_t RFM95_receive(uint8_t *buf, const uint8_t maxBufSize); +/** +* @brief RFM95_send +* @param packet +* @return True if packet sent +*/ +LOCAL bool RFM95_send(const rfm95_packet_t *packet); + +/** +* @brief Sets the transmitter and receiver centre frequency +* @param frequencyHz Frequency in Hz +*/ +LOCAL void RFM95_setFrequency(const uint32_t frequencyHz); +/** +* @brief Sets the transmitter power output level, and configures the transmitter pin +* @param newPowerLevel Transmitter power level in dBm (+5 to +23) +* @return True power level adjusted +*/ +LOCAL bool RFM95_setTxPowerLevel(rfm95_powerLevel_t newPowerLevel); + +/** +* @brief Sets the transmitter power output percent. +* @param newPowerPercent Transmitter power level in percent +* @return True power level adjusted +*/ +LOCAL bool RFM95_setTxPowerPercent(const uint8_t newPowerPercent); + +/** +* @brief Enable TCXO mode +* Call this immediately after init(), to force your radio to use an external +* frequency source, such as a Temperature Compensated Crystal Oscillator (TCXO). +* See the comments in the main documentation about the sensitivity of this radio to +* clock frequency especially when using narrow bandwidths. +* @note Has to be called while radio is in sleep mode. +*/ +LOCAL void RFM95_enableTCXO(void) __attribute__((unused)); + +/** +* @brief Sets the radio into low-power sleep mode +* @return true if sleep mode was successfully entered +*/ +LOCAL bool RFM95_sleep(void); +/** +* @brief Sets the radio into standby mode +* @return true if standby mode was successfully entered +*/ +LOCAL bool RFM95_standBy(void); +/** +* @brief Powerdown radio, if RFM95_POWER_PIN defined +*/ +LOCAL void RFM95_powerDown(void); +/** +* @brief Powerup radio, if RFM95_POWER_PIN defined +*/ +LOCAL void RFM95_powerUp(void); +/** +* @brief RFM95_sendACK +* @param recipient +* @param sequenceNumber +* @param RSSI (rfm95_RSSI_t) +*/ +LOCAL void RFM95_sendACK(const uint8_t recipient, const rfm95_rfm69_sequenceNumber_t sequenceNumber, + const rfm95_RSSI_t RSSI); +/** +* @brief RFM95_sendWithRetry +* @param recipient +* @param buffer +* @param bufferSize +* @param noACK +* @return True if packet successfully sent +*/ +LOCAL bool RFM95_sendWithRetry(const uint8_t recipient, const void *buffer, + const uint8_t bufferSize, const bool noACK); +/** +* @brief Report channel activity +* @return True if no channel activity detected, False if timeout occured +*/ +LOCAL bool RFM95_channelFree(void); + +/** +* @brief RFM95_setRadioMode +* @param newRadioMode +* @return True if mode changed +*/ +LOCAL bool RFM95_setRadioMode(const rfm95_radioMode_t newRadioMode); +/** +* @brief Low level interrupt handler +*/ +LOCAL void RFM95_interruptHandler(void); + +/** +* @brief Packet engine +*/ +LOCAL void RFM95_RXFIFOHandling(void); +/** +* @brief RFM95_getSendingRSSI +* @return RSSI Signal strength of last packet received +*/ +LOCAL int16_t RFM95_getReceivingRSSI(void); +/** +* @brief RFM95_getSendingRSSI +* @return RSSI Signal strength of last packet sent (if ACK and ATC enabled) +*/ +LOCAL int16_t RFM95_getSendingRSSI(void); +/** +* @brief RFM95_getReceivingSNR +* @return SNR +*/ +LOCAL int16_t RFM95_getReceivingSNR(void); +/** +* @brief RFM95_getSendingSNR +* @return SNR of last packet sent (if ACK and ATC enabled) +*/ +LOCAL int16_t RFM95_getSendingSNR(void); +/** +* @brief Get transmitter power level +* @return Transmitter power level in percents +*/ +LOCAL uint8_t RFM95_getTxPowerPercent(void); +/** +* @brief Get transmitter power level +* @return Transmitter power level in dBm +*/ +LOCAL uint8_t RFM95_getTxPowerLevel(void); +/** +* @brief RFM_executeATC +* @param currentRSSI +* @param targetRSSI +* @return True if power level adjusted +*/ +LOCAL bool RFM95_executeATC(const rfm95_RSSI_t currentRSSI, const rfm95_RSSI_t targetRSSI); +/** +* @brief RFM95_ATCmode +* @param targetRSSI Target RSSI for transmitter (default -60) +* @param OnOff True to enable ATC +*/ +LOCAL void RFM95_ATCmode(const bool OnOff, const int16_t targetRSSI = RFM95_TARGET_RSSI); +/** +* @brief RFM95_sanityCheck +* @return True if sanity check passed +*/ +LOCAL bool RFM95_sanityCheck(void); + +#endif + +/** @}*/ diff --git a/hal/transport/RFM95/driver/RFM95registers.h b/hal/transport/RFM95/driver/RFM95registers.h index a54a89d0f..ac7f5719c 100644 --- a/hal/transport/RFM95/driver/RFM95registers.h +++ b/hal/transport/RFM95/driver/RFM95registers.h @@ -19,83 +19,504 @@ * Based on Mike McCauley's RFM95 library, Copyright (C) 2014 Mike McCauley * Radiohead http://www.airspayce.com/mikem/arduino/RadioHead/index.html * - * RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2018 Olivier Mauti + * RFM95 driver refactored and optimized for MySensors, Copyright (C) 2017-2019 Olivier Mauti + * RFM69 compatibility mode, Copyright (C) 2019 Olivier Mauti * * Definitions for HopeRF LoRa radios: - * http://www.hoperf.com/upload/rf/RFM95_96_97_98W.pdf + * https://www.hoperf.com/data/upload/portal/20190611/RFM95W-V1.1.pdf * */ // Register access -#define RFM95_READ_REGISTER (0x7Fu) //!< reading register -#define RFM95_WRITE_REGISTER (0x80u) //!< writing register -#define RFM95_NOP (0x00u) //!< NOP - -// Registers, available in LoRa mode -#define RFM95_REG_00_FIFO 0x00 //!< REG_00_FIFO -#define RFM95_REG_01_OP_MODE 0x01 //!< REG_01_OP_MODE -#define RFM95_REG_02_RESERVED 0x02 //!< REG_02_RESERVED -#define RFM95_REG_03_RESERVED 0x03 //!< REG_03_RESERVED -#define RFM95_REG_04_RESERVED 0x04 //!< REG_04_RESERVED -#define RFM95_REG_05_RESERVED 0x05 //!< REG_05_RESERVED -#define RFM95_REG_06_FRF_MSB 0x06 //!< REG_06_FRF_MSB -#define RFM95_REG_07_FRF_MID 0x07 //!< REG_07_FRF_MID -#define RFM95_REG_08_FRF_LSB 0x08 //!< REG_08_FRF_LSB -#define RFM95_REG_09_PA_CONFIG 0x09 //!< REG_09_PA_CONFIG -#define RFM95_REG_0A_PA_RAMP 0x0a //!< REG_0A_PA_RAMP -#define RFM95_REG_0B_OCP 0x0b //!< REG_0B_OCP -#define RFM95_REG_0C_LNA 0x0c //!< REG_0C_LNA +#define RFM95_READ_REGISTER 0x7f //!< reading register +#define RFM95_WRITE_REGISTER 0x80 //!< writing register +#define RFM95_NOP 0x00 //!< NOP + +// Common registers, OOK / FSK & LORA modes +#define RFM95_REG_00_FIFO 0x00 //!< REG_00_FIFO +#define RFM95_REG_01_OP_MODE 0x01 //!< REG_01_OP_MODE +#define RFM95_REG_06_FRF_MSB 0x06 //!< REG_06_FRF_MSB +#define RFM95_REG_07_FRF_MID 0x07 //!< REG_07_FRF_MID +#define RFM95_REG_08_FRF_LSB 0x08 //!< REG_08_FRF_LSB +#define RFM95_REG_09_PA_CONFIG 0x09 //!< REG_09_PA_CONFIG +#define RFM95_REG_0A_PA_RAMP 0x0a //!< REG_0A_PA_RAMP +#define RFM95_REG_0B_OCP 0x0b //!< REG_0B_OCP +#define RFM95_REG_0C_LNA 0x0c //!< REG_0C_LNA +#define RFM95_REG_40_DIO_MAPPING1 0x40 //!< REG_40_DIO_MAPPING1 +#define RFM95_REG_41_DIO_MAPPING2 0x41 //!< REG_41_DIO_MAPPING2 +#define RFM95_REG_42_VERSION 0x42 //!< REG_42_VERSION +#define RFM95_REG_4B_TCXO 0x4b //!< REG_4B_TCXO +#define RFM95_REG_4D_PA_DAC 0x4d //!< REG_4D_PA_DAC +#define RFM95_REG_5B_FORMER_TEMP 0x5b //!< REG_5B_FORMER_TEMP +#define RFM95_REG_61_AGC_REF 0x61 //!< REG_61_AGC_REF +#define RFM95_REG_62_AGC_THRESH1 0x62 //!< REG_62_AGC_THRESH1 +#define RFM95_REG_63_AGC_THRESH2 0x63 //!< REG_63_AGC_THRESH2 +#define RFM95_REG_64_AGC_THRESH3 0x64 //!< REG_64_AGC_THRESH3 + +// FSK / OOK mode registers +#define RFM95_REG_02_BITRATE_MSB 0x02 //!< RFM95_REG_02_BITRATE_MSB +#define RFM95_REG_03_BITRATE_LSB 0x03 //!< RFM95_REG_03_BITRATE_LSB +#define RFM95_REG_04_FDEV_MSB 0x04 //!< RFM95_REG_04_FDEV_MSB +#define RFM95_REG_05_FDEV_LSB 0x05 //!< RFM95_REG_05_FDEV_LSB +#define RFM95_REG_0D_RX_CONFIG 0x0d //!< RFM95_REG_0D_RX_CONFIG +#define RFM95_REG_0E_RSSI_CONFIG 0x0e //!< RFM95_REG_0E_RSSI_CONFIG +#define RFM95_REG_0F_RSSI_COLLISION 0x0f //!< RFM95_REG_0F_RSSI_COLLISION +#define RFM95_REG_10_RSSI_THRESHOLD 0x10 //!< RFM95_REG_10_RSSI_THRESHOLD +#define RFM95_REG_11_RSSI_VALUE 0x11 //!< REG_11_RSSI_VALUE +#define RFM95_REG_12_RXBW 0x12 //!< RFM95_REG_12_RXBW +#define RFM95_REG_13_AFCBW 0x13 //!< RFM95_REG_13_AFCBW +#define RFM95_REG_14_OOK_PEAK 0x14 //!< RFM95_REG_14_OOK_PEAK +#define RFM95_REG_15_OOK_FIX 0x15 //!< RFM95_REG_15_OOK_FIX +#define RFM95_REG_16_OOK_AVG 0x16 //!< RFM95_REG_16_OOK_AVG +#define RFM95_REG_1A_AFCFEI 0x1a //!< RFM95_REG_1A_AFCFEI +#define RFM95_REG_1B_AFC_MSB 0x1b //!< RFM95_REG_1B_AFC_MSB +#define RFM95_REG_1C_AFC_LSB 0x1c //!< RFM95_REG_1C_AFC_LSB +#define RFM95_REG_1D_FEI_MSB 0x1d //!< RFM95_REG_1D_FEI_MSB +#define RFM95_REG_1E_FEI_LSB 0x1e //!< RFM95_REG_1E_FEI_LSB +#define RFM95_REG_1F_PREAMBLEDETECT 0x1f //!< REG_1F_SYMB_TIMEOUT_LSB +#define RFM95_REG_20_RX_TIMEOUT1 0x20 //!< RFM95_REG_20_RX_TIMEOUT1 +#define RFM95_REG_21_RX_TIMEOUT2 0x21 //!< RFM95_REG_21_RX_TIMEOUT2 +#define RFM95_REG_22_RX_TIMEOUT3 0x22 //!< RFM95_REG_22_RX_TIMEOUT3 +#define RFM95_REG_23_RX_DELAY 0x23 //!< RFM95_REG_23_RX_DELAY +#define RFM95_REG_24_OSC 0x24 //!< RFM95_REG_24_OSC +#define RFM95_REG_25_PREAMBLE_MSB 0x25 //!< RFM95_REG_25_PREAMBLE_MSB +#define RFM95_REG_26_PREAMBLE_LSB 0x26 //!< RFM95_REG_26_PREAMBLE_LSB +#define RFM95_REG_27_SYNC_CONFIG 0x27 //!< REG_27_SYNC_CONFIG +#define RFM95_REG_28_SYNC_VALUE1 0x28 //!< RFM95_REG_28_SYNC_VALUE1 +#define RFM95_REG_29_SYNC_VALUE2 0x29 //!< RFM95_REG_29_SYNC_VALUE2 +#define RFM95_REG_2A_SYNC_VALUE3 0x2A //!< RFM95_REG_2A_SYNC_VALUE3 +#define RFM95_REG_2B_SYNC_VALUE4 0x2B //!< RFM95_REG_2B_SYNC_VALUE4 +#define RFM95_REG_2C_SYNC_VALUE5 0x2C //!< RFM95_REG_2C_SYNC_VALUE5 +#define RFM95_REG_2D_SYNC_VALUE6 0x2D //!< RFM95_REG_2D_SYNC_VALUE6 +#define RFM95_REG_2E_SYNC_VALUE7 0x2E //!< RFM95_REG_2E_SYNC_VALUE7 +#define RFM95_REG_2F_SYNC_VALUE8 0x2F //!< RFM95_REG_2F_SYNC_VALUE8 +#define RFM95_REG_30_PACKET_CONFIG1 0x30 //!< RFM95_REG_30_PACKET_CONFIG1 +#define RFM95_REG_31_PACKET_CONFIG2 0x31 //!< RFM95_REG_30_PACKET_CONFIG2 +#define RFM95_REG_32_PAYLOAD_LENGTH 0x33 //!< RFM95_REG_32_PAYLOAD_LENGTH +#define RFM95_REG_33_NODE_ADDR 0x33 //!< RFM95_REG_33_NODE_ADDR +#define RFM95_REG_34_BROADCAST_ADDR 0x34 //!< RFM95_REG_34_BROADCAST_ADDR +#define RFM95_REG_35_FIFO_THRESHOLD 0x35 //!< RFM95_REG_35_FIFO_THRESHOLD +#define RFM95_REG_36_SEQ_CONFIG1 0x36 //!< RFM95_REG_36_SEQ_CONFIG1 +#define RFM95_REG_37_SEQ_CONFIG2 0x37 //!< RFM95_REG_37_SEQ_CONFIG2 +#define RFM95_REG_38_TIMER_RESOL 0x38 //!< RFM95_REG_38_TIMER_RESOL +#define RFM95_REG_39_TIMER1_COEF 0x39 //!< RFM95_REG_39_TIMER1_COEF +#define RFM95_REG_3A_TIMER2_COEF 0x3A //!< RFM95_REG_3A_TIMER2_COEF +#define RFM95_REG_3B_IMAGECAL 0x3B //!< RFM95_REG_3B_IMAGECAL +#define RFM95_REG_3C_TEMP 0x3C //!< RFM95_REG_3C_TEMP +#define RFM95_REG_3D_LOW_BAT 0x3D //!< RFM95_REG_3D_LOW_BAT +#define RFM95_REG_3E_IRQ_FLAGS1 0x3E //!< RFM95_REG_3E_IRQ_FLAGS1 +#define RFM95_REG_3F_IRQ_FLAGS2 0x3F //!< RFM95_REG_3F_IRQ_FLAGS2 +#define RFM95_REG_44_PLL_HOP 0x44 //!< RFM95_REG_44_PLL_HOP +#define RFM95_REG_5D_BIT_RATE_FRAC 0x5D //!< RFM95_REG_5D_BIT_RATE_FRAC + +// LORA mode registers #define RFM95_REG_0D_FIFO_ADDR_PTR 0x0d //!< REG_0D_FIFO_ADDR_PTR -#define RFM95_REG_0E_FIFO_TX_BASE_ADDR 0x0e //!< REG_0E_FIFO_TX_BASE_ADDR -#define RFM95_REG_0F_FIFO_RX_BASE_ADDR 0x0f //!< REG_0F_FIFO_RX_BASE_ADDR -#define RFM95_REG_10_FIFO_RX_CURRENT_ADDR 0x10 //!< REG_10_FIFO_RX_CURRENT_ADDR +#define RFM95_REG_0E_FIFO_TX_BASE_ADDR 0x0e //!< REG_0E_FIFO_TX_BASE_ADDR +#define RFM95_REG_0F_FIFO_RX_BASE_ADDR 0x0f //!< REG_0F_FIFO_RX_BASE_ADDR +#define RFM95_REG_10_FIFO_RX_CURRENT_ADDR 0x10 //!< REG_10_FIFO_RX_CURRENT_ADDR #define RFM95_REG_11_IRQ_FLAGS_MASK 0x11 //!< REG_11_IRQ_FLAGS_MASK -#define RFM95_REG_12_IRQ_FLAGS 0x12 //!< REG_12_IRQ_FLAGS -#define RFM95_REG_13_RX_NB_BYTES 0x13 //!< REG_13_RX_NB_BYTES +#define RFM95_REG_12_IRQ_FLAGS 0x12 //!< REG_12_IRQ_FLAGS +#define RFM95_REG_13_RX_NB_BYTES 0x13 //!< REG_13_RX_NB_BYTES #define RFM95_REG_14_RX_HEADER_CNT_VALUE_MSB 0x14 //!< REG_14_RX_HEADER_CNT_VALUE_MSB #define RFM95_REG_15_RX_HEADER_CNT_VALUE_LSB 0x15 //!< REG_15_RX_HEADER_CNT_VALUE_LSB #define RFM95_REG_16_RX_PACKET_CNT_VALUE_MSB 0x16 //!< REG_16_RX_PACKET_CNT_VALUE_MSB #define RFM95_REG_17_RX_PACKET_CNT_VALUE_LSB 0x17 //!< REG_17_RX_PACKET_CNT_VALUE_LSB -#define RFM95_REG_18_MODEM_STAT 0x18 //!< REG_18_MODEM_STAT +#define RFM95_REG_18_MODEM_STAT 0x18 //!< REG_18_MODEM_STAT #define RFM95_REG_19_PKT_SNR_VALUE 0x19 //!< REG_19_PKT_SNR_VALUE #define RFM95_REG_1A_PKT_RSSI_VALUE 0x1a //!< REG_1A_PKT_RSSI_VALUE -#define RFM95_REG_1B_RSSI_VALUE 0x1b //!< REG_1B_RSSI_VALUE -#define RFM95_REG_1C_HOP_CHANNEL 0x1c //!< REG_1C_HOP_CHANNEL +#define RFM95_REG_1B_RSSI_VALUE 0x1b //!< REG_1B_RSSI_VALUE +#define RFM95_REG_1C_HOP_CHANNEL 0x1c //!< REG_1C_HOP_CHANNEL #define RFM95_REG_1D_MODEM_CONFIG1 0x1d //!< REG_1D_MODEM_CONFIG1 #define RFM95_REG_1E_MODEM_CONFIG2 0x1e //!< REG_1E_MODEM_CONFIG2 #define RFM95_REG_1F_SYMB_TIMEOUT_LSB 0x1f //!< REG_1F_SYMB_TIMEOUT_LSB -#define RFM95_REG_20_PREAMBLE_MSB 0x20 //!< REG_20_PREAMBLE_MSB -#define RFM95_REG_21_PREAMBLE_LSB 0x21 //!< REG_21_PREAMBLE_LSB +#define RFM95_REG_20_PREAMBLE_MSB 0x20 //!< REG_20_PREAMBLE_MSB +#define RFM95_REG_21_PREAMBLE_LSB 0x21 //!< REG_21_PREAMBLE_LSB #define RFM95_REG_22_PAYLOAD_LENGTH 0x22 //!< REG_22_PAYLOAD_LENGTH -#define RFM95_REG_23_MAX_PAYLOAD_LENGTH 0x23 //!< REG_23_MAX_PAYLOAD_LENGTH -#define RFM95_REG_24_HOP_PERIOD 0x24 //!< REG_24_HOP_PERIOD -#define RFM95_REG_25_FIFO_RX_BYTE_ADDR 0x25 //!< REG_25_FIFO_RX_BYTE_ADDR +#define RFM95_REG_23_MAX_PAYLOAD_LENGTH 0x23 //!< REG_23_MAX_PAYLOAD_LENGTH +#define RFM95_REG_24_HOP_PERIOD 0x24 //!< REG_24_HOP_PERIOD +#define RFM95_REG_25_FIFO_RX_BYTE_ADDR 0x25 //!< REG_25_FIFO_RX_BYTE_ADDR #define RFM95_REG_26_MODEM_CONFIG3 0x26 //!< REG_26_MODEM_CONFIG3 -// Reserved when in LoRa mode -#define RFM95_REG_40_DIO_MAPPING1 0x40 //!< REG_40_DIO_MAPPING1 -#define RFM95_REG_41_DIO_MAPPING2 0x41 //!< REG_41_DIO_MAPPING2 -#define RFM95_REG_42_VERSION 0x42 //!< REG_42_VERSION -#define RFM95_REG_4B_TCXO 0x4b //!< REG_4B_TCXO -#define RFM95_REG_4D_PA_DAC 0x4d //!< REG_4D_PA_DAC -#define RFM95_REG_5B_FORMER_TEMP 0x5b //!< REG_5B_FORMER_TEMP -#define RFM95_REG_61_AGC_REF 0x61 //!< REG_61_AGC_REF -#define RFM95_REG_62_AGC_THRESH1 0x62 //!< REG_62_AGC_THRESH1 -#define RFM95_REG_63_AGC_THRESH2 0x63 //!< REG_63_AGC_THRESH2 -#define RFM95_REG_64_AGC_THRESH3 0x64 //!< REG_64_AGC_THRESH3 - -// RFM95_REG_01_OP_MODE 0x01 -#define RFM95_LONG_RANGE_MODE 0x80 //!< LONG_RANGE_MODE -#define RFM95_ACCESS_SHARED_REG 0x40 //!< ACCESS_SHARED_REG - -#define RFM95_MODE_SLEEP 0x00 //!< MODE_SLEEP -#define RFM95_MODE_STDBY 0x01 //!< MODE_STDBY -#define RFM95_MODE_FSTX 0x02 //!< MODE_FSTX -#define RFM95_MODE_TX 0x03 //!< MODE_TX -#define RFM95_MODE_FSRX 0x04 //!< MODE_FSRX -#define RFM95_MODE_RXCONTINUOUS 0x05 //!< MODE_RXCONTINUOUS -#define RFM95_MODE_RXSINGLE 0x06 //!< MODE_RXSINGLE -#define RFM95_MODE_CAD 0x07 //!< MODE_CAD + +// RFM95_REG_01_OP_MODE +#define RFM95_MODULATION_FSK 0x00 //!< RFM95_MODULATION_FSK +#define RFM95_MODULATION_OOK 0x20 //!< RFM95_MODULATION_OOK + +#define RFM95_LORA_MODE 0x80 //!< RFM95_LORA_MODE +#define RFM95_FSK_OOK_MODE 0x00 //!< RFM95_FSK_OOK_MODE + +#define RFM95_HIGH_FREQUENCY_REG 0x00 //!< RFM95_HIGH_FREQUENCY_REG +#define RFM95_LOW_FREQUENCY_REG 0x08 //!< RFM95_LOW_FREQUENCY_REG + +#define RFM95_MODE_SLEEP 0x00 //!< RFM95_MODE_SLEEP +#define RFM95_MODE_STDBY 0x01 //!< RFM95_MODE_STDBY +#define RFM95_MODE_SYNTHESIZER_TX 0x02 //!< RFM95_MODE_SYNTHESIZER_TX +#define RFM95_MODE_TX 0x03 //!< RFM95_MODE_TX +#define RFM95_MODE_SYNTHESIZER_RX 0x04 //!< RFM95_MODE_SYNTHESIZER_RX +#define RFM95_MODE_RX 0x05 //!< RFM95_MODE_RX +#define RFM95_MODE_RXSINGLE 0x06 //!< RFM95_MODE_RXSINGLE +#define RFM95_MODE_CAD 0x07 //!< RFM95_MODE_CAD + +// REG_02_BIT_RATE_MSB +#define RFM95_BITRATEMSB_55555 0x02 + +// REG_03_BIT_RATE_LSB +#define RFM95_BITRATELSB_55555 0x40 + +// REG_04_FDEV_MSB +#define RFM95_FDEVMSB_50000 0x03 + +// REG_04_FDEV_LSB +#define RFM95_FDEVLSB_50000 0x33 + +// RFM95_REG_0C_LNA +#define RFM95_LNA_GAIN_MASK 0x1F +#define RFM95_LNA_GAIN_G1 0x20 // Default +#define RFM95_LNA_GAIN_G2 0x40 +#define RFM95_LNA_GAIN_G3 0x60 +#define RFM95_LNA_GAIN_G4 0x80 +#define RFM95_LNA_GAIN_G5 0xA0 +#define RFM95_LNA_GAIN_G6 0xC0 + +#define RFM95_LNA_BOOST_MASK 0xFC +#define RFM95_LNA_BOOST_OFF 0x00 // Default +#define RFM95_LNA_BOOST_ON 0x03 + +// RFM95_REG_1D_RX_CONFIG +#define RFM95_RX_CONFIG_RESTARTRX_ON_COLLISION_ON 0x80 +#define RFM95_RX_CONFIG_RESTARTRX_ON_COLLISION_OFF 0x00 // Default +#define RFM95_RX_CONFIG_RESTARTRX_WITHOUT_PLLLOCK 0x40 // Write only +#define RFM95_RX_CONFIG_RESTARTRX_WITH_PLLLOCK 0x20 // Write only +#define RFM95_RX_CONFIG_AFCAUTO_ON 0x10 +#define RFM95_RX_CONFIG_AFCAUTO_OFF 0x00 // Default +#define RFM95_RX_CONFIG_AGCAUTO_ON 0x08 // Default +#define RFM95_RX_CONFIG_AGCAUTO_OFF 0x00 +#define RFM95_RX_CONFIG_RXTRIGGER_OFF 0x00 +#define RFM95_RX_CONFIG_RXTRIGGER_RSSI 0x01 +#define RFM95_RX_CONFIG_RXTRIGGER_PREAMBLEDETECT 0x06 // Default +#define RFM95_RX_CONFIG_RXTRIGGER_RSSI_PREAMBLEDETECT 0x07 + +// RFM95_REG_1E_RSSI_CONFIG +#define RFM95_RSSI_CONFIG_OFFSET_MASK 0x07 +#define RFM95_RSSI_CONFIG_OFFSET_P_00_DB 0x00 // Default +#define RFM95_RSSI_CONFIG_OFFSET_P_01_DB 0x08 +#define RFM95_RSSI_CONFIG_OFFSET_P_02_DB 0x10 +#define RFM95_RSSI_CONFIG_OFFSET_P_03_DB 0x18 +#define RFM95_RSSI_CONFIG_OFFSET_P_04_DB 0x20 +#define RFM95_RSSI_CONFIG_OFFSET_P_05_DB 0x28 +#define RFM95_RSSI_CONFIG_OFFSET_P_06_DB 0x30 +#define RFM95_RSSI_CONFIG_OFFSET_P_07_DB 0x38 +#define RFM95_RSSI_CONFIG_OFFSET_P_08_DB 0x40 +#define RFM95_RSSI_CONFIG_OFFSET_P_09_DB 0x48 +#define RFM95_RSSI_CONFIG_OFFSET_P_10_DB 0x50 +#define RFM95_RSSI_CONFIG_OFFSET_P_11_DB 0x58 +#define RFM95_RSSI_CONFIG_OFFSET_P_12_DB 0x60 +#define RFM95_RSSI_CONFIG_OFFSET_P_13_DB 0x68 +#define RFM95_RSSI_CONFIG_OFFSET_P_14_DB 0x70 +#define RFM95_RSSI_CONFIG_OFFSET_P_15_DB 0x78 +#define RFM95_RSSI_CONFIG_OFFSET_M_16_DB 0x80 +#define RFM95_RSSI_CONFIG_OFFSET_M_15_DB 0x88 +#define RFM95_RSSI_CONFIG_OFFSET_M_14_DB 0x90 +#define RFM95_RSSI_CONFIG_OFFSET_M_13_DB 0x98 +#define RFM95_RSSI_CONFIG_OFFSET_M_12_DB 0xA0 +#define RFM95_RSSI_CONFIG_OFFSET_M_11_DB 0xA8 +#define RFM95_RSSI_CONFIG_OFFSET_M_10_DB 0xB0 +#define RFM95_RSSI_CONFIG_OFFSET_M_09_DB 0xB8 +#define RFM95_RSSI_CONFIG_OFFSET_M_08_DB 0xC0 +#define RFM95_RSSI_CONFIG_OFFSET_M_07_DB 0xC8 +#define RFM95_RSSI_CONFIG_OFFSET_M_06_DB 0xD0 +#define RFM95_RSSI_CONFIG_OFFSET_M_05_DB 0xD8 +#define RFM95_RSSI_CONFIG_OFFSET_M_04_DB 0xE0 +#define RFM95_RSSI_CONFIG_OFFSET_M_03_DB 0xE8 +#define RFM95_RSSI_CONFIG_OFFSET_M_02_DB 0xF0 +#define RFM95_RSSI_CONFIG_OFFSET_M_01_DB 0xF8 + +#define RFM95_RSSI_CONFIG_SMOOTHING_MASK 0xF8 +#define RFM95_RSSI_CONFIG_SMOOTHING_2 0x00 +#define RFM95_RSSI_CONFIG_SMOOTHING_4 0x01 +#define RFM95_RSSI_CONFIG_SMOOTHING_8 0x02 // Default +#define RFM95_RSSI_CONFIG_SMOOTHING_16 0x03 +#define RFM95_RSSI_CONFIG_SMOOTHING_32 0x04 +#define RFM95_RSSI_CONFIG_SMOOTHING_64 0x05 +#define RFM95_RSSI_CONFIG_SMOOTHING_128 0x06 +#define RFM95_RSSI_CONFIG_SMOOTHING_256 0x07 + +// RFM95_REG_1A_AFCFEI +#define RFM95_AFCFEI_AGCSTART 0x10 +#define RFM95_AFCFEI_AFCCLEAR 0x02 +#define RFM95_AFCFEI_AFCAUTOCLEAR_ON 0x01 +#define RFM95_AFCFEI_AFCAUTOCLEAR_OFF 0x00 // Default + +// RFM95_REG_1E_MODEM_CONFIG2 +#define RFM95_SPREADING_FACTOR_64CPS 0x60 //!< SPREADING_FACTOR_64CPS, SF6 +#define RFM95_SPREADING_FACTOR_128CPS 0x70 //!< SPREADING_FACTOR_128CPS, SF7 +#define RFM95_SPREADING_FACTOR_256CPS 0x80 //!< SPREADING_FACTOR_256CPS, SF8 +#define RFM95_SPREADING_FACTOR_512CPS 0x90 //!< SPREADING_FACTOR_512CPS, SF9 +#define RFM95_SPREADING_FACTOR_1024CPS 0xA0 //!< SPREADING_FACTOR_1024CPS, SF10 +#define RFM95_SPREADING_FACTOR_2048CPS 0xB0 //!< SPREADING_FACTOR_2048CPS, SF11 +#define RFM95_SPREADING_FACTOR_4096CPS 0xC0 //!< SPREADING_FACTOR_4096CPS, SF12 + +#define RFM95_SYM_TIMEOUT_MSB 0x03 //!< SYM_TIMEOUT_MSB +#define RFM95_RX_PAYLOAD_CRC_ON 0x04 //!< RX_PAYLOAD_CRC_ON +#define RFM95_TX_CONTINUOUS_MOdE 0x08 //!< TX_CONTINUOUS_MODE + +// RFM95_REG_24_OSC +#define RFM95_OSC_RCCALSTART 0x08 + +#define RFM95_OSC_CLKOUT_MASK 0xF8 +#define RFM95_OSC_CLKOUT_32_MHZ 0x00 +#define RFM95_OSC_CLKOUT_16_MHZ 0x01 +#define RFM95_OSC_CLKOUT_8_MHZ 0x02 +#define RFM95_OSC_CLKOUT_4_MHZ 0x03 +#define RFM95_OSC_CLKOUT_2_MHZ 0x04 +#define RFM95_OSC_CLKOUT_1_MHZ 0x05 // Default +#define RFM95_OSC_CLKOUT_RC 0x06 +#define RFM95_OSC_CLKOUT_OFF 0x07 + +// RFM95_REG_26_MODEM_CONFIG3 0x26 +#define RFM95_LOW_DATA_RATE_OPTIMIZE 0x08 //!< LOW_DATA_RATE_OPTIMIZE +#define RFM95_AGC_AUTO_ON 0x04 //!< AGC_AUTO_ON + +// RFM95_REG_27_SYNC_CONFIG +#define RFM95_SYNC_AUTORXRESTART_OFF 0x00 //!< RFM95_SYNC_AUTORXRESTART_OFF +#define RFM95_SYNC_AUTORXRESTART_NO_PLL 0x40 //!< RFM95_SYNC_AUTORXRESTART_NO_PLL +#define RFM95_SYNC_AUTORXRESTART_WAIT_PLL 0x80 //!< RFM95_SYNC_AUTORXRESTART_WAIT_PLL + +#define RFM95_SYNC_PREAMBLE_POLARITY_AA 0x00 //!< RFM95_SYNC_PREAMBLE_POLARITY_AA +#define RFM95_SYNC_PREAMBLE_POLARITY_55 0x20 //!< RFM95_SYNC_PREAMBLE_POLARITY_55 + +#define RFM95_SYNC_OFF 0x00 //!< RFM95_SYNC_OFF +#define RFM95_SYNC_ON 0x10 //!< RFM95_SYNC_ON + +#define RFM95_SYNC_FIFO_FILL_AUTO 0x00 //!< RFM95_SYNC_FIFO_FILL_AUTO +#define RFM95_SYNC_FIFO_FILL_MANUAL 0x08 //!< RFM95_SYNC_FIFO_FILL_MANUAL + +#define RFM95_SYNC_SIZE_1 0x00 //!< RFM95_SYNC_SIZE_1 +#define RFM95_SYNC_SIZE_2 0x01 //!< RFM95_SYNC_SIZE_2 +#define RFM95_SYNC_SIZE_3 0x02 //!< RFM95_SYNC_SIZE_3 +#define RFM95_SYNC_SIZE_4 0x03 //!< RFM95_SYNC_SIZE_4 +#define RFM95_SYNC_SIZE_5 0x04 //!< RFM95_SYNC_SIZE_5 +#define RFM95_SYNC_SIZE_6 0x05 //!< RFM95_SYNC_SIZE_6 +#define RFM95_SYNC_SIZE_7 0x06 //!< RFM95_SYNC_SIZE_7 +#define RFM95_SYNC_SIZE_8 0x07 //!< RFM95_SYNC_SIZE_8 + +// RFM95_REG_30_PACKET_CONFIG1 +#define RFM95_PACKET1_FORMAT_FIXED 0x00 //!< +#define RFM95_PACKET1_FORMAT_VARIABLE 0x80 //!< + +#define RFM95_PACKET1_DCFREE_OFF 0x00 //!< +#define RFM95_PACKET1_DCFREE_MANCHESTER 0x20 //!< +#define RFM95_PACKET1_DCFREE_WHITENING 0x40 //!< + +#define RFM95_PACKET1_CRC_OFF 0x00 //!< +#define RFM95_PACKET1_CRC_ON 0x10 //!< + +#define RFM95_PACKET1_CRCAUTOCLEAR_ON 0x00 //!< +#define RFM95_PACKET1_CRCAUTOCLEAR_OFF 0x08 //!< + +#define RFM95_PACKET1_ADRSFILTERING_NONE 0x00 //!< +#define RFM95_PACKET1_ADRSFILTERING_NODE 0x02 //!< +#define RFM95_PACKET1_ADRSFILTERING_NODEBROADCAST 0x04 //!< + +#define RFM95_PACKET1_CRC_WHITENING_CCITT 0x00 //!< +#define RFM95_PACKET1_CRC_WHITENING_IBM 0x01 //!< + + +// RFM95_REG_31_PACKET_CONFIG2 +#define RFM95_PACKET2_DATA_MODE_CONTINUOUS 0x00 //!< +#define RFM95_PACKET2_DATA_MODE_PACKET 0x40 //!< + +#define RFM95_PACKET2_IOHOME_OFF 0x00 //!< +#define RFM95_PACKET2_IOHOME_ON 0x20 //!< + +#define RFM95_PACKET2_IOHOME_POWER_FRAME_OFF 0x00 //!< +#define RFM95_PACKET2_IOHOME_POWER_FRAME_ON 0x10 //!< + +#define RFM95_PACKET2_BEACON_OFF 0x00 //!< +#define RFM95_PACKET2_BEACON_ON 0x08 //!< + +// RFM95_REG_35_FIFO_THRESHOLD +#define RFM95_TXSTART_CONDITION_FIFO_THRESHOLD 0x00 //!< +#define RFM95_TXSTART_CONDITION_FIFO_NOT_EMPTY 0x80 //!< + +// RFM95_REG_36_SEQ_CONFIG1 +#define RFM95_SEQ_CONFIG1_SEQUENCER_START 0x80 //!< + +#define RFM95_SEQ_CONFIG1_SEQUENCER_STOP 0x40 //!< + +#define RFM95_SEQ_CONFIG1_IDLEMODE_MASK 0xDF //!< +#define RFM95_SEQ_CONFIG1_IDLEMODE_SLEEP 0x20 //!< +#define RFM95_SEQ_CONFIG1_IDLEMODE_STANDBY 0x00 //!< Default + +#define RFM95_SEQ_CONFIG1_FROMSTART_MASK 0xE7 //!< +#define RFM95_SEQ_CONFIG1_FROMSTART_TOLPS 0x00 //!< Default +#define RFM95_SEQ_CONFIG1_FROMSTART_TORX 0x08 //!< +#define RFM95_SEQ_CONFIG1_FROMSTART_TOTX 0x10 //!< +#define RFM95_SEQ_CONFIG1_FROMSTART_TOTX_ONFIFOLEVEL 0x18 //!< + +#define RFM95_SEQ_CONFIG1_LPS_MASK 0xFB //!< +#define RFM95_SEQ_CONFIG1_LPS_SEQUENCER_OFF 0x00 //!< Default +#define RFM95_SEQ_CONFIG1_LPS_IDLE 0x04 //!< + +#define RFM95_SEQ_CONFIG1_FROMIDLE_MASK 0xFD //!< +#define RFM95_SEQ_CONFIG1_FROMIDLE_TOTX 0x00 //!< Default +#define RFM95_SEQ_CONFIG1_FROMIDLE_TORX 0x02 //!< + +#define RFM95_SEQ_CONFIG1_FROMTX_MASK 0xFE //!< +#define RFM95_SEQ_CONFIG1_FROMTX_TOLPS 0x00 //!< Default +#define RFM95_SEQ_CONFIG1_FROMTX_TORX 0x01 //!< + +// RFM95_REG_37_SEQ_CONFIG2 +#define RFM95_SEQ_CONFIG2_FROMRX_MASK 0x1F //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOUNUSED_000 0x00 //!< Default +#define RFM95_SEQ_CONFIG2_FROMRX_TORXPKT_ONPLDRDY 0x20 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOLPS_ONPLDRDY 0x40 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TORXPKT_ONCRCOK 0x60 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOSEQUENCEROFF_ONRSSI 0x80 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOSEQUENCEROFF_ONSYNC 0xA0 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOSEQUENCEROFF_ONPREAMBLE 0xC0 //!< +#define RFM95_SEQ_CONFIG2_FROMRX_TOUNUSED_111 0xE0 //!< + +#define RFM95_SEQ_CONFIG2_FROMRXTIMEOUT_MASK 0xE7 //!< +#define RFM95_SEQ_CONFIG2_FROMRXTIMEOUT_TORXRESTART 0x00 //!< Default +#define RFM95_SEQ_CONFIG2_FROMRXTIMEOUT_TOTX 0x08 //!< +#define RFM95_SEQ_CONFIG2_FROMRXTIMEOUT_TOLPS 0x10 //!< +#define RFM95_SEQ_CONFIG2_FROMRXTIMEOUT_TOSEQUENCEROFF 0x18 //!< + +#define RFM95_SEQ_CONFIG2_FROMRXPKT_MASK 0xF8 //!< +#define RFM95_SEQ_CONFIG2_FROMRXPKT_TOSEQUENCEROFF 0x00 //!< Default +#define RFM95_SEQ_CONFIG2_FROMRXPKT_TOTX_ONFIFOEMPTY 0x01 //!< +#define RFM95_SEQ_CONFIG2_FROMRXPKT_TOLPS 0x02 //!< +#define RFM95_SEQ_CONFIG2_FROMRXPKT_TOSYNTHESIZERRX 0x03 //!< +#define RFM95_SEQ_CONFIG2_FROMRXPKT_TORX 0x04 //!< + + +// RFM95_REG_38_TIMER_RESOLUTION +#define RFM95_TIMER1_RESOL_MASK 0xF3 //!< +#define RFM95_TIMER1_RESOL_OFF 0x00 //!< Default +#define RFM95_TIMER1_RESOL_000064_US 0x04 //!< +#define RFM95_TIMER1_RESOL_004100_US 0x08 //!< +#define RFM95_TIMER1_RESOL_262000_US 0x0C //!< + +#define RFM95_TIMER2_RESOL_MASK 0xFC //!< +#define RFM95_TIMER2_RESOL_OFF 0x00 //!< Default +#define RFM95_TIMER2_RESOL_000064_US 0x01 //!< +#define RFM95_TIMER2_RESOL_004100_US 0x02 //!< +#define RFM95_TIMER2_RESOL_262000_US 0x03 //!< + +// RFM95_REG_39_TIMER1_COEFFICIENT +#define RFM95_TIMER1_COEFFICIENT 0xF5 //!< Default + +// RFM95_REG_3A_TIMER2_COEFFICIENT +#define RFM95_TIMER2_COEFFICIENT 0x20 //!< Default + +// RFM95_REG_3B_IMAGECAL +#define RFM95_IMAGECAL_AUTOIMAGECAL_MASK 0x7F +#define RFM95_IMAGECAL_AUTOIMAGECAL_ON 0x80 +#define RFM95_IMAGECAL_AUTOIMAGECAL_OFF 0x00 // Default + +#define RFM95_IMAGECAL_IMAGECAL_MASK 0xBF +#define RFM95_IMAGECAL_IMAGECAL_START 0x40 + +#define RFM95_IMAGECAL_IMAGECAL_RUNNING 0x20 +#define RFM95_IMAGECAL_IMAGECAL_DONE 0x00 // Default + +#define RFM95_IMAGECAL_TEMPCHANGE_HIGHER 0x08 +#define RFM95_IMAGECAL_TEMPCHANGE_LOWER 0x00 + +#define RFM95_IMAGECAL_TEMPTHRESHOLD_MASK 0xF9 +#define RFM95_IMAGECAL_TEMPTHRESHOLD_05 0x00 +#define RFM95_IMAGECAL_TEMPTHRESHOLD_10 0x02 // Default +#define RFM95_IMAGECAL_TEMPTHRESHOLD_15 0x04 +#define RFM95_IMAGECAL_TEMPTHRESHOLD_20 0x06 + +#define RFM95_IMAGECAL_TEMPMONITOR_MASK 0xFE +#define RFM95_IMAGECAL_TEMPMONITOR_ON 0x00 // Default +#define RFM95_IMAGECAL_TEMPMONITOR_OFF 0x01 + + +// RFM95_REG_3E_IRQ_FLAGS1 +#define RFM95_IRQ1_MODE_READY 0x80 //!< RFM95_IRQ1_MODE_READY +#define RFM95_IRQ1_RX_READY 0x40 //!< RFM95_IRQ1_RX_READY +#define RFM95_IRQ1_TX_READY 0x20 //!< RFM95_IRQ1_TX_READY +#define RFM95_IRQ1_PLL_LOCK 0x10 //!< RFM95_IRQ1_PLL_LOCK +#define RFM95_IRQ1_RSSI 0x08 //!< RFM95_IRQ1_RSSI +#define RFM95_IRQ1_TIMEOUT 0x04 //!< RFM95_IRQ1_TIMEOUT +#define RFM95_IRQ1_PREAMBLE_DETECT 0x02 //!< RFM95_IRQ1_PREAMBLE_DETECT +#define RFM95_IRQ1_SYNC_ADDRESS_MATCH 0x01 //!< RFM95_IRQ1_SYNC_ADDRESS_MATCH + +// RFM95_REG_3F_IRQ_FLAGS2 +#define RFM95_IRQ2_FIFO_FULL 0x80 //!< RFM95_IRQ2_FIFO_FULL +#define RFM95_IRQ2_FIFO_EMTPY 0x40 //!< RFM95_IRQ2_FIFO_EMTPY +#define RFM95_IRQ2_FIFO_LEVEL 0x20 //!< RFM95_IRQ2_FIFO_LEVEL +#define RFM95_IRQ2_FIFO_OVERRUN 0x10 //!< RFM95_IRQ2_FIFO_OVERRUN +#define RFM95_IRQ2_PACKET_SENT 0x08 //!< RFM95_IRQ2_PACKET_SENT +#define RFM95_IRQ2_PAYLOAD_READY 0x04 //!< RFM95_IRQ2_PAYLOAD_READY +#define RFM95_IRQ2_CRC_OK 0x02 //!< RFM95_IRQ2_CRC_OK +#define RFM95_IRQ2_LOW_BAT 0x01 //!< RFM95_IRQ2_LOW_BAT + + +// RFM95_REG_40_DIOMAPPING1 +#define RFM95_DIOMAPPING1_DIO0_00 0x00 // Default +#define RFM95_DIOMAPPING1_DIO0_01 0x40 +#define RFM95_DIOMAPPING1_DIO0_10 0x80 +#define RFM95_DIOMAPPING1_DIO0_11 0xC0 + +#define RFM95_DIOMAPPING1_DIO1_MASK 0xCF +#define RFM95_DIOMAPPING1_DIO1_00 0x00 // Default +#define RFM95_DIOMAPPING1_DIO1_01 0x10 +#define RFM95_DIOMAPPING1_DIO1_10 0x20 +#define RFM95_DIOMAPPING1_DIO1_11 0x30 + +#define RFM95_DIOMAPPING1_DIO2_MASK 0xF3 +#define RFM95_DIOMAPPING1_DIO2_00 0x00 // Default +#define RFM95_DIOMAPPING1_DIO2_01 0x04 +#define RFM95_DIOMAPPING1_DIO2_10 0x08 +#define RFM95_DIOMAPPING1_DIO2_11 0x0C + +#define RFM95_DIOMAPPING1_DIO3_MASK 0xFC +#define RFM95_DIOMAPPING1_DIO3_00 0x00 // Default +#define RFM95_DIOMAPPING1_DIO3_01 0x01 +#define RFM95_DIOMAPPING1_DIO3_10 0x02 +#define RFM95_DIOMAPPING1_DIO3_11 0x03 + +// RFM95_REG_41_DIOMAPPING2 +#define RFM95_DIOMAPPING2_DIO4_MASK 0x3F +#define RFM95_DIOMAPPING2_DIO4_00 0x00 // Default +#define RFM95_DIOMAPPING2_DIO4_01 0x40 +#define RFM95_DIOMAPPING2_DIO4_10 0x80 +#define RFM95_DIOMAPPING2_DIO4_11 0xC0 + +#define RFM95_DIOMAPPING2_DIO5_MASK 0xCF +#define RFM95_DIOMAPPING2_DIO5_00 0x00 // Default +#define RFM95_DIOMAPPING2_DIO5_01 0x10 +#define RFM95_DIOMAPPING2_DIO5_10 0x20 +#define RFM95_DIOMAPPING2_DIO5_11 0x30 + +// RFM95_REG_4B_TCXO 0x4b +#define RFM95_TCXO_TCXO_INPUT_ON 0x10 //!< TCXO_TCXO_INPUT_ON + +// RFM95_REG_4D_PA_DAC 0x4d +#define RFM95_PA_DAC_DISABLE 0x04 //!< PA_DAC_DISABLE +#define RFM95_PA_DAC_ENABLE 0x07 //!< PA_DAC_ENABLE + +// REG_25_PREAMBLE_MSB +#define RFM95_PREAMBLESIZE_MSB_VALUE 0x00 // Default + +// REG_26_PREAMBLE_LSB +#define RFM95_PREAMBLESIZE_LSB_VALUE 0x03 // Default + +// REG_12_RXBW +#define RFM95_RXBW_MANT_16 0x00 // 0b00000 +#define RFM95_RXBW_MANT_20 0x08 // 0b01000 +#define RFM95_RXBW_MANT_24 0x10 // 0b10000 +#define RFM95_RXBW_EXP_7 0x07 +#define RFM95_RXBW_EXP_6 0x06 +#define RFM95_RXBW_EXP_5 0x05 +#define RFM95_RXBW_EXP_4 0x04 +#define RFM95_RXBW_EXP_3 0x03 +#define RFM95_RXBW_EXP_2 0x02 +#define RFM95_RXBW_EXP_1 0x01 // RFM95_REG_09_PA_CONFIG 0x09 #define RFM95_OUTPUT_POWER 0x0F //!< OUTPUT_POWER @@ -198,7 +619,7 @@ #define RFM95_AGC_AUTO_ON 0x04 //!< AGC_AUTO_ON // RFM95_REG_4B_TCXO 0x4b -#define RFM95_TCXO_TCXO_INPUT_ON 0x10 //!< TCXO_TCXO_INPUT_ON +#define RFM95_TCXO_INPUT_ON 0x10 //!< RFM95_TCXO_INPUT_ON // RFM95_REG_4D_PA_DAC 0x4d #define RFM95_PA_DAC_DISABLE 0x04 //!< PA_DAC_DISABLE diff --git a/hal/transport/RS485/MyTransportRS485.cpp b/hal/transport/RS485/MyTransportRS485.cpp index fdf963125..9f5d648e1 100644 --- a/hal/transport/RS485/MyTransportRS485.cpp +++ b/hal/transport/RS485/MyTransportRS485.cpp @@ -240,7 +240,7 @@ bool _serialProcess() return true; } -bool transportSend(const uint8_t to, const void* data, const uint8_t len, const bool noACK) +bool RS485_transportSend(const uint8_t to, const void* data, const uint8_t len, const bool noACK) { (void)noACK; // not implemented const char *datap = static_cast(data); @@ -329,7 +329,7 @@ bool transportSend(const uint8_t to, const void* data, const uint8_t len, const -bool transportInit(void) +bool RS485_transportInit(void) { // Reset the state machine _dev.begin(MY_RS485_BAUD_RATE); @@ -345,33 +345,33 @@ bool transportInit(void) return true; } -void transportSetAddress(const uint8_t address) +void RS485_transportSetAddress(const uint8_t address) { _nodeId = address; } -uint8_t transportGetAddress(void) +uint8_t RS485_transportGetAddress(void) { return _nodeId; } -bool transportDataAvailable(void) +bool RS485_transportDataAvailable(void) { _serialProcess(); return _packet_received; } -bool transportSanityCheck(void) +bool RS485_transportSanityCheck(void) { // not implemented yet return true; } -uint8_t transportReceive(void* data) +uint8_t RS485_transportReceive(void* data, const uint8_t maxBufSize) { if (_packet_received) { - memcpy(data,_data,_packet_len); + (void)memcpy(data, (const void *)_data, min(maxBufSize, _packet_len)); _packet_received = false; return _packet_len; } else { @@ -379,63 +379,63 @@ uint8_t transportReceive(void* data) } } -void transportPowerDown(void) +void RS485_transportPowerDown(void) { // Nothing to shut down here } -void transportPowerUp(void) +void RS485_transportPowerUp(void) { // not implemented } -void transportSleep(void) +void RS485_transportSleep(void) { // not implemented } -void transportStandBy(void) +void RS485_transportStandBy(void) { // not implemented } -int16_t transportGetSendingRSSI(void) +int16_t RS485_transportGetSendingRSSI(void) { // not implemented return INVALID_RSSI; } -int16_t transportGetReceivingRSSI(void) +int16_t RS485_transportGetReceivingRSSI(void) { // not implemented return INVALID_RSSI; } -int16_t transportGetSendingSNR(void) +int16_t RS485_transportGetSendingSNR(void) { // not implemented return INVALID_SNR; } -int16_t transportGetReceivingSNR(void) +int16_t RS485_transportGetReceivingSNR(void) { // not implemented return INVALID_SNR; } -int16_t transportGetTxPowerPercent(void) +int16_t RS485_transportGetTxPowerPercent(void) { // not implemented return static_cast(100); } -int16_t transportGetTxPowerLevel(void) +int16_t RS485_transportGetTxPowerLevel(void) { // not implemented return static_cast(100); } -bool transportSetTxPowerPercent(const uint8_t powerPercent) +bool RS485_transportSetTxPowerPercent(const uint8_t powerPercent) { // not possible (void)powerPercent; diff --git a/keywords.txt b/keywords.txt index d63941799..9d0b00393 100755 --- a/keywords.txt +++ b/keywords.txt @@ -51,6 +51,10 @@ MY_SLEEP_HANDLER LITERAL1 AUTO LITERAL1 MY_CORE_COMPATIBILITY_CHECK LITERAL1 MY_DEBUG_VERBOSE_TRANSPORT LITERAL1 +MY_DEBUG_VERBOSE_TRANSPORT_ENCRYPTION LITERAL1 +MY_DEBUG_VERBOSE_TRANSPORT_QUEUE LITERAL1 +MY_TRANSPORT_COUNT LITERAL1 +MY_TRANSPORT_RX_QUEUE LITERAL1 MY_NODE_ID LITERAL1 MY_PARENT_NODE_ID LITERAL1 MY_PARENT_NODE_IS_STATIC LITERAL1 @@ -164,8 +168,22 @@ MY_RF24_IRQ_PIN LITERAL1 MY_RF24_PA_LEVEL LITERAL1 MY_RF24_POWER_PIN LITERAL1 MY_RF24_SPI_SPEED LITERAL1 +MY_RF24_ATC LITERAL1 +MY_RF24_BC_RETRIES LITERAL1 +MY_RF24_USE_INTERRUPTS LITERAL1 # NRF5 +MY_NRF5_CHL_MODE LITERAL1 +MY_NRF5_CHL_PIN LITERAL1 +MY_NRF5_CPS_MODE LITERAL1 +MY_NRF5_CPS_PIN LITERAL1 +MY_NRF5_LNA_ENABLED LITERAL1 +MY_NRF5_LNA_DISABLED LITERAL1 +MY_NRF5_LNA_PIN LITERAL1 +MY_NRF5_PA_DISABLED LITERAL1 +MY_NRF5_PA_ENABLED LITERAL1 +MY_NRF5_PA_LNA LITERAL1 +MY_NRF5_PA_PIN LITERAL1 MY_DEBUG_VERBOSE_NRF5_ESB LITERAL1 MY_NRF5_ESB_ADDR_WIDTH LITERAL1 MY_NRF5_ESB_BASE_RADIO_ID LITERAL1 @@ -197,7 +215,11 @@ MY_RFM95_TCXO LITERAL1 MY_RFM95_TX_POWER LITERAL1 MY_RFM95_TX_POWER_DBM LITERAL1 MY_RFM95_TX_TIMEOUT_MS LITERAL1 +MY_RFM95_CSMA_LIMIT_DBM LITERAL1 +MY_RFM95_CSMA_TIMEOUT_MS LITERAL1 MY_RFM95_ENABLE_ENCRYPTION LITERAL1 +MY_RFM95_ENABLE_SW_ENCRYPTION LITERAL1 +MY_RFM95_RFM69_COMPATIBILITY LITERAL1 # RFM69 MY_DEBUG_VERBOSE_RFM69 LITERAL1 @@ -229,6 +251,7 @@ MY_RFM69_RST_PIN LITERAL1 MY_RFM69_SPI_SPEED LITERAL1 MY_RFM69_TX_TIMEOUT_MS LITERAL1 MY_RFM69_TX_POWER_DBM LITERAL1 +MY_RFM69_ENABLE_SW_ENCRYPTION LITERAL1 # RS485 MY_RS485 LITERAL1