Various MyTransport optimisations
This commit is contained in:
+52
-13
@@ -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 <henrik.ekblad@mysensors.org>
|
||||
@@ -81,18 +81,54 @@
|
||||
//#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_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 +141,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,7 +184,10 @@
|
||||
*/
|
||||
//#define MY_PARENT_NODE_IS_STATIC
|
||||
|
||||
// Enables repeater functionality (relays messages from other nodes)
|
||||
/**
|
||||
* @def MY_REPEATER_FEATURE
|
||||
* @brief Enables repeater functionality (relays messages from other nodes)
|
||||
*/
|
||||
//#define MY_REPEATER_FEATURE
|
||||
|
||||
/**
|
||||
@@ -160,7 +198,7 @@
|
||||
* potential buffered messages.
|
||||
*/
|
||||
#ifndef MY_SMART_SLEEP_WAIT_DURATION
|
||||
#define MY_SMART_SLEEP_WAIT_DURATION 500
|
||||
#define MY_SMART_SLEEP_WAIT_DURATION (500ul)
|
||||
#endif
|
||||
|
||||
/**********************************
|
||||
@@ -199,7 +237,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
|
||||
|
||||
/**
|
||||
@@ -207,7 +245,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
|
||||
|
||||
/**
|
||||
@@ -215,7 +253,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
|
||||
|
||||
|
||||
@@ -716,4 +754,5 @@
|
||||
#define MY_RX_MESSAGE_BUFFER_FEATURE
|
||||
#define MY_RX_MESSAGE_BUFFER_SIZE
|
||||
#define MY_NODE_LOCK_FEATURE
|
||||
#define MY_REPEATER_FEATURE
|
||||
#endif
|
||||
|
||||
+10
@@ -239,6 +239,16 @@
|
||||
#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
|
||||
|
||||
// RADIO
|
||||
#if defined(MY_RADIO_NRF24) || defined(MY_RADIO_RFM69) || defined(MY_RS485)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -28,7 +28,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;
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
|
||||
+4
-1
@@ -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.
|
||||
|
||||
+3
-3
@@ -36,9 +36,9 @@
|
||||
#include <stdint.h>
|
||||
#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
|
||||
|
||||
@@ -99,7 +99,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();
|
||||
@@ -142,7 +142,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"));
|
||||
@@ -353,15 +353,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);
|
||||
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] = { cnt,route };
|
||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(outBuf, 2));
|
||||
wait(200);
|
||||
}
|
||||
}
|
||||
@@ -390,15 +390,15 @@ 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
|
||||
// delay for slow nodes *****************************
|
||||
_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
|
||||
|
||||
@@ -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:
|
||||
* - <b>MCO</b> messages emitted by MySensorsCore
|
||||
* - SUB SYSTEMS:
|
||||
@@ -44,7 +44,7 @@
|
||||
* | | MCO | BGN | STP | Callback setup()
|
||||
* | | MCO | BGN | INIT OK,ID=%%d,PAR=%%d,DIS=%%d,REG=%%d | Core initialized, parent ID (PAR), distance to GW (DIS), registration (REG)
|
||||
* | | MCO | BGN | NODE UNLOCKED | Node successfully unlocked (see signing chapter)
|
||||
* |!| MCO | BGN | TSP FAIL | Transport initialization failed
|
||||
* |!| MCO | BGN | TSP FAIL | Transport initialisation failed
|
||||
* | | MCO | REG | REQ | Registration request
|
||||
* | | MCO | REG | NOT NEEDED | No registration needed (i.e. GW)
|
||||
* |!| MCO | SND | NODE NOT REG | Node is not registered, cannot send message
|
||||
|
||||
+353
-212
@@ -6,7 +6,7 @@
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* 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,61 @@
|
||||
#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) {
|
||||
// initialise radio
|
||||
if (!transportInit()) {
|
||||
TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n"));
|
||||
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
||||
@@ -56,26 +87,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 +114,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 +152,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 +162,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 +172,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() {
|
||||
void stUplinkTransition(void) {
|
||||
#if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED)
|
||||
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);
|
||||
}
|
||||
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 +273,84 @@ 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();
|
||||
TRANSPORT_DEBUG(PSTR("TSM:READY:SRT\n")); // save routing table
|
||||
transportSaveRoutingTable();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// stFailure: entered upon HW init failure or max retries exceeded
|
||||
void stFailureTransition() {
|
||||
TRANSPORT_DEBUG(PSTR("TSM:FAILURE\n"));
|
||||
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
|
||||
#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() {
|
||||
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 +358,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 +420,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
|
||||
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
|
||||
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
|
||||
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;
|
||||
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));
|
||||
(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
|
||||
transportWait(2000, C_INTERNAL, I_PONG);
|
||||
(void)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
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -464,32 +554,33 @@ void transportProcessMessage() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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),MAX_PAYLOAD));
|
||||
// null terminate data
|
||||
_msg.data[mGetLength(_msg)] = 0x00;
|
||||
|
||||
// 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.
|
||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
|
||||
transportSetRoute(sender, last);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Is message addressed to this node?
|
||||
if (destination == _nc.nodeId) {
|
||||
// null terminate data
|
||||
_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
|
||||
@@ -502,7 +593,7 @@ void transportProcessMessage() {
|
||||
if (type == I_ID_RESPONSE) {
|
||||
#if (MY_NODE_ID == AUTO)
|
||||
// only active if node ID dynamic
|
||||
transportAssignNodeID(_msg.getByte());
|
||||
(void)transportAssignNodeID(_msg.getByte());
|
||||
#endif
|
||||
return; // no further processing required
|
||||
}
|
||||
@@ -511,16 +602,14 @@ void transportProcessMessage() {
|
||||
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 +618,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 +627,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));
|
||||
// delay for slow nodes *****************************
|
||||
#if defined(MY_GATEWAY_FEATURE) && (F_CPU<=16000000)
|
||||
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 +640,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 +661,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
|
||||
@@ -582,49 +676,61 @@ void transportProcessMessage() {
|
||||
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);
|
||||
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(false)){
|
||||
if (transportCheckUplink()) {
|
||||
_transportSM.lastUplinkCheck = hwMillis();
|
||||
TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
|
||||
// delay minimizes collisions
|
||||
// random 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));
|
||||
(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 !defined(MY_GATEWAY_FEATURE)
|
||||
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));
|
||||
(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) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
@@ -632,52 +738,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();
|
||||
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
|
||||
}
|
||||
else {
|
||||
// transport not active, nothing to be done
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
|
||||
// process all msgs in FIFO or counter exit
|
||||
while (transportAvailable() && _processedMessages--) {
|
||||
@@ -691,10 +793,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 +803,58 @@ 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));
|
||||
(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 (ok || to==BROADCAST_ADDRESS);
|
||||
return result;
|
||||
}
|
||||
|
||||
void transportClearRoutingTable(void) {
|
||||
for (uint8_t i = 0; i != 255; i++) {
|
||||
transportSetRoute(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, 256);
|
||||
#endif
|
||||
}
|
||||
|
||||
void transportSaveRoutingTable(void) {
|
||||
#if defined(MY_RAM_ROUTING_TABLE_ENABLED)
|
||||
hwWriteConfigBlock((void*)EEPROM_ROUTES_ADDRESS, (void*)&_transportRoutingTable.route, 256);
|
||||
#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
|
||||
+127
-59
@@ -6,7 +6,7 @@
|
||||
* network topology allowing messages to be routed to nodes.
|
||||
*
|
||||
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
|
||||
* 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,7 +36,7 @@
|
||||
* - TSM:ID from <b>stID</b> Check/request node ID, if dynamic node ID set
|
||||
* - TSM:UPL from <b>stUplink</b> Verify uplink connection by pinging GW
|
||||
* - TSM:READY from <b>stReady</b> Transport is ready and fully operational
|
||||
* - TSM:FAILURE from <b>stFailure</b> Failure in transport link or transport HW
|
||||
* - TSM:FAIL from <b>stFailure</b> Failure in transport link or transport HW
|
||||
* - Transport support function (<b>TSF</b>)
|
||||
* - TSF:CHKUPL from @ref transportCheckUplink(), checks connection to GW
|
||||
* - TSF:ASID from @ref transportAssignNodeID(), assigns node ID
|
||||
@@ -52,7 +52,7 @@
|
||||
* |E| SYS | SUB | Message | Comment
|
||||
* |-|------|-----------|-----------------------|---------------------------------------------------------------------
|
||||
* | | TSM | INIT | | <b>Transition to stInit state</b>
|
||||
* | | TSM | INIT | STATID,ID=%%d | Node ID is static
|
||||
* | | 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 +67,14 @@
|
||||
* |!| TSM | ID | FAIL,ID=%%d | ID verification failed, ID invalid
|
||||
* | | TSM | UPL | | <b>Transition to stUplink state</b>
|
||||
* | | 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 | | <b>Transition to stReady</b>, i.e. transport is ready and fully operational
|
||||
* |!| 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 | | <b>Transition to stFailure state</b>
|
||||
* | | TSM | FAILURE | PDT | Power-down transport
|
||||
* | | TSM | FAILURE | RE-INIT | Attempt to re-initialize transport
|
||||
* | | TSM | FAIL | CNT=%%d | <b>Transition to stFailure state</b>, 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 +96,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
|
||||
@@ -123,8 +125,9 @@
|
||||
* - <b>t</b>=msg type
|
||||
* - <b>pt</b>=payload type
|
||||
* - <b>l</b>=length
|
||||
* - <b>ft</b>=failed uplink transmission counter
|
||||
* - <b>sg</b>=signing flag
|
||||
* - <b>ft</b>=failed uplink transmission counter
|
||||
* - <b>st</b>=send status, OK=success, NACK=no radio ACK received
|
||||
*
|
||||
* @brief API declaration for MyTransport
|
||||
*
|
||||
@@ -143,11 +146,46 @@
|
||||
#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 (15u) //!< Max. number of consecutive TSM failure state entries (4bit)
|
||||
|
||||
#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 +197,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 +214,82 @@ 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 : 4; //!< counter for TSM failures (max 15)
|
||||
// 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[256]; //!< 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 +298,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 +329,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,44 +357,73 @@ 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(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 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
|
||||
|
||||
// interface functions for radio driver ************************************************
|
||||
|
||||
/**
|
||||
* @brief Initialize transport HW
|
||||
|
||||
Reference in New Issue
Block a user