diff --git a/MyConfig.h b/MyConfig.h index 94ce969..214069b 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -2,7 +2,7 @@ * 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 + * repeater and gateway builds a routing tables in RAM or EEPROM which keeps track of the * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad @@ -81,18 +81,60 @@ //#define MY_RADIO_RFM69 //#define MY_RS485 +/** +* @def MY_RAM_ROUTING_TABLE_FEATURE +* @brief If enabled, the routing table is kept in RAM (if memory allows) and saved in regular intervals. +* note: AVR has limited memory, use with care +*/ +#define MY_RAM_ROUTING_TABLE_FEATURE + +/** +* @def MY_ROUTING_TABLE_SAVE_INTERVAL_MS +* @brief Interval to dump content of routing table to eeprom +*/ +#ifndef MY_ROUTING_TABLE_SAVE_INTERVAL_MS +#define MY_ROUTING_TABLE_SAVE_INTERVAL_MS (10*60*1000ul) +#endif /** * @def MY_TRANSPORT_SANITY_CHECK -* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure. This feature is enabled for all repeater nodes (incl. GW) +* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure. +* This feature is enabled for all repeater nodes (incl. GW) */ //#define MY_TRANSPORT_SANITY_CHECK + /** -* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL +* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS * @brief Interval (in ms) of transport sanity checks */ -#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL -#define MY_TRANSPORT_SANITY_CHECK_INTERVAL ((uint32_t)60000) +#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS +#define MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS (60*1000ul) #endif +/** +* @def MY_TRANSPORT_DISCOVERY_INTERVAL_MS +* @brief This is a gateway-only feature: Interval (in ms) to issue network discovery checks +*/ +#ifndef MY_TRANSPORT_DISCOVERY_INTERVAL_MS +#define MY_TRANSPORT_DISCOVERY_INTERVAL_MS (10*60*1000ul) +#endif + +/** + *@def MY_TRANSPORT_UPLINK_CHECK_DISABLED + *@brief If set, uplink check to GW is disabled during transport initialisation + */ +//#define MY_TRANSPORT_UPLINK_CHECK_DISABLED + +/** +*@def MY_TRANSPORT_DONT_CARE_MODE +*@brief If set, transport traffic is unmonitored and GW connection is optional +*/ +//#define MY_TRANSPORT_DONT_CARE_MODE + +/** + *@def MY_TRANSPORT_MAX_TX_FAILURES + *@brief Set to override max. consecutive TX failures until SNP is initiated + */ +//#define MY_TRANSPORT_MAX_TX_FAILURES (10u) + /** * @def MY_REGISTRATION_FEATURE * @brief If enabled, node has to register to gateway/controller before allowed to send sensor data. @@ -105,14 +147,13 @@ */ #ifndef MY_REGISTRATION_RETRIES -#define MY_REGISTRATION_RETRIES 3 +#define MY_REGISTRATION_RETRIES (3u) #endif /** * @def MY_REGISTRATION_DEFAULT - * @brief Node registration default - this applies if no registration response is recieved from controller + * @brief Node registration default - this applies if no registration response is received from controller */ - #define MY_REGISTRATION_DEFAULT true /** @@ -149,8 +190,11 @@ */ //#define MY_PARENT_NODE_IS_STATIC -// Enables repeater functionality (relays messages from other nodes) -// #define MY_REPEATER_FEATURE +/** +* @def MY_REPEATER_FEATURE +* @brief Enables repeater functionality (relays messages from other nodes) +*/ +//#define MY_REPEATER_FEATURE /** * @def MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS @@ -207,7 +251,7 @@ * @brief Max buffersize needed for messages coming from controller. */ #ifndef MY_GATEWAY_MAX_RECEIVE_LENGTH -#define MY_GATEWAY_MAX_RECEIVE_LENGTH 100 +#define MY_GATEWAY_MAX_RECEIVE_LENGTH (100u) #endif /** @@ -215,7 +259,7 @@ * @brief Max buffer size when sending messages. */ #ifndef MY_GATEWAY_MAX_SEND_LENGTH -#define MY_GATEWAY_MAX_SEND_LENGTH 120 +#define MY_GATEWAY_MAX_SEND_LENGTH (120u) #endif /** @@ -223,7 +267,7 @@ * @brief Max number of parallel clients (sever mode). */ #ifndef MY_GATEWAY_MAX_CLIENTS -#define MY_GATEWAY_MAX_CLIENTS 1 +#define MY_GATEWAY_MAX_CLIENTS (1u) #endif @@ -794,5 +838,8 @@ #define MY_RX_MESSAGE_BUFFER_FEATURE #define MY_RX_MESSAGE_BUFFER_SIZE #define MY_NODE_LOCK_FEATURE +#define MY_REPEATER_FEATURE +#define MY_TRANSPORT_DONT_CARE_MODE #define MY_LINUX_SERIAL_GROUPNAME +#define MY_IS_SERIAL_PTY #endif diff --git a/MySensors.h b/MySensors.h index 4eeff85..01ab39a 100644 --- a/MySensors.h +++ b/MySensors.h @@ -236,6 +236,39 @@ #endif #endif +// RAM ROUTING TABLE +#if defined(MY_RAM_ROUTING_TABLE_FEATURE) && defined(MY_REPEATER_FEATURE) + // activate feature based on architecture + #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_SAMD) || defined(LINUX_ARCH_RASPBERRYPI) + #define MY_RAM_ROUTING_TABLE_ENABLED + #elif defined(ARDUINO_ARCH_AVR) + // memory limited, enable with care + // #define MY_RAM_ROUTING_TABLE_ENABLED + #endif +#endif + +#if defined(MY_TRANSPORT_DONT_CARE_MODE) && !defined(MY_GATEWAY_FEATURE) + // enables transport don't care mode, i.e. transport link is not monitored and GW connection optional + #if !defined(MY_PARENT_NODE_ID) || MY_PARENT_NODE_ID==255 + #error MY_TRANSPORT_DONT_CARE_MODE requires MY_PARENT_NODE_ID set + #endif + #if !defined(MY_PARENT_NODE_IS_STATIC) + #define MY_PARENT_NODE_IS_STATIC + #endif + #ifdef MY_REGISTRATION_FEATURE + #undef MY_REGISTRATION_FEATURE + #endif + #ifdef MY_TRANSPORT_SANITY_CHECK + #undef MY_TRANSPORT_SANITY_CHECK + #endif + #ifndef MY_TRANSPORT_UPLINK_CHECK_DISABLED + #define MY_TRANSPORT_UPLINK_CHECK_DISABLED + #endif + #ifdef MY_REGISTRATION_FEATURE + #undef MY_REGISTRATION_FEATURE + #endif +#endif + // RADIO #if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485) diff --git a/core/MyCapabilities.h b/core/MyCapabilities.h index 735ec32..d861961 100644 --- a/core/MyCapabilities.h +++ b/core/MyCapabilities.h @@ -70,7 +70,7 @@ #define MY_CAP_SIGN "-" #endif -#if defined(MY_RX_MESSAGE_BUFFER_SIZE) +#if defined(MY_RX_MESSAGE_BUFFER_FEATURE) #define MY_CAP_RXBUF "Q" #else #define MY_CAP_RXBUF "-" @@ -79,4 +79,4 @@ #define MY_CAPABILITIES MY_CAP_RESET MY_CAP_RADIO MY_CAP_OTA_FW MY_CAP_TYPE MY_CAP_ARCH MY_CAP_SIGN MY_CAP_RXBUF -#endif /* MyGatewayTransportEthernet_h */ +#endif /* MyCapabilities_h */ diff --git a/core/MyGatewayTransportSerial.cpp b/core/MyGatewayTransportSerial.cpp index 144ebe8..45f05e6 100644 --- a/core/MyGatewayTransportSerial.cpp +++ b/core/MyGatewayTransportSerial.cpp @@ -27,7 +27,7 @@ extern MyMessage _msgTmp; char _serialInputString[MY_GATEWAY_MAX_RECEIVE_LENGTH]; // A buffer for incoming commands from serial interface -int _serialInputPos; +uint8_t _serialInputPos; MyMessage _serialMsg; bool gatewayTransportSend(MyMessage &message) { diff --git a/core/MyHwATMega328.cpp b/core/MyHwATMega328.cpp index 59b9526..567f2cf 100644 --- a/core/MyHwATMega328.cpp +++ b/core/MyHwATMega328.cpp @@ -203,6 +203,10 @@ void hwDebugPrint(const char *fmt, ... ) { // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); MY_SERIALDEVICE.print(fmtBuffer); + #else + // prepend timestamp (AVR nodes) + MY_SERIALDEVICE.print(hwMillis()); + MY_SERIALDEVICE.print(" "); #endif va_list args; va_start (args, fmt ); diff --git a/core/MyIndication.h b/core/MyIndication.h index ab2bbbc..7e9cd07 100644 --- a/core/MyIndication.h +++ b/core/MyIndication.h @@ -34,11 +34,12 @@ typedef enum { INDICATION_GOT_PARENT, //!< Found parent node. INDICATION_REQ_NODEID, //!< Request node ID. INDICATION_GOT_NODEID, //!< Got a node ID. + INDICATION_CHECK_UPLINK, //!< Check uplink INDICATION_REQ_REGISTRATION, //!< Request node registration. INDICATION_GOT_REGISTRATION, //!< Got registration reponse. INDICATION_REBOOT, //!< Rebooting node. INDICATION_PRESENT, //!< Presenting node to gateway. - INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested. + INDICATION_CLEAR_ROUTING, //!< Clear routing table requested. INDICATION_SLEEP, //!< Node goes to sleep. INDICATION_WAKEUP, //!< Node just woke from sleep. INDICATION_FW_UPDATE_START, //!< Start of OTA firmware update process. @@ -50,7 +51,9 @@ typedef enum { INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure. INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node. INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID. + INDICATION_ERR_CHECK_UPLINK, //!< Failed to check uplink INDICATION_ERR_SIGN, //!< Error signing. + INDICATION_ERR_LENGTH, //!< Invalid message length. INDICATION_ERR_VERSION, //!< Protocol version mismatch. INDICATION_ERR_NET_FULL, //!< Network full. All node ID's are taken. INDICATION_ERR_INIT_GWTRANSPORT, //!< Gateway transport hardware init failure. diff --git a/core/MyMessage.h b/core/MyMessage.h index 40bf824..890e5f2 100644 --- a/core/MyMessage.h +++ b/core/MyMessage.h @@ -35,9 +35,9 @@ #include #endif -#define PROTOCOL_VERSION 2 //!< The version of the protocol -#define MAX_MESSAGE_LENGTH 32 //!< The maximum size of a message (including header) -#define HEADER_SIZE 7 //!< The size of the header +#define PROTOCOL_VERSION (2u) //!< The version of the protocol +#define MAX_MESSAGE_LENGTH (32u) //!< The maximum size of a message (including header) +#define HEADER_SIZE (7u) //!< The size of the header #define MAX_PAYLOAD (MAX_MESSAGE_LENGTH - HEADER_SIZE) //!< The maximum size of a payload depends on #MAX_MESSAGE_LENGTH and #HEADER_SIZE /// @brief The command field (message-type) defines the overall properties of a message diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index 7778b6e..cf45f49 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -103,7 +103,7 @@ void _begin() { #if defined(MY_RADIO_FEATURE) // Save static parent id in eeprom (used by bootloader) hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID); - transportInitialize(); + transportInitialise(); while (!isTransportReady()) { hwWatchdogReset(); transportProcess(); @@ -146,7 +146,7 @@ void _begin() { inclusionInit(); #endif - // initialize the transport driver + // initialise the transport driver if (!gatewayTransportInit()) { setIndication(INDICATION_ERR_INIT_GWTRANSPORT); debug(PSTR("!MCO:BGN:TSP FAIL\n")); @@ -355,15 +355,15 @@ bool _processInternalMessages() { } else if (type == I_DEBUG) { #if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG) - char debug_msg = _msg.data[0]; + const char debug_msg = _msg.data[0]; if (debug_msg == 'R') { // routing table #if defined(MY_REPEATER_FEATURE) - for (uint8_t cnt = 0; cnt != 255; cnt++) { - uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS + cnt); + for (uint16_t cnt = 0; cnt < SIZE_ROUTES; cnt++) { + const uint8_t route = transportGetRoute(cnt); if (route != BROADCAST_ADDRESS) { debug(PSTR("MCO:PIM:ROUTE N=%d,R=%d\n"), cnt, route); - uint8_t OutBuf[2] = { cnt,route }; - _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(OutBuf, 2)); + uint8_t outBuf[2] = { (uint8_t)cnt,route }; + _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(outBuf, 2)); wait(200); } } @@ -392,15 +392,19 @@ bool _processInternalMessages() { // sender is a node if (type == I_REGISTRATION_REQUEST) { #if defined(MY_GATEWAY_FEATURE) - // register request are exclusively handled by GW/Controller - // !!! eventually define if AUTO ACK or register request forwarded to controller + // registeration requests are exclusively handled by GW/Controller #if !defined(MY_REGISTRATION_CONTROLLER) - // auto register if version compatible + // auto registration if version compatible bool approveRegistration = true; #if defined(MY_CORE_COMPATIBILITY_CHECK) approveRegistration = (_msg.getByte() >= MY_CORE_MIN_VERSION); #endif + + #if (F_CPU>16000000) + // delay for fast GW and slow nodes + delay(5); + #endif _sendRoute(build(_msgTmp, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_RESPONSE, false).set(approveRegistration)); #else return false; // processing of this request via controller diff --git a/core/MySensorsCore.h b/core/MySensorsCore.h index f88a028..390ad82 100644 --- a/core/MySensorsCore.h +++ b/core/MySensorsCore.h @@ -25,7 +25,7 @@ * @{ * * MySensorsCore-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE - * - [!] Exclamation mark is prepended in case of error + * - [!] Exclamation mark is prepended in case of error or warning * - SYSTEM: * - MCO messages emitted by MySensorsCore * - SUB SYSTEMS: diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 1519ff0..7200558 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB + * Copyright (C) 2013-2016 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -20,30 +20,64 @@ #include "MyTransport.h" // SM: transitions and update states -static transportState stInit = { stInitTransition, NULL }; +static transportState stInit = { stInitTransition, stInitUpdate }; static transportState stParent = { stParentTransition, stParentUpdate }; static transportState stID = { stIDTransition, stIDUpdate }; -static transportState stUplink = { stUplinkTransition, NULL }; +static transportState stUplink = { stUplinkTransition, stUplinkUpdate }; static transportState stReady = { stReadyTransition, stReadyUpdate }; static transportState stFailure = { stFailureTransition, stFailureUpdate }; // transport SM variables static transportSM _transportSM; -// stInit: initialize transport HW -void stInitTransition() { +// global variables +extern MyMessage _msg; // incoming message +extern MyMessage _msgTmp; // outgoing message +extern NodeConfig _nc; // node config + +#if defined(MY_RAM_ROUTING_TABLE_ENABLED) + static routingTable _transportRoutingTable; //!< routing table + static uint32_t _lastRoutingTableSave; //!< last routing table dump +#endif + +// regular sanity check, activated by default on GW and repeater nodes +#if defined(MY_TRANSPORT_SANITY_CHECK) + static uint32_t _lastSanityCheck; //!< last sanity check +#endif + +// regular network discovery, sends I_DISCOVER_REQUESTS to update routing table +// sufficient to have GW triggering requests to also update repeater nodes +#if defined(MY_GATEWAY_FEATURE) + static uint32_t _lastNetworkDiscovery; //! last network discovery +#endif + +// stInit: initialise transport HW +void stInitTransition(void) { TRANSPORT_DEBUG(PSTR("TSM:INIT\n")); - // initialize status variables + // initialise status variables _transportSM.pingActive = false; _transportSM.transportActive = false; - #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE) - _transportSM.lastSanityCheck = hwMillis(); - #endif - _transportSM.lastUplinkCheck = 0; - // Read node settings (ID, parentId, GW distance) from EEPROM - hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); + _transportSM.lastUplinkCheck = 0ul; - // initialize radio + #if defined(MY_TRANSPORT_SANITY_CHECK) + _lastSanityCheck = hwMillis(); + #endif + #if defined(MY_GATEWAY_FEATURE) + _lastNetworkDiscovery = 0ul; + #endif + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + _lastRoutingTableSave = hwMillis(); + #endif + + // Read node settings (ID, parent ID, GW distance) from EEPROM + hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); +} + +void stInitUpdate(void) { + #if defined(MY_TRANSPORT_DONT_CARE_MODE) + TRANSPORT_DEBUG(PSTR("TSM:INIT:TDC\n")); // transport don't care mode + #endif + // initialise radio if (!transportInit()) { TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n")); setIndication(INDICATION_ERR_INIT_TRANSPORT); @@ -56,26 +90,26 @@ void stInitTransition() { // Set configuration for gateway TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n")); _nc.parentNodeId = GATEWAY_ADDRESS; - _nc.distance = 0; + _nc.distance = 0u; _nc.nodeId = GATEWAY_ADDRESS; transportSetAddress(GATEWAY_ADDRESS); // GW mode: skip FPAR,ID,UPL states transportSwitchSM(stReady); #else if ((uint8_t)MY_NODE_ID != AUTO) { - TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID,ID=%d\n"),MY_NODE_ID); - // Set static id - _nc.nodeId = MY_NODE_ID; - // Save static id in eeprom - hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID); + TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID=%d\n"),(uint8_t)MY_NODE_ID); + // Set static ID + _nc.nodeId = (uint8_t)MY_NODE_ID; + // Save static ID to eeprom (for bootloader) + hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)MY_NODE_ID); } - // set ID if static or set in EEPROM + // assign ID if set if (_nc.nodeId == AUTO || transportAssignNodeID(_nc.nodeId)) { - // if node ID > 0, proceed to next state + // if node ID valid (>0 and <255), proceed to next state transportSwitchSM(stParent); } else { - // ID invalid (=0), nothing we can do + // ID invalid (0 or 255) transportSwitchSM(stFailure); } #endif @@ -83,32 +117,35 @@ void stInitTransition() { } // stParent: find parent -void stParentTransition() { +void stParentTransition(void) { TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent setIndication(INDICATION_FIND_PARENT); _transportSM.uplinkOk = false; _transportSM.preferredParentFound = false; #if defined(MY_PARENT_NODE_IS_STATIC) - TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"),MY_PARENT_NODE_ID); // static parent + TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"), (uint8_t)MY_PARENT_NODE_ID); // static parent _transportSM.findingParentNode = false; - _nc.distance = 1; // assumption, CHKUPL:GWDC will update this variable - _nc.parentNodeId = MY_PARENT_NODE_ID; - // skipping find parent - setIndication(INDICATION_GOT_PARENT); - transportSwitchSM(stID); + _nc.distance = 1u; // assumption, CHKUPL:GWDC will update this variable + _nc.parentNodeId = (uint8_t)MY_PARENT_NODE_ID; + // save parent ID to eeprom (for bootloader) + hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, (uint8_t)MY_PARENT_NODE_ID); #else _transportSM.findingParentNode = true; _nc.distance = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID _nc.parentNodeId = AUTO; // Broadcast find parent request - transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_REQUEST, false).set("")); + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_REQUEST, false).set("")); #endif } // stParentUpdate -void stParentUpdate() { - #if !defined(MY_PARENT_NODE_IS_STATIC) - if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) { +void stParentUpdate(void) { + #if defined(MY_PARENT_NODE_IS_STATIC) + // skipping find parent + setIndication(INDICATION_GOT_PARENT); + transportSwitchSM(stID); + #else + if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS || _transportSM.preferredParentFound) { // timeout or preferred parent found if (_nc.parentNodeId != AUTO) { // parent assigned @@ -118,9 +155,9 @@ void stParentUpdate() { // go to next state transportSwitchSM(stID); } - else if (transportTimeInState() > STATE_TIMEOUT) { + else { // timeout w/o reply or valid parent - if (_transportSM.retries < STATE_RETRIES) { + if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) { // retries left TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n")); // find parent, no reply // reenter state @@ -128,7 +165,7 @@ void stParentUpdate() { } else { // no retries left, finding parent failed - TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail + TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n")); setIndication(INDICATION_ERR_FIND_PARENT); transportSwitchSM(stFailure); } @@ -138,67 +175,100 @@ void stParentUpdate() { } // stID: verify and request ID if necessary -void stIDTransition() { +void stIDTransition(void) { TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID if (_nc.nodeId == AUTO) { // send ID request setIndication(INDICATION_REQ_NODEID); TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID - transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("")); + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("")); } } -void stIDUpdate() { +void stIDUpdate(void) { if (_nc.nodeId != AUTO) { // current node ID is valid - TRANSPORT_DEBUG(PSTR("TSM:ID:OK,ID=%d\n"), _nc.nodeId); + TRANSPORT_DEBUG(PSTR("TSM:ID:OK\n")); setIndication(INDICATION_GOT_NODEID); // proceed to next state transportSwitchSM(stUplink); } - else if (transportTimeInState() > STATE_TIMEOUT) { + else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) { // timeout - if (_transportSM.retries < STATE_RETRIES) { - // reenter + if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) { + // retries left: reenter state transportSwitchSM(stID); } else { // no retries left - TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL,ID=%d\n"), _nc.nodeId); + TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL\n")); setIndication(INDICATION_ERR_GET_NODEID); transportSwitchSM(stFailure); } } } -void stUplinkTransition() { - TRANSPORT_DEBUG(PSTR("TSM:UPL\n")); - // check uplink - if(transportCheckUplink(true)) { - // uplink ok, i.e. GW replied - TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok - // proceed to next state - transportSwitchSM(stReady); - } - else { - // uplink failed, at this point, no retries or timeout - TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink failed - // go back to stParent - transportSwitchSM(stParent); - } +void stUplinkTransition(void) { + #if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED) + TRANSPORT_DEBUG(PSTR("TSM:UPL\n")); + setIndication(INDICATION_CHECK_UPLINK); + _transportSM.pingResponse = INVALID_HOPS; + _transportSM.pingActive = true; + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)1)); + #endif } -void stReadyTransition() { +void stUplinkUpdate(void) { + #if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED) + if (_transportSM.pingResponse != INVALID_HOPS) { + _transportSM.lastUplinkCheck = hwMillis(); + // uplink ok, i.e. GW replied + TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok + //_transportSM.pingActive = false; ==> set false upon receiving I_PONG + if (_transportSM.pingResponse != _nc.distance) { + TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%d,N=%d\n"), _nc.distance, _transportSM.pingResponse); // distance to GW changed + _nc.distance = _transportSM.pingResponse; + } + transportSwitchSM(stReady); // proceed to next state + } + else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) { + // timeout + if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) { + // retries left: reenter state + transportSwitchSM(stUplink); + } + else { + // no retries left + TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink check failed + _transportSM.pingActive = false; + setIndication(INDICATION_ERR_CHECK_UPLINK); + transportSwitchSM(stParent); // go back to stParent + } + } + #else + TRANSPORT_DEBUG(PSTR("TSM:UPL:DISABLED\n")); // uplink check disabled + transportSwitchSM(stReady); + #endif +} + +void stReadyTransition(void) { // transport is ready and fully operational TRANSPORT_DEBUG(PSTR("TSM:READY\n")); // transport is ready _transportSM.uplinkOk = true; - _transportSM.failedUplinkTransmissions = 0; // reset counter + _transportSM.failureCounter = 0u; // reset failure counter + _transportSM.failedUplinkTransmissions = 0u; // reset failed uplink TX counter } -// stReadyUpdate: monitors uplink failures -void stReadyUpdate() { - #if !defined(MY_GATEWAY_FEATURE) - if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) { +// stReadyUpdate: monitors link +void stReadyUpdate(void) { + #if defined(MY_GATEWAY_FEATURE) + if (hwMillis() - _lastNetworkDiscovery > MY_TRANSPORT_DISCOVERY_INTERVAL_MS) { + _lastNetworkDiscovery = hwMillis(); + TRANSPORT_DEBUG(PSTR("TSM:READY:NWD REQ\n")); // send transport network discovery + (void)transportRouteMessage(build(_msgTmp, GATEWAY_ADDRESS, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_REQUEST, false).set("")); + } + #else + if (_transportSM.failedUplinkTransmissions > MY_TRANSPORT_MAX_TX_FAILURES) { // too many uplink transmissions failed, find new parent (if non-static) #if !defined(MY_PARENT_NODE_IS_STATIC) TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,SNP\n")); // uplink failed, search new parent @@ -206,70 +276,92 @@ void stReadyUpdate() { #else TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,STATP\n")); // uplink failed, static parent // reset counter - _transportSM.failedUplinkTransmissions = 0; + _transportSM.failedUplinkTransmissions = 0u; #endif } #endif + + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + if (hwMillis() - _lastRoutingTableSave > MY_ROUTING_TABLE_SAVE_INTERVAL_MS) { + _lastRoutingTableSave = hwMillis(); + transportSaveRoutingTable(); + } + #endif } // stFailure: entered upon HW init failure or max retries exceeded -void stFailureTransition() { - TRANSPORT_DEBUG(PSTR("TSM:FAILURE\n")); - _transportSM.uplinkOk = false; // uplink nok +void stFailureTransition(void) { + if (_transportSM.failureCounter < MY_TRANSPORT_MAX_TSM_FAILURES) { + _transportSM.failureCounter++; // increment consecutive TSM failure counter + } + TRANSPORT_DEBUG(PSTR("TSM:FAIL:CNT=%d\n"),_transportSM.failureCounter); + _transportSM.uplinkOk = false; // uplink nok _transportSM.transportActive = false; // transport inactive setIndication(INDICATION_ERR_INIT_TRANSPORT); - // power down transport, no need until re-init - TRANSPORT_DEBUG(PSTR("TSM:FAILURE:PDT\n")); // power down transport - transportPowerDown(); + #if defined(MY_RADIO_FEATURE) + TRANSPORT_DEBUG(PSTR("TSM:FAIL:PDT\n")); // power down transport, no need until re-init + transportPowerDown(); + #endif } -void stFailureUpdate() { - if (transportTimeInState()> TIMEOUT_FAILURE_STATE) { - TRANSPORT_DEBUG(PSTR("TSM:FAILURE:RE-INIT\n")); // attempt to re-initialize transport +void stFailureUpdate(void) { + if (transportTimeInState() > ( isTransportExtendedFailure()? MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE: MY_TRANSPORT_TIMEOUT_FAILURE_STATE) ) { + TRANSPORT_DEBUG(PSTR("TSM:FAIL:RE-INIT\n")); // attempt to re-initialise transport transportSwitchSM(stInit); } } void transportSwitchSM(transportState& newState) { if (_transportSM.currentState != &newState) { - // state change, reset retry counter - _transportSM.retries = 0; - // change state - _transportSM.currentState = &newState; + _transportSM.stateRetries = 0u; // state change, reset retry counter + _transportSM.currentState = &newState; // change state } else { - _transportSM.retries++; // increment retries + _transportSM.stateRetries++; // increment retries } - // Transition event - if (_transportSM.currentState->Transition) _transportSM.currentState->Transition(); - // save time - _transportSM.stateEnter = hwMillis(); + if (_transportSM.currentState->Transition) _transportSM.currentState->Transition(); // State transition + _transportSM.stateEnter = hwMillis(); // save time } -uint32_t transportTimeInState() { +uint32_t transportTimeInState(void) { return hwMillis() - _transportSM.stateEnter; } -void transportUpdateSM(){ +void transportUpdateSM(void) { if (_transportSM.currentState->Update) _transportSM.currentState->Update(); } -bool isTransportReady() { +bool isTransportReady(void) { return _transportSM.uplinkOk; } -bool isTransportSearchingParent() { +bool isTransportExtendedFailure(void) { + return _transportSM.failureCounter == MY_TRANSPORT_MAX_TSM_FAILURES; +} + +bool isTransportSearchingParent(void) { return _transportSM.findingParentNode; } -void transportInitialize() { +bool isMessageReceived(void) { + return _transportSM.MsgReceived; +} + +void resetMessageReceived(void) { + _transportSM.MsgReceived = false; +} + + +void transportInitialise(void) { + _transportSM.failureCounter = 0u; // reset failure counter + transportLoadRoutingTable(); // load routing table to RAM (if feature enabled) // intial state - _transportSM.currentState = &stFailure; + _transportSM.currentState = NULL; transportSwitchSM(stInit); } // update TSM and process incoming messages -void transportProcess() { +void transportProcess(void) { // update state machine transportUpdateSM(); // process transport FIFO @@ -277,54 +369,55 @@ void transportProcess() { } -bool transportCheckUplink(bool force) { - if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < CHKUPL_INTERVAL) { - TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK,FCTRL\n")); // flood control +bool transportCheckUplink(const bool force) { + if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < MY_TRANSPORT_CHKUPL_INTERVAL_MS) { + TRANSPORT_DEBUG(PSTR("TSF:CKU:OK,FCTRL\n")); // flood control return true; } // ping GW - uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS); + const uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS); // verify hops if (hopsCount != INVALID_HOPS) { // update _transportSM.lastUplinkCheck = hwMillis(); - TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK\n")); + TRANSPORT_DEBUG(PSTR("TSF:CKU:OK\n")); // did distance to GW change upstream, eg. re-routing of uplink nodes if (hopsCount != _nc.distance) { - TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:DGWC,O=%d,N=%d\n"), _nc.distance, hopsCount); // distance to GW changed + TRANSPORT_DEBUG(PSTR("TSF:CKU:DGWC,O=%d,N=%d\n"), _nc.distance, hopsCount); // distance to GW changed _nc.distance = hopsCount; } return true; } else { - TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:FAIL\n")); + TRANSPORT_DEBUG(PSTR("TSF:CKU:FAIL\n")); return false; } } -bool transportAssignNodeID(uint8_t newNodeId) { +bool transportAssignNodeID(const uint8_t newNodeId) { // verify if ID valid if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) { _nc.nodeId = newNodeId; transportSetAddress(newNodeId); // Write ID to EEPROM hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId); - TRANSPORT_DEBUG(PSTR("TSF:ASID:OK,ID=%d\n"),newNodeId); + TRANSPORT_DEBUG(PSTR("TSF:SID:OK,ID=%d\n"),newNodeId); // Node ID assigned return true; } else { - TRANSPORT_DEBUG(PSTR("!TSF:ASID:FAIL,ID=%d\n"),newNodeId); + TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%d\n"),newNodeId); // ID is invalid, cannot assign ID setIndication(INDICATION_ERR_NET_FULL); + _nc.nodeId = AUTO; return false; } } bool transportRouteMessage(MyMessage &message) { - uint8_t destination = message.destination; - uint8_t route; + const uint8_t destination = message.destination; + uint8_t route = _nc.parentNodeId; // by default, all traffic is routed via parent node if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) { - TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:FPAR ACTIVE\n")); // find parent active, message not sent + TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent // request to send a non-BC message while finding parent active, abort return false; } @@ -338,122 +431,130 @@ bool transportRouteMessage(MyMessage &message) { else { #if defined(MY_REPEATER_FEATURE) // destination not GW & not BC, get route - route = hwReadConfig(EEPROM_ROUTES_ADDRESS + destination); + route = transportGetRoute(destination); if (route == AUTO) { - // route unknown - if (message.last != _nc.parentNodeId) { - // message not from parent, i.e. child node - route it to parent - TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:%d UNKNOWN\n"), destination); - route = _nc.parentNodeId; - } - else { - // route unknown and msg received from parent, send it to destination assuming in rx radius + TRANSPORT_DEBUG(PSTR("!TSF:RTE:%d UNKNOWN\n"), destination); // route unknown + #if !defined(MY_GATEWAY_FEATURE) + if (message.last != _nc.parentNodeId) { + // message not from parent, i.e. child node - route it to parent + route = _nc.parentNodeId; + } + else { + // route unknown and msg received from parent, send it to destination assuming in rx radius + route = destination; + } + #else + // if GW, all unknown destinations are directly addressed route = destination; - } + #endif } #else route = _nc.parentNodeId; // not a repeater, all traffic routed via parent #endif } // send message - bool ok = transportSendWrite(route, message); + const bool result = transportSendWrite(route, message); #if !defined(MY_GATEWAY_FEATURE) // update counter if (route == _nc.parentNodeId) { - if (!ok) { + if (!result) { setIndication(INDICATION_ERR_TX); _transportSM.failedUplinkTransmissions++; } - else _transportSM.failedUplinkTransmissions = 0; + else _transportSM.failedUplinkTransmissions = 0u; } #else - if(!ok) setIndication(INDICATION_ERR_TX); + if(!result) setIndication(INDICATION_ERR_TX); #endif - return ok; + return result; } bool transportSendRoute(MyMessage &message) { + bool result = false; if (isTransportReady()) { - return transportRouteMessage(message); + result = transportRouteMessage(message); } else { // TNR: transport not ready - TRANSPORT_DEBUG(PSTR("!TSF:SEND:TNR\n")); - return false; + TRANSPORT_DEBUG(PSTR("!TSF:SND:TNR\n")); } + return result; } // only be used inside transport -bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){ - uint32_t enter = hwMillis(); +bool transportWait(const uint32_t ms, const uint8_t cmd, const uint8_t msgtype){ + const uint32_t enter = hwMillis(); // invalidate msg type _msg.type = !msgtype; bool expectedResponse = false; while ((hwMillis() - enter < ms) && !expectedResponse) { // process incoming messages transportProcessFIFO(); - #if defined(ARDUINO_ARCH_ESP8266) - yield(); - #endif + yield(); // process esp8266 stack, ignored for AVR expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype); } return expectedResponse; } -uint8_t transportPingNode(uint8_t targetId) { +uint8_t transportPingNode(const uint8_t targetId) { if(!_transportSM.pingActive){ + TRANSPORT_DEBUG(PSTR("TSF:PNG:SEND,TO=%d\n"), targetId); if(targetId == _nc.nodeId) { - // ping to ourself, pingActive remains false - return 0; + // pinging self + _transportSM.pingResponse = 0u; + } + else { + _transportSM.pingActive = true; + _transportSM.pingResponse = INVALID_HOPS; + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)1)); + // Wait for ping reply or timeout + (void)transportWait(2000, C_INTERNAL, I_PONG); } - _transportSM.pingActive = true; - _transportSM.pingResponse = INVALID_HOPS; - TRANSPORT_DEBUG(PSTR("TSF:PING:SEND,TO=%d\n"), targetId); - transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01)); - // Wait for ping reply or timeout - transportWait(2000, C_INTERNAL, I_PONG); // make sure missing I_PONG msg does not block pinging function by leaving pignActive=true _transportSM.pingActive = false; return _transportSM.pingResponse; } else { + TRANSPORT_DEBUG(PSTR("!TSF:PNG:ACTIVE\n")); // ping active, cannot start new ping return INVALID_HOPS; } } -void transportClearRoutingTable() { - for (uint8_t i = 0; i != 255; i++) { - hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, BROADCAST_ADDRESS); - } - TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table -} - -uint32_t transportGetHeartbeat() { +uint32_t transportGetHeartbeat(void) { return transportTimeInState(); } -void transportProcessMessage() { - (void)signerCheckTimer(); // Manage signing timeout - - uint8_t payloadLength = transportReceive((uint8_t *)&_msg); - (void)payloadLength; // currently not used - +void transportProcessMessage(void) { + // Manage signing timeout + (void)signerCheckTimer(); + // receive message setIndication(INDICATION_RX); - - uint8_t command = mGetCommand(_msg); - uint8_t type = _msg.type; - uint8_t sender = _msg.sender; - uint8_t last = _msg.last; - uint8_t destination = _msg.destination; + const uint8_t payloadLength = transportReceive((uint8_t *)&_msg); + // get message length and limit size + const uint8_t msgLength = min(mGetLength(_msg), MAX_PAYLOAD); + // calculate expected length + const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength); + const uint8_t command = mGetCommand(_msg); + const uint8_t type = _msg.type; + const uint8_t sender = _msg.sender; + const uint8_t last = _msg.last; + const uint8_t destination = _msg.destination; TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), - sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf)); + sender, last, destination, _msg.sensor, command, type, mGetPayloadType(_msg), msgLength, mGetSigned(_msg), _msg.getString(_convBuf)); - // verify protocol version - if(mGetVersion(_msg) != PROTOCOL_VERSION) { + // Reject payloads with incorrect length + if (payloadLength != expectedMessageLength) { + setIndication(INDICATION_ERR_LENGTH); + TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN,%d!=%d\n"), payloadLength, expectedMessageLength); // invalid payload length + return; + } + + // Reject messages with incorrect protocol version + if (mGetVersion(_msg) != PROTOCOL_VERSION) { setIndication(INDICATION_ERR_VERSION); - TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d!=%d\n"), mGetVersion(_msg),PROTOCOL_VERSION); // protocol version mismatch + TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d=%d\n"), mGetVersion(_msg), PROTOCOL_VERSION); // protocol version mismatch return; } @@ -463,33 +564,39 @@ void transportProcessMessage() { TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n")); return; } + + // update routing table if msg not from parent + #if defined(MY_REPEATER_FEATURE) + #if !defined(MY_GATEWAY_FEATURE) + if (last != _nc.parentNodeId) { + #else + // GW doesn't have parent + { + #endif + // Message is from one of the child nodes. Add it to routing table. + transportSetRoute(sender, last); + } + #endif + + // set message received flag + _transportSM.MsgReceived = true; // Is message addressed to this node? if (destination == _nc.nodeId) { // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD)); // null terminate data - _msg.data[mGetLength(_msg)] = 0x00; - - // update routing table if msg not from parent - #if defined(MY_REPEATER_FEATURE) - if (last != _nc.parentNodeId) { - // Message is from one of the child nodes. Add it to routing table. - hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last); - } - #endif - + _msg.data[msgLength] = 0u; // Check if sender requests an ack back. if (mGetRequestAck(_msg)) { + TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested _msgTmp = _msg; // Copy message mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop) mSetAck(_msgTmp, true); // set ACK flag _msgTmp.sender = _nc.nodeId; _msgTmp.destination = sender; - // send ACK - TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested - // use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply - transportSendRoute(_msgTmp); + // send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply + (void)transportSendRoute(_msgTmp); } if(!mGetAck(_msg)) { // only process if not ACK @@ -500,27 +607,25 @@ void transportProcessMessage() { } #if !defined(MY_GATEWAY_FEATURE) if (type == I_ID_RESPONSE) { - #if (MY_NODE_ID == AUTO) - // only active if node ID dynamic - transportAssignNodeID(_msg.getByte()); - #endif - return; // no further processing required + #if (MY_NODE_ID == AUTO) + // only active if node ID dynamic + (void)transportAssignNodeID(_msg.getByte()); + #endif + return; // no further processing required } if (type == I_FIND_PARENT_RESPONSE) { #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC) if (_transportSM.findingParentNode) { // only process if find parent active // Reply to a I_FIND_PARENT_REQUEST message. Check if the distance is shorter than we already have. uint8_t distance = _msg.getByte(); - TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR RES,ID=%d,D=%d\n"), sender, distance); // find parent response if (isValidDistance(distance)) { - // Distance to gateway is one more for us w.r.t. parent - distance++; + distance++; // Distance to gateway is one more for us w.r.t. parent // update settings if distance shorter or preferred parent found - if (((isValidDistance(distance) && distance < _nc.distance) || (!_autoFindParent && sender == MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) { + if (((isValidDistance(distance) && distance < _nc.distance) || (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) { // Found a neighbor closer to GW than previously found - if (!_autoFindParent && sender == MY_PARENT_NODE_ID) { + if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) { _transportSM.preferredParentFound = true; - TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF FOUND\n")); // find parent, preferred parent found + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n")); // find parent, preferred parent found } _nc.distance = distance; _nc.parentNodeId = sender; @@ -529,7 +634,7 @@ void transportProcessMessage() { } } else { - TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive + TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive } return; #endif @@ -538,7 +643,11 @@ void transportProcessMessage() { // general if (type == I_PING) { TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged - transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01)); + #if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000) + // delay for fast GW and slow nodes + delay(5); + #endif + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)1)); return; // no further processing required } if (type == I_PONG) { @@ -547,9 +656,11 @@ void transportProcessMessage() { _transportSM.pingResponse = _msg.getByte(); TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received } + else { + TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive + } return; // no further processing required } - if (_processInternalMessages()) { return; // no further processing required } @@ -566,13 +677,12 @@ void transportProcessMessage() { } #if defined(MY_GATEWAY_FEATURE) // Hand over message to controller - gatewayTransportSend(_msg); + (void)gatewayTransportSend(_msg); #endif // Call incoming message callback if available if (receive) { receive(_msg); } - return; } else if (destination == BROADCAST_ADDRESS) { TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg @@ -581,49 +691,61 @@ void transportProcessMessage() { // only reply if node is fully operational if (type == I_FIND_PARENT_REQUEST) { #if defined(MY_REPEATER_FEATURE) - if (sender != _nc.parentNodeId) { // no circular reference - TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPR: find parent request - // node is in our range, update routing table - important if node has new repeater as parent - hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender); - // check if uplink functional - node can only be parent node if link to GW functional - // this also prevents circular references in case GW ooo - if(transportCheckUplink(false)){ - _transportSM.lastUplinkCheck = hwMillis(); - TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok - // delay minimizes collisions - delay(hwMillis() & 0x3ff); - transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance)); - } - else { - TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request - } + if (sender != _nc.parentNodeId) { // no circular reference + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPAR: find parent request + // check if uplink functional - node can only be parent node if link to GW functional + // this also prevents circular references in case GW ooo + if (transportCheckUplink()) { + _transportSM.lastUplinkCheck = hwMillis(); + TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok + // random delay minimizes collisions + delay(hwMillis() & 0x3ff); + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance)); } - + else { + TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request + } + } #endif - return; // no further processing required + return; // no further processing required, do not forward } + } // isTransportReady + if (type == I_FIND_PARENT_RESPONSE) { + return; // no further processing required, do not forward } - if (type == I_DISCOVER_REQUEST) { - if (last == _nc.parentNodeId) { - // random wait to minimize collisions - delay(hwMillis() & 0x3ff); - transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId)); - // no return here (for fwd if repeater) + #if !defined(MY_GATEWAY_FEATURE) + if (type == I_DISCOVER_REQUEST) { + if (last == _nc.parentNodeId) { + // random wait to minimize collisions + delay(hwMillis() & 0x3ff); + (void)transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId)); + // no return here (for fwd if repeater) + } } - } + #endif } // controlled BC relay #if defined(MY_REPEATER_FEATURE) // controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds if(last == _nc.parentNodeId && sender != _nc.nodeId && isTransportReady()){ TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding - transportRouteMessage(_msg); + (void)transportRouteMessage(_msg); } #endif - // Call incoming message callback if available, but only if message received from parent - if (command != C_INTERNAL && last == _nc.parentNodeId && receive) { - receive(_msg); + // Callback for BC, only for non-internal messages + if (command != C_INTERNAL) { + #if !defined(MY_GATEWAY_FEATURE) + // only proceed if message received from parent + if (last != _nc.parentNodeId) return; + #endif + #if defined(MY_GATEWAY_FEATURE) + // Hand over message to controller + (void)gatewayTransportSend(_msg); + #endif + if (receive) { + receive(_msg); + } } } @@ -632,52 +754,48 @@ void transportProcessMessage() { #if defined(MY_REPEATER_FEATURE) if (isTransportReady()) { TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg - // update routing table if message not received from parent - if (last != _nc.parentNodeId) { - hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last); - } if (command == C_INTERNAL) { if (type == I_PING || type == I_PONG) { uint8_t hopsCnt = _msg.getByte(); if (hopsCnt != MAX_HOPS) { TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt); - _msg.set((uint8_t)(hopsCnt + 1)); + hopsCnt++; + _msg.set(hopsCnt); } } } // Relay this message to another node - transportRouteMessage(_msg); + (void)transportRouteMessage(_msg); } #else - TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NORP\n")); // message relaying request, but not a repeater + TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n")); // message relaying request, but not a repeater #endif } } -void transportInvokeSanityCheck() { +void transportInvokeSanityCheck(void) { if (!transportSanityCheck()) { - TRANSPORT_DEBUG(PSTR("!TSF:SANCHK:FAIL\n")); // sanity check fail + TRANSPORT_DEBUG(PSTR("!TSF:SNK:FAIL\n")); // sanity check fail transportSwitchSM(stFailure); - return; } else { - TRANSPORT_DEBUG(PSTR("TSF:SANCHK:OK\n")); // sanity check ok + TRANSPORT_DEBUG(PSTR("TSF:SNK:OK\n")); // sanity check ok } } -inline void transportProcessFIFO() { - if (_transportSM.transportActive) { - #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE) - if (hwMillis() - _transportSM.lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL) { - _transportSM.lastSanityCheck = hwMillis(); - transportInvokeSanityCheck(); - } - #endif - } - else { - // transport not active, nothing to be done +void transportProcessFIFO(void) { + if (!_transportSM.transportActive) { + // transport not active, no further processing required return; } + + #if defined(MY_TRANSPORT_SANITY_CHECK) + if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) { + _lastSanityCheck = hwMillis(); + transportInvokeSanityCheck(); + } + #endif + uint8_t _processedMessages = MAX_SUBSEQ_MSGS; // process all msgs in FIFO or counter exit while (transportAvailable() && _processedMessages--) { @@ -691,10 +809,8 @@ inline void transportProcessFIFO() { #endif } -bool transportSendWrite(uint8_t to, MyMessage &message) { - // Update last - message.last = _nc.nodeId; - +bool transportSendWrite(const uint8_t to, MyMessage &message) { + message.last = _nc.nodeId; // Update last // sign message if required if (!signerSignMsg(message)) { TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n")); @@ -703,17 +819,60 @@ bool transportSendWrite(uint8_t to, MyMessage &message) { } // msg length changes if signed - uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message); - + const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength(message) ); + // send setIndication(INDICATION_TX); - bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length)); - + bool result = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, totalMsgLength)); + // broadcasting (workaround counterfeits) + result |= (to == BROADCAST_ADDRESS); + TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"), - (ok || to == BROADCAST_ADDRESS ? "" : "!"),message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, - mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, to==BROADCAST_ADDRESS ? "bc" : (ok ? "OK":"NACK"), message.getString(_convBuf)); - - return (ok || to==BROADCAST_ADDRESS); + (result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, + mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf)); + + return result; } -// EOF MyTransport.cpp +void transportClearRoutingTable(void) { + for (uint16_t i = 0; i < SIZE_ROUTES; i++) { + transportSetRoute((uint8_t)i, BROADCAST_ADDRESS); + } + transportSaveRoutingTable(); // save cleared routing table to EEPROM (if feature enabled) + TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table +} + +void transportLoadRoutingTable(void) { + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + hwReadConfigBlock((void*)&_transportRoutingTable.route, (void*)EEPROM_ROUTES_ADDRESS, SIZE_ROUTES); + TRANSPORT_DEBUG(PSTR("TSF:LRT:OK\n")); // load routing table + #endif +} + +void transportSaveRoutingTable(void) { + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + hwWriteConfigBlock((void*)EEPROM_ROUTES_ADDRESS, (void*)&_transportRoutingTable.route, SIZE_ROUTES); + TRANSPORT_DEBUG(PSTR("TSF:SRT:OK\n")); // save routing table + #endif +} + +void transportSetRoute(const uint8_t node, const uint8_t route) { + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + _transportRoutingTable.route[node] = route; + #else + hwWriteConfig(EEPROM_ROUTES_ADDRESS + node, route); + #endif +} + +uint8_t transportGetRoute(const uint8_t node) { + uint8_t result; + #if defined(MY_RAM_ROUTING_TABLE_ENABLED) + result = _transportRoutingTable.route[node]; + #else + result = hwReadConfig(EEPROM_ROUTES_ADDRESS + node); + #endif + return result; +} + + +// EOF MyTransport.cpp \ No newline at end of file diff --git a/core/MyTransport.h b/core/MyTransport.h index 2267bee..0cd350e 100644 --- a/core/MyTransport.h +++ b/core/MyTransport.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB + * Copyright (C) 2013-2016 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -36,23 +36,27 @@ * - TSM:ID from stID Check/request node ID, if dynamic node ID set * - TSM:UPL from stUplink Verify uplink connection by pinging GW * - TSM:READY from stReady Transport is ready and fully operational - * - TSM:FAILURE from stFailure Failure in transport link or transport HW + * - TSM:FAIL from stFailure Failure in transport link or transport HW * - Transport support function (TSF) * - TSF:CHKUPL from @ref transportCheckUplink(), checks connection to GW * - TSF:ASID from @ref transportAssignNodeID(), assigns node ID * - TSF:PING from @ref transportPingNode(), pings a node * - TSF:CRT from @ref transportClearRoutingTable(), clears routing table stored in EEPROM + * - TSF:LRT from @ref transportLoadRoutingTable(), loads RAM routing table from EEPROM (only GW/repeaters) + * - TSF:SRT from @ref transportSaveRoutingTable(), saves RAM routing table to EEPROM (only GW/repeaters) * - TSF:MSG from @ref transportProcessMessage(), processes incoming message * - TSF:SANCHK from @ref transportInvokeSanityCheck(), calls transport-specific sanity check * - TSF:ROUTE from @ref transportRouteMessage(), sends message * - TSF:SEND from @ref transportSendRoute(), sends message if transport is ready (exposed) + * * Transport debug log messages: * * |E| SYS | SUB | Message | Comment * |-|------|-----------|-----------------------|--------------------------------------------------------------------- * | | TSM | INIT | | Transition to stInit state - * | | TSM | INIT | STATID,ID=%%d | Node ID is static + * | | TSM | INIT | TDC | Transport don't care mode + * | | TSM | INIT | STATID=%%d | Node ID is static * | | TSM | INIT | TSP OK | Transport device configured and fully operational * | | TSM | INIT | GW MODE | Node is set up as GW, thus omitting ID and findParent states * |!| TSM | INIT | TSP FAIL | Transport device initialization failed @@ -67,13 +71,15 @@ * |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid * | | TSM | UPL | | Transition to stUplink state * | | TSM | UPL | OK | Uplink OK, GW returned ping + * | | TSF | UPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N) * |!| TSM | UPL | FAIL | Uplink check failed, i.e. GW could not be pinged * | | TSM | READY | | Transition to stReady, i.e. transport is ready and fully operational + * | | TSM | READY | SRT | Save routing table * |!| TSM | READY | UPL FAIL,SNP | Too many failed uplink transmissions, search new parent * |!| TSM | READY | FAIL,STATP | Too many failed uplink transmissions, static parent enforced - * | | TSM | FAILURE | | Transition to stFailure state - * | | TSM | FAILURE | PDT | Power-down transport - * | | TSM | FAILURE | RE-INIT | Attempt to re-initialize transport + * | | TSM | FAIL | CNT=%%d | Transition to stFailure state, consecutive failure counter (CNT) + * | | TSM | FAIL | PDT | Power-down transport + * | | TSM | FAIL | RE-INIT | Attempt to re-initialize transport * | | TSF | CHKUPL | OK | Uplink OK * | | TSF | CHKUPL | OK,FCTRL | Uplink OK, flood control prevents pinging GW in too short intervals * | | TSF | CHKUPL | DGWC,O=%%d,N=%%d | Uplink check revealed changed network topology, old distance (O), new distance (N) @@ -95,7 +101,8 @@ * | | TSF | MSG | FWD BC MSG | Controlled broadcast message forwarding * | | TSF | MSG | REL MSG | Relay message * | | TSF | MSG | REL PxNG,HP=%%d | Relay PING/PONG message, increment hop counter (HP) - * |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch + * |!| TSF | MSG | LEN,%%d!=%%d | Invalid message length, (actual!=expected) + * |!| TSF | MSG | PVER,%%d!=%%d | Message protocol version mismatch (actual!=expected) * |!| TSF | MSG | SIGN VERIFY FAIL | Signing verification failed * |!| TSF | MSG | REL MSG,NORP | Node received a message for relaying, but node is not a repeater, message skipped * |!| TSF | MSG | SIGN FAIL | Signing message failed @@ -103,6 +110,8 @@ * | | TSF | SANCHK | OK | Sanity check passed * |!| TSF | SANCHK | FAIL | Sanity check failed, attempt to re-initialize radio * | | TSF | CRT | OK | Clearing routing table successful + * | | TSF | LRT | OK | Loading routing table successful + * | | TSF | SRT | OK | Saving routing table successful * |!| TSF | ROUTE | FPAR ACTIVE | Finding parent active, message not sent * |!| TSF | ROUTE | DST %%d UNKNOWN | Routing for destination (DST) unknown, send message to parent * |!| TSF | SEND | TNR | Transport not ready, message cannot be sent @@ -123,8 +132,9 @@ * - t=msg type * - pt=payload type * - l=length - * - ft=failed uplink transmission counter * - sg=signing flag + * - ft=failed uplink transmission counter + * - st=send status, OK=success, NACK=no radio ACK received * * @brief API declaration for MyTransport * @@ -139,15 +149,50 @@ #if defined(MY_DEBUG) #define TRANSPORT_DEBUG(x,...) debug(x, ##__VA_ARGS__) //!< debug #else - #define TRANSPORT_DEBUG(x,...) //!< debug NULL + #define TRANSPORT_DEBUG(x,...) //!< debug NULL #endif #if defined(MY_REPEATER_FEATURE) - #define TRANSMISSION_FAILURES 10 //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes + #define MY_TRANSPORT_MAX_TX_FAILURES (10u) //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes #else - #define TRANSMISSION_FAILURES 5 //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes + #define MY_TRANSPORT_MAX_TX_FAILURES (5u) //!< search for a new parent node after this many transmission failures, lower threshold for non-repeating nodes #endif +#define MY_TRANSPORT_MAX_TSM_FAILURES (7u) //!< Max. number of consecutive TSM failure state entries (3bits) + +#ifndef MY_TRANSPORT_TIMEOUT_FAILURE_STATE + #define MY_TRANSPORT_TIMEOUT_FAILURE_STATE (10*1000ul) //!< Duration failure state (in ms) +#endif +#ifndef MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE + #define MY_TRANSPORT_TIMEOUT_EXT_FAILURE_STATE (60*1000ul) //!< Duration extended failure state (in ms) +#endif +#ifndef MY_TRANSPORT_STATE_TIMEOUT_MS + #define MY_TRANSPORT_STATE_TIMEOUT_MS (2*1000ul) //!< general state timeout (in ms) +#endif +#ifndef MY_TRANSPORT_CHKUPL_INTERVAL_MS + #define MY_TRANSPORT_CHKUPL_INTERVAL_MS (10*1000ul) //!< Interval to re-check uplink (in ms) +#endif +#ifndef MY_TRANSPORT_STATE_RETRIES + #define MY_TRANSPORT_STATE_RETRIES (3u) //!< retries before switching to FAILURE +#endif + +#define AUTO (255u) //!< ID 255 is reserved +#define BROADCAST_ADDRESS (255u) //!< broadcasts are addressed to ID 255 +#define DISTANCE_INVALID (255u) //!< invalid distance when searching for parent +#define MAX_HOPS (254u) //!< maximal mumber of hops for ping/pong +#define INVALID_HOPS (255u) //!< invalid hops +#define MAX_SUBSEQ_MSGS (5u) //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue) + +// parent node check +#if defined(MY_PARENT_NODE_IS_STATIC) && !defined(MY_PARENT_NODE_ID) + #error MY_PARENT_NODE_IS_STATIC but no MY_PARENT_NODE_ID defined! +#endif + +#define _autoFindParent (bool)(MY_PARENT_NODE_ID == AUTO) //!< returns true if static parent id is undefined +#define isValidDistance(distance) (bool)(distance!=DISTANCE_INVALID) //!< returns true if distance is valid +#define isValidParent(parent) (bool)(parent != AUTO) //!< returns true if parent is valid + +// RX queue #if defined(MY_RX_MESSAGE_BUFFER_FEATURE) #if defined(MY_RADIO_RFM69) #error Receive message buffering not supported for RFM69! @@ -159,20 +204,6 @@ #error Receive message buffering requires message buffering feature enabled! #endif -#define TIMEOUT_FAILURE_STATE 10000 //!< duration failure state -#define STATE_TIMEOUT 2000 //!< general state timeout -#define STATE_RETRIES 3 //!< retries before switching to FAILURE -#define AUTO 255 //!< ID 255 is reserved for auto initialization of nodeId. -#define BROADCAST_ADDRESS ((uint8_t)255) //!< broadcasts are addressed to ID 255 -#define DISTANCE_INVALID ((uint8_t)255) //!< invalid distance when searching for parent -#define MAX_HOPS ((uint8_t)254) //!< maximal mumber of hops for ping/pong -#define INVALID_HOPS ((uint8_t)255) //!< invalid hops -#define MAX_SUBSEQ_MSGS 5 //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue) -#define CHKUPL_INTERVAL ((uint32_t)10000) //!< Minimum time interval to re-check uplink - -#define _autoFindParent (bool)(MY_PARENT_NODE_ID == AUTO) //!< returns true if static parent id is undefined -#define isValidDistance(distance) (bool)(distance!=DISTANCE_INVALID) //!< returns true if distance is valid -#define isValidParent(parent) (bool)(parent != AUTO) //!< returns true if parent is valid /** * @brief SM state @@ -190,67 +221,83 @@ struct transportState { * This structure stores transport status and SM variables */ typedef struct { + // SM variables transportState* currentState; //!< pointer to current fsm state uint32_t stateEnter; //!< state enter timepoint + // general transport variables uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding - uint32_t lastSanityCheck; //!< last sanity check // 8 bits bool findingParentNode : 1; //!< flag finding parent node is active bool preferredParentFound : 1; //!< flag preferred parent found bool uplinkOk : 1; //!< flag uplink ok bool pingActive : 1; //!< flag ping active bool transportActive : 1; //!< flag transport active - uint8_t reserved : 3; //!< reserved + uint8_t stateRetries : 3; //!< retries / state re-enter (max 7) // 8 bits - uint8_t retries : 4; //!< retries / state re-enter - uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions + uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions (max 15) + uint8_t failureCounter : 3; //!< counter for TSM failures (max 7) + bool MsgReceived : 1; //!< flag message received // 8 bits - uint8_t pingResponse; //!< stores hops received in I_PONG -} __attribute__((packed)) transportSM; + uint8_t pingResponse; //!< stores I_PONG hops +} transportSM; +/** +* @brief RAM routing table +*/ +typedef struct { + uint8_t route[SIZE_ROUTES]; //!< route for node +} routingTable; // PRIVATE functions /** -* @brief Initialize SM variables and transport HW +* @brief Initialise SM variables and transport HW */ -void stInitTransition(); +void stInitTransition(void); +/** +* @brief Initialise transport +*/ +void stInitUpdate(void); /** * @brief Find parent */ -void stParentTransition(); +void stParentTransition(void); /** * @brief Verify find parent responses */ -void stParentUpdate(); +void stParentUpdate(void); /** * @brief Send ID request */ -void stIDTransition(); +void stIDTransition(void); /** * @brief Verify ID response and GW link */ -void stIDUpdate(); +void stIDUpdate(void); /** * @brief Send uplink ping request */ -void stUplinkTransition(); +void stUplinkTransition(void); +/** +* @brief Verify uplink response +*/ +void stUplinkUpdate(void); /** * @brief Set transport OK */ -void stReadyTransition(); +void stReadyTransition(void); /** * @brief Monitor transport link */ -void stReadyUpdate(); +void stReadyUpdate(void); /** * @brief Transport failure and power down radio */ -void stFailureTransition(); +void stFailureTransition(void); /** * @brief Re-initialize transport after timeout */ -void stFailureUpdate(); +void stFailureUpdate(void); /** * @brief Switch SM state * @param newState New state to switch SM to @@ -259,30 +306,30 @@ void transportSwitchSM(transportState& newState); /** * @brief Update SM state */ -void transportUpdateSM(); +void transportUpdateSM(void); /** * @brief Request time in current SM state * @return ms in current state */ -uint32_t transportTimeInState(); +uint32_t transportTimeInState(void); /** * @brief Call transport driver sanity check */ -void transportInvokeSanityCheck(); +void transportInvokeSanityCheck(void); /** * @brief Process all pending messages in RX FIFO */ -void transportProcessFIFO(); +void transportProcessFIFO(void); /** * @brief Receive message from RX FIFO and process */ -void transportProcessMessage(); +void transportProcessMessage(void); /** * @brief Assign node ID * @param newNodeId New node ID * @return true if node ID valid and successfully assigned */ -bool transportAssignNodeID(uint8_t newNodeId); +bool transportAssignNodeID(const uint8_t newNodeId); /** * @brief Wait and process messages for a defined amount of time until specified message received * @param ms Time to wait and process incoming messages in ms @@ -290,13 +337,13 @@ bool transportAssignNodeID(uint8_t newNodeId); * @param msgtype Specific message type * @return true if specified command received within waiting time */ -bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype); +bool transportWait(const uint32_t ms, const uint8_t cmd, const uint8_t msgtype); /** * @brief Ping node * @param targetId Node to be pinged * @return hops from pinged node or 255 if no answer received within 2000ms */ -uint8_t transportPingNode(uint8_t targetId); +uint8_t transportPingNode(const uint8_t targetId); /** * @brief Send and route message according to destination * @@ -318,42 +365,80 @@ bool transportSendRoute(MyMessage &message); * @param message * @return true if message sent successfully */ -bool transportSendWrite(uint8_t to, MyMessage &message); +bool transportSendWrite(const uint8_t to, MyMessage &message); /** * @brief Check uplink to GW, includes flooding control * @param force to override flood control timer * @return true if uplink ok */ -bool transportCheckUplink(bool force); +bool transportCheckUplink(const bool force=false); // PUBLIC functions /** * @brief Initialize transport and SM */ -void transportInitialize(); +void transportInitialize(void); /** * @brief Process FIFO msg and update SM */ -void transportProcess(); +void transportProcess(void); /** * @brief Flag transport ready * @return true if transport is initialize and ready */ -bool isTransportReady(); +bool isTransportReady(void); /** * @brief Flag searching parent ongoing * @return true if transport is searching for parent */ -bool isTransportSearchingParent(); +bool isTransportSearchingParent(void); +/** +* @brief Flag TSM extended failure +* @return true if TSM had too many consecutive failure state entries +*/ +bool isTransportExtendedFailure(void); +/** +* @brief Flag valid message received +* @return true if valid message received, needs to be reset if used +*/ +bool isMessageReceived(void); +/** +* @brief Reset message received flag +*/ +void resetMessageReceived(void); /** * @brief Clear routing table */ -void transportClearRoutingTable(); +void transportClearRoutingTable(void); /** -* @brief Return heart beat, i.e. ms in current state +* @brief Return heart beat +* @return MS in current state */ -uint32_t transportGetHeartbeat(); +uint32_t transportGetHeartbeat(void); +/** +* @brief Load routing table from EEPROM to RAM. +* Only for GW devices with enough RAM, i.e. ESP8266, RPI Sensebender GW, etc. +* Atmega328 has only limited amount of RAM +*/ +void transportLoadRoutingTable(void); +/** +* @brief Save routing table to EEPROM. +*/ +void transportSaveRoutingTable(void); +/** +* @brief Update routing table +* @param node +* @param route +*/ +void transportSetRoute(const uint8_t node, const uint8_t route); +/** +* @brief Load route to node +* @param node +* @return route to node +*/ +uint8_t transportGetRoute(const uint8_t node); + // interface functions for radio driver