diff --git a/MyConfig.h b/MyConfig.h index 1a1de4b..2addec2 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -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 diff --git a/MySensors.h b/MySensors.h index 2276d09..b012d19 100644 --- a/MySensors.h +++ b/MySensors.h @@ -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 diff --git a/core/MyGatewayTransport.cpp b/core/MyGatewayTransport.cpp index fcf2b53..c4aad27 100644 --- a/core/MyGatewayTransport.cpp +++ b/core/MyGatewayTransport.cpp @@ -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); } diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index fe9e7a7..606fb31 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2015 Sensnology AB + * Copyright (C) 2013-2016 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -24,18 +24,28 @@ #include #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 +} diff --git a/core/MySensorsCore.h b/core/MySensorsCore.h index 094f370..cf5c9f4 100644 --- a/core/MySensorsCore.h +++ b/core/MySensorsCore.h @@ -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; diff --git a/core/MySigning.cpp b/core/MySigning.cpp index a9be61f..bdfa4e9 100644 --- a/core/MySigning.cpp +++ b/core/MySigning.cpp @@ -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 diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 238918b..180e939 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -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); diff --git a/core/MyTransport.h b/core/MyTransport.h index 305b53f..4508cc9 100644 --- a/core/MyTransport.h +++ b/core/MyTransport.h @@ -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 | | Transition to stInit state - * | | 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 | | Transition to stReady, i.e. transport is ready and fully operational * | | TSM | READY | SRT | Save routing table + * | | TSM | READY | ID=%%d,PAR=%%d,DIS=%%d| Transition to stReady 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 | Transition to stFailure state, 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 /** @}*/ diff --git a/keywords.txt b/keywords.txt index 27f94f4..b39e424 100644 --- a/keywords.txt +++ b/keywords.txt @@ -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