Merge pull request #634 from tekka007/transportSMupdate

Allow node to enter loop() if transport is NOK
This commit is contained in:
Henrik Ekblad
2016-11-24 21:18:17 +01:00
committed by GitHub
9 changed files with 350 additions and 253 deletions
+9 -8
View File
@@ -133,12 +133,6 @@
*/
//#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
@@ -178,6 +172,14 @@
*/
#define MY_CORE_COMPATIBILITY_CHECK
/**
* @def MY_TRANSPORT_WAIT_READY_MS
* @brief Timeout in MS until transport is ready during startup, set to 0 for no timeout
*/
#ifndef MY_TRANSPORT_WAIT_READY_MS
#define MY_TRANSPORT_WAIT_READY_MS (0ul)
#endif
/**
* @def MY_NODE_ID
* @brief Node id defaults to AUTO (tries to fetch id from controller).
@@ -946,6 +948,7 @@
#define MY_IS_RFM69HW
#define MY_PARENT_NODE_IS_STATIC
#define MY_REGISTRATION_CONTROLLER
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define MY_DEBUG_VERBOSE_RF24
#define MY_TRANSPORT_SANITY_CHECK
#define MY_RF24_IRQ_PIN
@@ -953,8 +956,6 @@
#define MY_RX_MESSAGE_BUFFER_SIZE
#define MY_NODE_LOCK_FEATURE
#define MY_REPEATER_FEATURE
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define MY_TRANSPORT_DONT_CARE_MODE
#define MY_LINUX_SERIAL_GROUPNAME
#define MY_IS_SERIAL_PTY
#define MY_RFM95_ATC_MODE_DISABLED
+2 -20
View File
@@ -239,26 +239,8 @@
#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
#if defined(MY_TRANSPORT_DONT_CARE_MODE)
#error This directive is deprecated, set MY_TRANSPORT_WAIT_READY_MS instead!
#endif
+1 -3
View File
@@ -24,8 +24,6 @@ extern bool transportSendRoute(MyMessage &message);
// global variables
extern MyMessage _msg;
extern MyMessage _msgTmp;
extern NodeConfig _nc;
inline void gatewayTransportProcess() {
if (gatewayTransportAvailable()) {
@@ -38,7 +36,7 @@ inline void gatewayTransportProcess() {
_msgTmp = _msg;
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
mSetAck(_msgTmp, true);
_msgTmp.sender = _nc.nodeId;
_msgTmp.sender = getNodeId();
_msgTmp.destination = _msg.sender;
gatewayTransportSend(_msgTmp);
}
+136 -99
View File
@@ -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
@@ -24,18 +24,28 @@
#include <unistd.h>
#endif
ControllerConfig _cc; // Configuration coming from controller
NodeConfig _nc; // Essential settings for node to work
// message buffers
MyMessage _msg; // Buffer for incoming messages
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others)
bool _nodeRegistered = false;
// core configuration
static coreConfig_t _coreConfig;
#if defined(MY_DEBUG)
char _convBuf[MAX_PAYLOAD*2+1];
#endif
void (*_timeCallback)(unsigned long); // Callback for requested time messages
// Callback for transport=ok transition
void _callbackTransportReady(void)
{
if (!_coreConfig.presentationSent) {
presentNode();
#if !defined(MY_GATEWAY_FEATURE)
_registerNode();
#endif
_coreConfig.presentationSent = true;
}
}
void _process(void) {
doYield();
@@ -68,6 +78,7 @@ void _infiniteLoop(void) {
}
void _begin(void) {
// reset wdt
hwWatchdogReset();
if (preHwInit) {
@@ -76,11 +87,14 @@ void _begin(void) {
hwInit();
debug(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER=" MYSENSORS_LIBRARY_VERSION "\n"));
CORE_DEBUG(PSTR("MCO:BGN:INIT " MY_NODE_TYPE ",CP=" MY_CAPABILITIES ",VER=" MYSENSORS_LIBRARY_VERSION "\n"));
// set defaults
_coreConfig.presentationSent = false;
// Call before() in sketch (if it exists)
if (before) {
debug(PSTR("MCO:BGN:BFR\n")); // before callback
CORE_DEBUG(PSTR("MCO:BGN:BFR\n")); // before callback
before();
}
@@ -91,8 +105,8 @@ void _begin(void) {
signerInit();
// Read latest received controller configuration from EEPROM
// Note: _cc.isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true (default)
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
// Note: _coreConfig.isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true (default)
hwReadConfigBlock((void*)&_coreConfig.controllerConfig, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(controllerConfig_t));
#if defined(MY_OTA_FIRMWARE_FEATURE)
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
@@ -100,43 +114,18 @@ void _begin(void) {
#endif
#if defined(MY_SENSOR_NETWORK)
// Save static parent id in eeprom (used by bootloader)
// Save static parent ID in eeprom (used by bootloader)
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
// Initialise transport layer
transportInitialise();
while (!isTransportReady()) {
transportProcess();
doYield();
}
#endif
#ifdef MY_NODE_LOCK_FEATURE
// Check if node has been locked down
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
// Node is locked, check if unlock pin is asserted, else hang the node
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
// Make a short delay so we are sure any large external nets are fully pulled
unsigned long enter = hwMillis();
while (hwMillis() - enter < 2) {}
if (hwDigitalRead(MY_NODE_UNLOCK_PIN) == 0) {
// Pin is grounded, reset lock counter
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
// Disable pullup
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
setIndication(INDICATION_ERR_LOCKED);
debug(PSTR("MCO:BGN:NODE UNLOCKED\n"));
} else {
// Disable pullup
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
nodeLock("LDB"); //Locked during boot
}
} else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
// Reset walue
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
}
#endif
// Register transport=ready callback
transportRegisterReadyCallback(_callbackTransportReady);
// wait until transport is ready
(void)transportWaitUntilReady(MY_TRANSPORT_WAIT_READY_MS);
#endif
_checkNodeLock();
#if defined(MY_GATEWAY_FEATURE)
#if defined(MY_INCLUSION_BUTTON_FEATURE)
inclusionInit();
@@ -145,46 +134,40 @@ void _begin(void) {
// initialise the transport driver
if (!gatewayTransportInit()) {
setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
debug(PSTR("!MCO:BGN:TSP FAIL\n"));
CORE_DEBUG(PSTR("!MCO:BGN:TSP FAIL\n"));
// Nothing more we can do
_infiniteLoop();
}
#endif
#if !defined(MY_GATEWAY_FEATURE)
presentNode();
#endif
// register node
_registerNode();
// Call sketch setup
if (setup) {
debug(PSTR("MCO:BGN:STP\n")); // setup callback
CORE_DEBUG(PSTR("MCO:BGN:STP\n")); // setup callback
setup();
}
debug(PSTR("MCO:BGN:INIT OK,ID=%d,PAR=%d,DIS=%d,REG=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance, _nodeRegistered);
CORE_DEBUG(PSTR("MCO:BGN:INIT OK,TSP=%d\n"), isTransportReady());
// reset wdt before handing over to loop
hwWatchdogReset();
}
void _registerNode(void) {
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
debug(PSTR("MCO:REG:REQ\n")); // registration request
setIndication(INDICATION_REQ_REGISTRATION);
_nodeRegistered = MY_REGISTRATION_DEFAULT;
uint8_t counter = MY_REGISTRATION_RETRIES;
// only proceed if register response received or retries exceeded
do {
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_REQUEST).set(MY_CORE_VERSION));
} while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
#else
_nodeRegistered = true;
debug(PSTR("MCO:REG:NOT NEEDED\n"));
#endif
void _registerNode(void)
{
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
CORE_DEBUG(PSTR("MCO:REG:REQ\n")); // registration request
setIndication(INDICATION_REQ_REGISTRATION);
_coreConfig.nodeRegistered = MY_REGISTRATION_DEFAULT;
uint8_t counter = MY_REGISTRATION_RETRIES;
// only proceed if register response received or retries exceeded
do {
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_REQUEST).set(MY_CORE_VERSION));
} while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
#else
_coreConfig.nodeRegistered = true;
CORE_DEBUG(PSTR("MCO:REG:NOT NEEDED\n"));
#endif
}
void presentNode(void) {
@@ -215,7 +198,7 @@ void presentNode(void) {
// Send a configuration exchange request to controller
// Node sends parent node. Controller answers with latest node configuration
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG).set(_nc.parentNodeId));
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG).set(getParentNodeId()));
// Wait configuration reply.
(void)wait(2000, C_INTERNAL, I_CONFIG);
@@ -228,16 +211,35 @@ void presentNode(void) {
}
uint8_t getNodeId(void) {
return _nc.nodeId;
uint8_t getNodeId(void)
{
#if defined(MY_SENSOR_NETWORK)
return transportGetNodeId();
#else
return 0xFF;
#endif
}
uint8_t getParentNodeId(void) {
return _nc.parentNodeId;
uint8_t getParentNodeId(void)
{
#if defined(MY_SENSOR_NETWORK)
return transportGetParentNodeId();
#else
return 0xFF;
#endif
}
ControllerConfig getConfig(void) {
return _cc;
uint8_t getDistanceGW(void)
{
#if defined(MY_SENSOR_NETWORK)
return transportGetDistanceGW();
#else
return 0xFF;
#endif
}
controllerConfig_t getConfig(void) {
return _coreConfig.controllerConfig;
}
@@ -246,7 +248,7 @@ bool _sendRoute(MyMessage &message) {
(void)message;
#endif
#if defined(MY_GATEWAY_FEATURE)
if (message.destination == _nc.nodeId) {
if (message.destination == getNodeId()) {
// This is a message sent from a sensor attached on the gateway node.
// Pass it directly to the gateway transport layer.
return gatewayTransportSend(message);
@@ -260,16 +262,16 @@ bool _sendRoute(MyMessage &message) {
}
bool send(MyMessage &message, const bool enableAck) {
message.sender = _nc.nodeId;
message.sender = getNodeId();
mSetCommand(message, C_SET);
mSetRequestAck(message, enableAck);
#if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
if (_nodeRegistered) {
if (_coreConfig.nodeRegistered) {
return _sendRoute(message);
}
else {
debug(PSTR("!MCO:SND:NODE NOT REG\n")); // node not registered
CORE_DEBUG(PSTR("!MCO:SND:NODE NOT REG\n")); // node not registered
return false;
}
#else
@@ -300,7 +302,7 @@ bool sendSketchInfo(const char *name, const char *version, const bool ack) {
if (name) {
result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, ack).set(name));
}
if (version) {
if (version) {
result &= _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, ack).set(version));
}
return result;
@@ -328,15 +330,15 @@ bool _processInternalMessages(void) {
}
else if (type == I_REGISTRATION_RESPONSE) {
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
_nodeRegistered = _msg.getBool();
_coreConfig.nodeRegistered = _msg.getBool();
setIndication(INDICATION_GOT_REGISTRATION);
debug(PSTR("MCO:PIM:NODE REG=%d\n"), _nodeRegistered); // node registration
CORE_DEBUG(PSTR("MCO:PIM:NODE REG=%d\n"), _coreConfig.nodeRegistered); // node registration
#endif
}
else if (type == I_CONFIG) {
// Pick up configuration from controller (currently only metric/imperial) and store it in eeprom if changed
_cc.isMetric = _msg.data[0] == 0x00 || _msg.data[0] == 'M'; // metric if null terminated or M
hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, _cc.isMetric);
_coreConfig.controllerConfig.isMetric = _msg.data[0] == 0x00 || _msg.data[0] == 'M'; // metric if null terminated or M
hwWriteConfigBlock((void*)&_coreConfig.controllerConfig, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(controllerConfig_t));
}
else if (type == I_PRESENTATION) {
// Re-send node presentation to controller
@@ -369,7 +371,7 @@ bool _processInternalMessages(void) {
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);
CORE_DEBUG(PSTR("MCO:PIM:ROUTE N=%d,R=%d\n"), cnt, route);
uint8_t outBuf[2] = { (uint8_t)cnt,route };
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG).set(outBuf, 2));
wait(200);
@@ -442,14 +444,14 @@ void wait(const uint32_t waitingMS) {
}
}
bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgtype) {
bool wait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType) {
const uint32_t enteringMS = hwMillis();
// invalidate msg type
_msg.type = !msgtype;
_msg.type = !msgType;
bool expectedResponse = false;
while ( (hwMillis() - enteringMS < waitingMS) && !expectedResponse ) {
_process();
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType);
}
return expectedResponse;
}
@@ -465,7 +467,7 @@ void doYield(void) {
}
int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, const uint8_t mode2) {
debug(PSTR("MCO:SLP:MS=%lu,SMS=%d,I1=%d,M1=%d,I2=%d,M2=%d\n"), sleepingMS, smartSleep, interrupt1, mode1, interrupt2, mode2);
CORE_DEBUG(PSTR("MCO:SLP:MS=%lu,SMS=%d,I1=%d,M1=%d,I2=%d,M2=%d\n"), sleepingMS, smartSleep, interrupt1, mode1, interrupt2, mode2);
// OTA FW feature: do not sleep if FW update ongoing
#if defined(MY_OTA_FIRMWARE_FEATURE)
if (isFirmwareUpdateOngoing()) {
@@ -482,7 +484,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
(void)interrupt2;
(void)mode2;
debug(PSTR("!MCO:SLP:REP\n")); // sleeping not possible, repeater feature enabled
CORE_DEBUG(PSTR("!MCO:SLP:REP\n")); // sleeping not possible, repeater feature enabled
wait(sleepingMS);
return MY_SLEEP_NOT_POSSIBLE;
#else
@@ -490,7 +492,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
#if defined(MY_SENSOR_NETWORK)
// Do not sleep if transport not ready
if (!isTransportReady()) {
debug(PSTR("!MCO:SLP:TNR\n")); // sleeping not possible, transport not ready
CORE_DEBUG(PSTR("!MCO:SLP:TNR\n")); // sleeping not possible, transport not ready
const uint32_t sleepEnterMS = hwMillis();
uint32_t sleepDeltaMS = 0;
while (!isTransportReady() && (sleepDeltaMS < sleepingTimeMS) && (sleepDeltaMS < MY_SLEEP_TRANSPORT_RECONNECT_TIMEOUT_MS)) {
@@ -500,7 +502,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
// sleep remainder
if (sleepDeltaMS < sleepingTimeMS) {
sleepingTimeMS -= sleepDeltaMS; // calculate remaining sleeping time
debug(PSTR("MCO:SLP:MS=%lu\n"), sleepingTimeMS);
CORE_DEBUG(PSTR("MCO:SLP:MS=%lu\n"), sleepingTimeMS);
}
else {
// no sleeping time left
@@ -516,7 +518,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
}
#if defined(MY_SENSOR_NETWORK)
debug(PSTR("MCO:SLP:TPD\n")); // sleep, power down transport
CORE_DEBUG(PSTR("MCO:SLP:TPD\n")); // sleep, power down transport
transportPowerDown();
#endif
@@ -545,7 +547,7 @@ int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep, const uint8_t in
}
setIndication(INDICATION_WAKEUP);
debug(PSTR("MCO:SLP:WUP=%d\n"), result); // sleep wake-up
CORE_DEBUG(PSTR("MCO:SLP:WUP=%d\n"), result); // sleep wake-up
return result;
#endif
}
@@ -580,22 +582,57 @@ int8_t smartSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t i
}
void _nodeLock(const char* str)
{
#ifdef MY_NODE_LOCK_FEATURE
void nodeLock(const char* str) {
// Make sure EEPROM is updated to locked status
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
while (1) {
setIndication(INDICATION_ERR_LOCKED);
debug(PSTR("MCO:NLK:NODE LOCKED. TO UNLOCK, GND PIN %d AND RESET\n"), MY_NODE_UNLOCK_PIN);
CORE_DEBUG(PSTR("MCO:NLK:NODE LOCKED. TO UNLOCK, GND PIN %d AND RESET\n"), MY_NODE_UNLOCK_PIN);
doYield();
(void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED).set(str));
#if defined(MY_SENSOR_NETWORK)
transportPowerDown();
debug(PSTR("MCO:NLK:TPD\n")); // power down transport
CORE_DEBUG(PSTR("MCO:NLK:TPD\n")); // power down transport
#endif
setIndication(INDICATION_SLEEP);
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
setIndication(INDICATION_WAKEUP);
}
}
#else
(void)str;
#endif
}
void _checkNodeLock(void)
{
#ifdef MY_NODE_LOCK_FEATURE
// Check if node has been locked down
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
// Node is locked, check if unlock pin is asserted, else hang the node
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
// Make a short delay so we are sure any large external nets are fully pulled
unsigned long enter = hwMillis();
while (hwMillis() - enter < 2) {}
if (hwDigitalRead(MY_NODE_UNLOCK_PIN) == 0) {
// Pin is grounded, reset lock counter
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
// Disable pullup
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
setIndication(INDICATION_ERR_LOCKED);
CORE_DEBUG(PSTR("MCO:BGN:NODE UNLOCKED\n"));
}
else {
// Disable pullup
hwPinMode(MY_NODE_UNLOCK_PIN, INPUT);
_nodeLock("LDB"); //Locked during boot
}
}
else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
// Reset walue
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
}
#endif
}
+30 -29
View File
@@ -41,8 +41,9 @@
* |-|------|-------|-----------------------------------------------|----------------------------------------------------------------------------
* | | MCO | BGN | INIT %%s,CP=%%s,LIB=%%s | Core initialization, capabilities (CP), library version (VER)
* | | MCO | BGN | BFR | Callback before()
* | | MCO | BGN | MTR | MY_TRANSPORT_RELAXED enabled
* | | 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 | INIT OK,TSP=%%d | Core initialised, transport status (TSP), 1=initialised, 0=not initialised
* | | MCO | BGN | NODE UNLOCKED | Node successfully unlocked (see signing chapter)
* |!| MCO | BGN | TSP FAIL | Transport initialization failed
* | | MCO | REG | REQ | Registration request
@@ -85,37 +86,34 @@
#ifdef MY_DEBUG
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug, to be removed (follow-up PR)
#define CORE_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
#else
#define debug(x,...) //!< debug NULL
#define debug(x,...) //!< debug NULL, to be removed (follow-up PR)
#define CORE_DEBUG(x,...) //!< debug NULL
#endif
/**
* @brief Node configuration
*
* This structure stores node-related configurations
*/
struct NodeConfig {
uint8_t nodeId; //!< Current node id
uint8_t parentNodeId; //!< Where this node sends its messages
uint8_t distance; //!< This nodes distance to sensor net gateway (number of hops)
};
/**
* @brief Controller configuration
*
* This structure stores controllerrelated configurations
* This structure stores controller-related configurations
*/
struct ControllerConfig {
typedef struct {
uint8_t isMetric; //!< Flag indicating if metric or imperial measurements are used
};
} controllerConfig_t;
/**
* @brief Node core configuration
*/
typedef struct {
controllerConfig_t controllerConfig; //!< Controller config
// 8 bit
bool nodeRegistered : 1; //!< Flag node registered
bool presentationSent : 1; //!< Flag presentation sent
uint8_t reserved : 6; //!< reserved
} coreConfig_t;
extern NodeConfig _nc; //!< Node config
extern MyMessage _msg; //!< Buffer for incoming messages
extern MyMessage _msgTmp; //!< Buffer for temporary messages (acks and nonces among others)
#ifdef MY_DEBUG
extern char _convBuf[MAX_PAYLOAD * 2 + 1];
#endif
// **** public functions ********
@@ -203,7 +201,7 @@ bool requestTime(const bool ack = false);
/**
* Returns the most recent node configuration received from controller
*/
ControllerConfig getConfig(void);
controllerConfig_t getControllerConfig(void);
/**
* Save a state (in local EEPROM). Good for actuators to "remember" state between
@@ -335,20 +333,23 @@ int8_t smartSleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t i
int8_t _sleep(const uint32_t sleepingMS, const bool smartSleep = false, const uint8_t interrupt1 = INTERRUPT_NOT_DEFINED, const uint8_t mode1 = MODE_NOT_DEFINED, const uint8_t interrupt2 = INTERRUPT_NOT_DEFINED, const uint8_t mode2 = MODE_NOT_DEFINED);
#ifdef MY_NODE_LOCK_FEATURE
/**
* @ingroup MyLockgrp
* @ingroup internals
* @brief Lock a node and transmit provided message with 30m intervals
*
* This function is called if suspicious activity has exceeded the threshold (see
* @ref ATTACK_COUNTER_MAX). Unlocking with a normal Arduino bootloader require erasing the EEPROM
* while unlocking with a custom bootloader require holding @ref UNLOCK_PIN low during power on/reset.
* @ref MY_NODE_LOCK_COUNTER_MAX). Unlocking with a normal Arduino bootloader require erasing the EEPROM
* while unlocking with a custom bootloader require holding @ref MY_NODE_UNLOCK_PIN low during power on/reset.
*
* @param str The string to transmit.
*/
void nodeLock(const char* str);
#endif
void _nodeLock(const char* str);
/**
* @brief Check node lock status and prevent node execution if locked.
*/
void _checkNodeLock(void);
// **** private functions ********
@@ -413,7 +414,7 @@ void loop(void) __attribute__((weak));
// Inline function and macros
static inline MyMessage& build(MyMessage &msg, const uint8_t destination, const uint8_t sensor, const uint8_t command, const uint8_t type, const bool ack = false) {
msg.sender = _nc.nodeId;
msg.sender = getNodeId();
msg.destination = destination;
msg.sensor = sensor;
msg.type = type;
+6 -6
View File
@@ -185,7 +185,7 @@ bool signerProcessInternal(MyMessage &msg) {
nof_nonce_requests++;
SIGN_DEBUG(PSTR("Nonce requests left until lockdown: %d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_nonce_requests);
if (nof_nonce_requests >= MY_NODE_LOCK_COUNTER_MAX) {
nodeLock("TMNR"); //Too many nonces requested
_nodeLock("TMNR"); //Too many nonces requested
}
#endif
#if defined(MY_SIGNING_SOFT)
@@ -317,7 +317,7 @@ bool signerSignMsg(MyMessage &msg) {
#if defined(MY_SIGNING_FEATURE)
// If destination is known to require signed messages and we are the sender,
// sign this message unless it is a handshake message
if (DO_SIGN(msg.destination) && msg.sender == _nc.nodeId) {
if (DO_SIGN(msg.destination) && msg.sender == getNodeId()) {
if (skipSign(msg)) {
return true;
} else {
@@ -350,7 +350,7 @@ bool signerSignMsg(MyMessage &msg) {
// After this point, only the 'last' member of the message structure is allowed to be altered if the
// message has been signed, or signature will become invalid and the message rejected by the receiver
}
} else if (_nc.nodeId == msg.sender) {
} else if (getNodeId() == msg.sender) {
mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned
}
#else
@@ -368,9 +368,9 @@ bool signerVerifyMsg(MyMessage &msg) {
// If we are a node, or we are a gateway and the sender require signatures (or just a strict gw)
// and we are the destination...
#if defined(MY_SIGNING_GW_REQUEST_SIGNATURES_FROM_ALL)
if (msg.destination == _nc.nodeId) {
if (msg.destination == getNodeId()) {
#else
if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == _nc.nodeId) {
if ((!MY_IS_GATEWAY || DO_SIGN(msg.sender)) && msg.destination == getNodeId()) {
#endif
// Internal messages of certain types are not verified
if (skipSign(msg)) {
@@ -399,7 +399,7 @@ bool signerVerifyMsg(MyMessage &msg) {
nof_failed_verifications++;
SIGN_DEBUG(PSTR("Failed verification attempts left until lockdown: %d\n"), MY_NODE_LOCK_COUNTER_MAX-nof_failed_verifications);
if (nof_failed_verifications >= MY_NODE_LOCK_COUNTER_MAX) {
nodeLock("TMFV"); //Too many failed verifications
_nodeLock("TMFV"); //Too many failed verifications
}
}
#endif
+103 -66
View File
@@ -20,23 +20,28 @@
#include "MyTransport.h"
// SM: transitions and update states
static transportState stInit = { stInitTransition, stInitUpdate };
static transportState stParent = { stParentTransition, stParentUpdate };
static transportState stID = { stIDTransition, stIDUpdate };
static transportState stUplink = { stUplinkTransition, stUplinkUpdate };
static transportState stReady = { stReadyTransition, stReadyUpdate };
static transportState stFailure = { stFailureTransition, stFailureUpdate };
static transportState_t stInit = { stInitTransition, stInitUpdate };
static transportState_t stParent = { stParentTransition, stParentUpdate };
static transportState_t stID = { stIDTransition, stIDUpdate };
static transportState_t stUplink = { stUplinkTransition, stUplinkUpdate };
static transportState_t stReady = { stReadyTransition, stReadyUpdate };
static transportState_t stFailure = { stFailureTransition, stFailureUpdate };
// transport SM variables
static transportSM _transportSM;
static transportSM_t _transportSM;
// transport configuration
static transportConfig_t _transportConfig;
// callback transportOk
transportCallback_t transportReady_cb = NULL;
// 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 routingTable_t _transportRoutingTable; //!< routing table
static uint32_t _lastRoutingTableSave; //!< last routing table dump
#endif
@@ -70,13 +75,10 @@ void stInitTransition(void) {
#endif
// Read node settings (ID, parent ID, GW distance) from EEPROM
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
hwReadConfigBlock((void*)&_transportConfig, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(transportConfig_t));
}
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"));
@@ -89,22 +91,22 @@ void stInitUpdate(void) {
#if defined(MY_GATEWAY_FEATURE)
// Set configuration for gateway
TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n"));
_nc.parentNodeId = GATEWAY_ADDRESS;
_nc.distance = 0u;
_nc.nodeId = GATEWAY_ADDRESS;
_transportConfig.parentNodeId = GATEWAY_ADDRESS;
_transportConfig.distanceGW = 0u;
_transportConfig.nodeId = GATEWAY_ADDRESS;
transportSetAddress(GATEWAY_ADDRESS);
// GW mode: skip FPAR,ID,UPL states
transportSwitchSM(stReady);
#else
if ((uint8_t)MY_NODE_ID != AUTO) {
#if MY_NODE_ID != AUTO
TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID=%d\n"),(uint8_t)MY_NODE_ID);
// Set static ID
_nc.nodeId = (uint8_t)MY_NODE_ID;
_transportConfig.nodeId = (uint8_t)MY_NODE_ID;
// Save static ID to eeprom (for bootloader)
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, (uint8_t)MY_NODE_ID);
}
#endif
// assign ID if set
if (_nc.nodeId == AUTO || transportAssignNodeID(_nc.nodeId)) {
if (_transportConfig.nodeId == AUTO || transportAssignNodeID(_transportConfig.nodeId)) {
// if node ID valid (>0 and <255), proceed to next state
transportSwitchSM(stParent);
}
@@ -125,14 +127,14 @@ void stParentTransition(void) {
#if defined(MY_PARENT_NODE_IS_STATIC)
TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"), (uint8_t)MY_PARENT_NODE_ID); // static parent
_transportSM.findingParentNode = false;
_nc.distance = 1u; // assumption, CHKUPL:GWDC will update this variable
_nc.parentNodeId = (uint8_t)MY_PARENT_NODE_ID;
_transportConfig.distanceGW = 1u; // assumption, CHKUPL:GWDC will update this variable
_transportConfig.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;
_transportConfig.distanceGW = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID
_transportConfig.parentNodeId = AUTO;
// Broadcast find parent request
(void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_REQUEST).set(""));
#endif
@@ -147,7 +149,7 @@ void stParentUpdate(void) {
#else
if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS || _transportSM.preferredParentFound) {
// timeout or preferred parent found
if (_nc.parentNodeId != AUTO) {
if (_transportConfig.parentNodeId != AUTO) {
// parent assigned
TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n")); // find parent ok
_transportSM.findingParentNode = false;
@@ -177,7 +179,7 @@ void stParentUpdate(void) {
// stID: verify and request ID if necessary
void stIDTransition(void) {
TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID
if (_nc.nodeId == AUTO) {
if (_transportConfig.nodeId == AUTO) {
// send ID request
setIndication(INDICATION_REQ_NODEID);
TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID
@@ -186,7 +188,7 @@ void stIDTransition(void) {
}
void stIDUpdate(void) {
if (_nc.nodeId != AUTO) {
if (_transportConfig.nodeId != AUTO) {
// current node ID is valid
TRANSPORT_DEBUG(PSTR("TSM:ID:OK\n"));
setIndication(INDICATION_GOT_NODEID);
@@ -214,7 +216,7 @@ void stUplinkTransition(void) {
setIndication(INDICATION_CHECK_UPLINK);
_transportSM.pingResponse = INVALID_HOPS;
_transportSM.pingActive = true;
(void)transportRouteMessage(build(_msgTmp,GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING).set((uint8_t)1));
(void)transportRouteMessage(build(_msgTmp,GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_PING).set((uint8_t)0x01));
#endif
}
@@ -224,10 +226,9 @@ void stUplinkUpdate(void) {
_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;
if (_transportSM.pingResponse != _transportConfig.distanceGW) {
TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW, _transportSM.pingResponse); // distance to GW changed
_transportConfig.distanceGW = _transportSM.pingResponse;
}
transportSwitchSM(stReady); // proceed to next state
}
@@ -253,10 +254,14 @@ void stUplinkUpdate(void) {
void stReadyTransition(void) {
// transport is ready and fully operational
TRANSPORT_DEBUG(PSTR("TSM:READY\n")); // transport is ready
TRANSPORT_DEBUG(PSTR("TSM:READY:ID=%d,PAR=%d,DIS=%d\n"), _transportConfig.nodeId, _transportConfig.parentNodeId, _transportConfig.distanceGW);
_transportSM.uplinkOk = true;
_transportSM.failureCounter = 0u; // reset failure counter
_transportSM.failedUplinkTransmissions = 0u; // reset failed uplink TX counter
// callback
if (transportReady_cb) {
transportReady_cb();
}
}
// stReadyUpdate: monitors link
@@ -311,7 +316,7 @@ void stFailureUpdate(void) {
}
}
void transportSwitchSM(transportState& newState) {
void transportSwitchSM(transportState_t& newState) {
if (_transportSM.currentState != &newState) {
_transportSM.stateRetries = 0u; // state change, reset retry counter
_transportSM.currentState = &newState; // change state
@@ -364,6 +369,19 @@ void transportInitialise(void) {
transportSwitchSM(stInit);
}
bool transportWaitUntilReady(const uint32_t waitingMS) {
// check if transport ready
TRANSPORT_DEBUG(PSTR("TSF:WUR:MS=%lu\n"), waitingMS); // timeout
uint32_t enterMS = hwMillis();
bool result = false;
while (!result && ( hwMillis() - enterMS < waitingMS || !waitingMS)) {
transportProcess();
result = isTransportReady();
doYield();
}
return result;
}
// update TSM and process incoming messages
void transportProcess(void) {
// update state machine
@@ -386,9 +404,9 @@ bool transportCheckUplink(const bool force) {
_transportSM.lastUplinkCheck = hwMillis();
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:CKU:DGWC,O=%d,N=%d\n"), _nc.distance, hopsCount); // distance to GW changed
_nc.distance = hopsCount;
if (hopsCount != _transportConfig.distanceGW) {
TRANSPORT_DEBUG(PSTR("TSF:CKU:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW, hopsCount); // distance to GW changed
_transportConfig.distanceGW = hopsCount;
}
return true;
}
@@ -401,7 +419,7 @@ bool transportCheckUplink(const bool force) {
bool transportAssignNodeID(const uint8_t newNodeId) {
// verify if ID valid
if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
_nc.nodeId = newNodeId;
_transportConfig.nodeId = newNodeId;
transportSetAddress(newNodeId);
// Write ID to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
@@ -411,14 +429,14 @@ bool transportAssignNodeID(const uint8_t newNodeId) {
else {
TRANSPORT_DEBUG(PSTR("!TSF:SID:FAIL,ID=%d\n"),newNodeId); // ID is invalid, cannot assign ID
setIndication(INDICATION_ERR_NET_FULL);
_nc.nodeId = AUTO;
_transportConfig.nodeId = AUTO;
return false;
}
}
bool transportRouteMessage(MyMessage &message) {
const uint8_t destination = message.destination;
uint8_t route = _nc.parentNodeId; // by default, all traffic is routed via parent node
uint8_t route = _transportConfig.parentNodeId; // by default, all traffic is routed via parent node
if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
TRANSPORT_DEBUG(PSTR("!TSF:RTE:FPAR ACTIVE\n")); // find parent active, message not sent
@@ -427,7 +445,7 @@ bool transportRouteMessage(MyMessage &message) {
}
if (destination == GATEWAY_ADDRESS) {
route = _nc.parentNodeId; // message to GW always routes via parent
route = _transportConfig.parentNodeId; // message to GW always routes via parent
}
else if (destination == BROADCAST_ADDRESS) {
route = BROADCAST_ADDRESS; // message to BC does not require routing
@@ -439,9 +457,9 @@ bool transportRouteMessage(MyMessage &message) {
if (route == AUTO) {
TRANSPORT_DEBUG(PSTR("!TSF:RTE:%d UNKNOWN\n"), destination); // route unknown
#if !defined(MY_GATEWAY_FEATURE)
if (message.last != _nc.parentNodeId) {
if (message.last != _transportConfig.parentNodeId) {
// message not from parent, i.e. child node - route it to parent
route = _nc.parentNodeId;
route = _transportConfig.parentNodeId;
}
else {
// route unknown and msg received from parent, send it to destination assuming in rx radius
@@ -453,14 +471,14 @@ bool transportRouteMessage(MyMessage &message) {
#endif
}
#else
route = _nc.parentNodeId; // not a repeater, all traffic routed via parent
route = _transportConfig.parentNodeId; // not a repeater, all traffic routed via parent
#endif
}
// send message
const bool result = transportSendWrite(route, message);
#if !defined(MY_GATEWAY_FEATURE)
// update counter
if (route == _nc.parentNodeId) {
if (route == _transportConfig.parentNodeId) {
if (!result) {
setIndication(INDICATION_ERR_TX);
_transportSM.failedUplinkTransmissions++;
@@ -489,16 +507,16 @@ bool transportSendRoute(MyMessage &message) {
}
// only be used inside transport
bool transportWait(const uint32_t ms, const uint8_t cmd, const uint8_t msgtype){
const uint32_t enter = hwMillis();
bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType){
const uint32_t enterMS = hwMillis();
// invalidate msg type
_msg.type = !msgtype;
_msg.type = !msgType;
bool expectedResponse = false;
while ((hwMillis() - enter < ms) && !expectedResponse) {
while ((hwMillis() - enterMS < waitingMS) && !expectedResponse) {
// process incoming messages
transportProcessFIFO();
doYield();
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType);
}
return expectedResponse;
}
@@ -506,14 +524,14 @@ bool transportWait(const uint32_t ms, const uint8_t cmd, const uint8_t msgtype){
uint8_t transportPingNode(const uint8_t targetId) {
if(!_transportSM.pingActive){
TRANSPORT_DEBUG(PSTR("TSF:PNG:SEND,TO=%d\n"), targetId);
if(targetId == _nc.nodeId) {
if(targetId == _transportConfig.nodeId) {
// pinging self
_transportSM.pingResponse = 0u;
}
else {
_transportSM.pingActive = true;
_transportSM.pingResponse = INVALID_HOPS;
(void)transportRouteMessage(build(_msgTmp, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING).set((uint8_t)1));
(void)transportRouteMessage(build(_msgTmp, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING).set((uint8_t)0x01));
// Wait for ping reply or timeout
(void)transportWait(2000, C_INTERNAL, I_PONG);
}
@@ -574,7 +592,7 @@ void transportProcessMessage(void) {
// update routing table if msg not from parent
#if defined(MY_REPEATER_FEATURE)
#if !defined(MY_GATEWAY_FEATURE)
if (last != _nc.parentNodeId) {
if (last != _transportConfig.parentNodeId) {
#else
// GW doesn't have parent
{
@@ -588,7 +606,7 @@ void transportProcessMessage(void) {
_transportSM.msgReceived = true;
// Is message addressed to this node?
if (destination == _nc.nodeId) {
if (destination == _transportConfig.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
@@ -599,7 +617,7 @@ void transportProcessMessage(void) {
_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.sender = _transportConfig.nodeId;
_msgTmp.destination = sender;
// send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
(void)transportSendRoute(_msgTmp);
@@ -627,15 +645,15 @@ void transportProcessMessage(void) {
if (isValidDistance(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 == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
// Found a neighbor closer to GW than previously found
if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) {
_transportSM.preferredParentFound = true;
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n")); // find parent, preferred parent found
}
_nc.distance = distance;
_nc.parentNodeId = sender;
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _nc.parentNodeId, _nc.distance);
_transportConfig.distanceGW = distance;
_transportConfig.parentNodeId = sender;
TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _transportConfig.parentNodeId, _transportConfig.distanceGW);
}
}
}
@@ -697,7 +715,7 @@ void transportProcessMessage(void) {
// 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
if (sender != _transportConfig.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
@@ -706,7 +724,7 @@ void transportProcessMessage(void) {
TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok
// random delay minimizes collisions
delay(hwMillis() & 0x3ff);
(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE).set(_nc.distance));
(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW));
}
else {
TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request
@@ -721,10 +739,10 @@ void transportProcessMessage(void) {
}
#if !defined(MY_GATEWAY_FEATURE)
if (type == I_DISCOVER_REQUEST) {
if (last == _nc.parentNodeId) {
if (last == _transportConfig.parentNodeId) {
// random wait to minimize collisions
delay(hwMillis() & 0x3ff);
(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE).set(_nc.parentNodeId));
(void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId));
// no return here (for fwd if repeater)
}
}
@@ -733,7 +751,7 @@ void transportProcessMessage(void) {
// 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()){
if(last == _transportConfig.parentNodeId && sender != _transportConfig.nodeId && isTransportReady()){
TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding
(void)transportRouteMessage(_msg);
}
@@ -743,7 +761,7 @@ void transportProcessMessage(void) {
if (command != C_INTERNAL) {
#if !defined(MY_GATEWAY_FEATURE)
// only proceed if message received from parent
if (last != _nc.parentNodeId) {
if (last != _transportConfig.parentNodeId) {
return;
}
#endif
@@ -818,7 +836,7 @@ void transportProcessFIFO(void) {
}
bool transportSendWrite(const uint8_t to, MyMessage &message) {
message.last = _nc.nodeId; // Update last
message.last = _transportConfig.nodeId; // Update last
// sign message if required
if (!signerSignMsg(message)) {
TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n"));
@@ -842,6 +860,25 @@ bool transportSendWrite(const uint8_t to, MyMessage &message) {
return result;
}
void transportRegisterReadyCallback(transportCallback_t cb)
{
transportReady_cb = cb;
}
uint8_t transportGetNodeId(void)
{
return _transportConfig.nodeId;
}
uint8_t transportGetParentNodeId(void)
{
return _transportConfig.parentNodeId;
}
uint8_t transportGetDistanceGW(void)
{
return _transportConfig.distanceGW;
}
void transportClearRoutingTable(void) {
for (uint16_t i = 0; i < SIZE_ROUTES; i++) {
transportSetRoute((uint8_t)i, BROADCAST_ADDRESS);
+62 -22
View File
@@ -41,6 +41,7 @@
* - 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:WUR from @ref transportWaitUntilReady(), waits until transport is ready
* - 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)
@@ -55,7 +56,6 @@
* |E| SYS | SUB | Message | Comment
* |-|------|-----------|-----------------------|---------------------------------------------------------------------
* | | TSM | INIT | | <b>Transition to stInit state</b>
* | | 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
@@ -73,8 +73,8 @@
* | | 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 | SRT | Save routing table
* | | TSM | READY | ID=%%d,PAR=%%d,DIS=%%d| <b>Transition to stReady</b> Transport ready, node ID (ID), parent node ID (PAR), distance to GW (DIS)
* |!| 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 | FAIL | CNT=%%d | <b>Transition to stFailure state</b>, consecutive failure counter (CNT)
@@ -87,6 +87,7 @@
* | | TSF | ASID | OK,ID=%%d | Node ID assigned
* |!| TSF | ASID | FAIL,ID=%%d | Assigned ID is invalid
* | | TSF | PING | SEND,TO=%%d | Send ping to destination (TO)
* | | TSF | WUR | MS=%%lu | Wait until transport ready, timeout (MS)
* | | TSF | MSG | ACK REQ | ACK message requested
* | | TSF | MSG | ACK | ACK message, do not proceed but forward to callback
* | | TSF | MSG | FPAR RES,ID=%%d,D=%%d | Response to find parent received from node (ID) with distance (D) to GW
@@ -147,11 +148,13 @@
// debug
#if defined(MY_DEBUG)
#define TRANSPORT_DEBUG(x,...) debug(x, ##__VA_ARGS__) //!< debug
#define TRANSPORT_DEBUG(x,...) hwDebugPrint(x, ##__VA_ARGS__) //!< debug
extern char _convBuf[MAX_PAYLOAD * 2 + 1];
#else
#define TRANSPORT_DEBUG(x,...) //!< debug NULL
#endif
#if defined(MY_REPEATER_FEATURE)
#define MY_TRANSPORT_MAX_TX_FAILURES (10u) //!< search for a new parent node after this many transmission failures, higher threshold for repeating nodes
#else
@@ -189,8 +192,8 @@
#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
#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)
@@ -204,25 +207,40 @@
#error Receive message buffering requires message buffering feature enabled!
#endif
/**
* @brief Callback type
*/
typedef void(*transportCallback_t)(void);
/**
/**
* @brief Node configuration
*
* This structure stores node-related configurations
*/
typedef struct {
uint8_t nodeId; //!< Current node id
uint8_t parentNodeId; //!< Where this node sends its messages
uint8_t distanceGW; //!< This nodes distance to sensor net gateway (number of hops)
} transportConfig_t;
/**
* @brief SM state
*
* This structure stores SM state definitions
*/
struct transportState {
void(*Transition)(); //!< state transition function
void(*Update)(); //!< state update function
};
typedef struct {
void(*Transition)(void); //!< state transition function
void(*Update)(void); //!< state update function
} transportState_t;
/**
* @brief Status variables and SM state
*
* This structure stores transport status and SM variables
*/
* @brief Status variables and SM state
*
* This structure stores transport status and SM variables
*/
typedef struct {
// SM variables
transportState* currentState; //!< pointer to current fsm state
transportState_t* 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
@@ -239,14 +257,14 @@ typedef struct {
bool msgReceived : 1; //!< flag message received
// 8 bits
uint8_t pingResponse; //!< stores I_PONG hops
} transportSM;
} transportSM_t;
/**
* @brief RAM routing table
*/
typedef struct {
uint8_t route[SIZE_ROUTES]; //!< route for node
} routingTable;
} routingTable_t;
// PRIVATE functions
@@ -302,7 +320,7 @@ void stFailureUpdate(void);
* @brief Switch SM state
* @param newState New state to switch SM to
*/
void transportSwitchSM(transportState& newState);
void transportSwitchSM(transportState_t& newState);
/**
* @brief Update SM state
*/
@@ -332,12 +350,12 @@ void transportProcessMessage(void);
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
* @param waitingMS Time to wait and process incoming messages in ms
* @param cmd Specific command
* @param msgtype Specific message type
* @param msgType Specific message type
* @return true if specified command received within waiting time
*/
bool transportWait(const uint32_t ms, const uint8_t cmd, const uint8_t msgtype);
bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType);
/**
* @brief Ping node
* @param targetId Node to be pinged
@@ -371,10 +389,16 @@ bool transportSendWrite(const uint8_t to, MyMessage &message);
* @param force to override flood control timer
* @return true if uplink ok
*/
bool transportCheckUplink(const bool force=false);
bool transportCheckUplink(const bool force = false);
// PUBLIC functions
/**
* @brief Wait until transport is ready
* @param waitingMS timeout in MS, set 0 (default) for no timeout, i.e. wait indefinitely. For a node in standalone mode (optional network connection) set >0 to allow a node entering the main loop() function.
* @return true if transport is ready
*/
bool transportWaitUntilReady(const uint32_t waitingMS = 0);
/**
* @brief Initialize transport and SM
*/
@@ -483,6 +507,22 @@ uint8_t transportReceive(void* data);
*/
void transportPowerDown();
/**
* @brief Get node ID
* @return node ID
*/
uint8_t transportGetNodeId(void);
/**
* @brief Get parent node ID
* @return parent node ID
*/
uint8_t transportGetParentNodeId(void);
/**
* @brief Get distance to GW
* @return distance (=hops) to GW
*/
uint8_t transportGetDistanceGW(void);
#endif // MyTransport_h
/** @}*/
+1
View File
@@ -46,6 +46,7 @@ MY_REGISTRATION_CONTROLLER LITERAL1
MY_CORE_COMPATIBILITY_CHECK LITERAL1
MY_TRANSPORT_SANITY_CHECK LITERAL1
MY_TRANSPORT_SANITY_CHECK_INTERVAL LITERAL1
MY_TRANSPORT_WAIT_READY_MS LITERAL1
MY_NODE_ID LITERAL1
MY_PARENT_NODE_ID LITERAL1
MY_PARENT_NODE_IS_STATIC LITERAL1