TransportProcess cleanup
This commit is contained in:
@@ -81,6 +81,40 @@
|
|||||||
//#define MY_RADIO_RFM69
|
//#define MY_RADIO_RFM69
|
||||||
//#define MY_RS485
|
//#define MY_RS485
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_REGISTRATION_FEATURE
|
||||||
|
* @brief If enabled, node has to register to gateway/controller before allowed to send sensor data.
|
||||||
|
*/
|
||||||
|
#define MY_REGISTRATION_FEATURE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_REGISTRATION_RETRIES
|
||||||
|
* @brief Number of registration retries if no reply received from GW/controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MY_REGISTRATION_RETRIES
|
||||||
|
#define MY_REGISTRATION_RETRIES 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_REGISTRATION_DEFAULT
|
||||||
|
* @brief Node registration default - this applies if no registration response is recieved from controller
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MY_REGISTRATION_DEFAULT true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_REGISTRATION_CONTROLLER
|
||||||
|
* @brief If enabled, node registration request has to be handled by controller
|
||||||
|
*/
|
||||||
|
// #define MY_REGISTRATION_CONTROLLER
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_CORE_COMPATIBILITY_CHECK
|
||||||
|
* @brief If enabled, library compatibility is checked during node registration. Incompatible libraries are unable to send sensor data.
|
||||||
|
*/
|
||||||
|
#define MY_CORE_COMPATIBILITY_CHECK
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @def MY_NODE_ID
|
* @def MY_NODE_ID
|
||||||
* @brief Node id defaults to AUTO (tries to fetch id from controller).
|
* @brief Node id defaults to AUTO (tries to fetch id from controller).
|
||||||
@@ -97,6 +131,12 @@
|
|||||||
#define MY_PARENT_NODE_ID AUTO
|
#define MY_PARENT_NODE_ID AUTO
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @def MY_PARENT_NODE_IS_STATIC
|
||||||
|
* @brief Enable MY_PARENT_NODE_IS_STATIC to disable fall back if parent node fails
|
||||||
|
*/
|
||||||
|
//#define MY_PARENT_NODE_IS_STATIC
|
||||||
|
|
||||||
// Enables repeater functionality (relays messages from other nodes)
|
// Enables repeater functionality (relays messages from other nodes)
|
||||||
// #define MY_REPEATER_FEATURE
|
// #define MY_REPEATER_FEATURE
|
||||||
|
|
||||||
@@ -646,4 +686,6 @@
|
|||||||
#define MY_SIGNING_REQUEST_SIGNATURES
|
#define MY_SIGNING_REQUEST_SIGNATURES
|
||||||
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
|
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
|
||||||
#define MY_IS_RFM69HW
|
#define MY_IS_RFM69HW
|
||||||
|
#define MY_PARENT_NODE_IS_STATIC
|
||||||
|
#define MY_DEBUG_VERBOSE_RF24
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -172,6 +172,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// FLASH
|
||||||
|
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||||
|
#include "drivers/SPIFlash/SPIFlash.cpp"
|
||||||
|
#include "core/MyOTAFirmwareUpdate.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
// GATEWAY - TRANSPORT
|
// GATEWAY - TRANSPORT
|
||||||
#if defined(MY_GATEWAY_MQTT_CLIENT)
|
#if defined(MY_GATEWAY_MQTT_CLIENT)
|
||||||
@@ -245,11 +250,6 @@
|
|||||||
#include "drivers/AVR/DigitalIO/DigitalIO.h"
|
#include "drivers/AVR/DigitalIO/DigitalIO.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// FLASH
|
|
||||||
#ifdef MY_OTA_FIRMWARE_FEATURE
|
|
||||||
#include "drivers/SPIFlash/SPIFlash.cpp"
|
|
||||||
#include "core/MyOTAFirmwareUpdate.cpp"
|
|
||||||
#endif
|
|
||||||
#include "core/MyTransport.cpp"
|
#include "core/MyTransport.cpp"
|
||||||
#if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
|
#if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
|
||||||
#error Only one forward link driver can be activated
|
#error Only one forward link driver can be activated
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "MyGatewayTransport.h"
|
#include "MyGatewayTransport.h"
|
||||||
|
|
||||||
extern boolean transportSendRoute(MyMessage &message);
|
extern bool transportSendRoute(MyMessage &message);
|
||||||
extern MyMessage _msg;
|
extern MyMessage _msg;
|
||||||
|
|
||||||
inline void gatewayTransportProcess() {
|
inline void gatewayTransportProcess() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -34,6 +34,8 @@ typedef enum {
|
|||||||
INDICATION_GOT_PARENT, //!< Found parent node.
|
INDICATION_GOT_PARENT, //!< Found parent node.
|
||||||
INDICATION_REQ_NODEID, //!< Request node ID.
|
INDICATION_REQ_NODEID, //!< Request node ID.
|
||||||
INDICATION_GOT_NODEID, //!< Got a node ID.
|
INDICATION_GOT_NODEID, //!< Got a node ID.
|
||||||
|
INDICATION_REQ_REGISTRATION, //!< Request node registration.
|
||||||
|
INDICATION_GOT_REGISTRATION, //!< Got registration reponse.
|
||||||
INDICATION_REBOOT, //!< Rebooting node.
|
INDICATION_REBOOT, //!< Rebooting node.
|
||||||
INDICATION_PRESENT, //!< Presenting node to gateway.
|
INDICATION_PRESENT, //!< Presenting node to gateway.
|
||||||
INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested.
|
INDICATION_CLEAR_ROUTING, //!< Clear rrouting table requested.
|
||||||
|
|||||||
@@ -188,8 +188,8 @@ typedef enum {
|
|||||||
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
|
I_LOCKED = 23, //!< Node is locked (reason in string-payload)
|
||||||
I_PING = 24, //!< Ping sent to node, payload incremental hop counter
|
I_PING = 24, //!< Ping sent to node, payload incremental hop counter
|
||||||
I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter
|
I_PONG = 25, //!< In return to ping, sent back to sender, payload incremental hop counter
|
||||||
I_REGISTER_REQUEST = 26, //!< Register request to GW
|
I_REGISTRATION_REQUEST = 26, //!< Register request to GW
|
||||||
I_REGISTER_RESPONSE = 27, //!< Register response from GW
|
I_REGISTRATION_RESPONSE = 27, //!< Register response from GW
|
||||||
I_DEBUG = 28 //!< Debug message
|
I_DEBUG = 28 //!< Debug message
|
||||||
} mysensor_internal;
|
} mysensor_internal;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -24,18 +24,17 @@ NodeConfig _nc; // Essential settings for node to work
|
|||||||
MyMessage _msg; // Buffer for incoming messages.
|
MyMessage _msg; // Buffer for incoming messages.
|
||||||
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
|
MyMessage _msgTmp; // Buffer for temporary messages (acks and nonces among others).
|
||||||
|
|
||||||
#ifdef MY_DEBUG
|
boolean _nodeRegistered = false;
|
||||||
|
|
||||||
|
#if defined(MY_DEBUG)
|
||||||
char _convBuf[MAX_PAYLOAD*2+1];
|
char _convBuf[MAX_PAYLOAD*2+1];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t _heartbeat = 0;
|
|
||||||
void (*_timeCallback)(unsigned long); // Callback for requested time messages
|
void (*_timeCallback)(unsigned long); // Callback for requested time messages
|
||||||
|
|
||||||
|
|
||||||
void _process() {
|
void _process() {
|
||||||
hwWatchdogReset();
|
hwWatchdogReset();
|
||||||
|
|
||||||
|
|
||||||
#if defined (MY_LEDS_BLINKING_FEATURE)
|
#if defined (MY_LEDS_BLINKING_FEATURE)
|
||||||
ledsProcess();
|
ledsProcess();
|
||||||
#endif
|
#endif
|
||||||
@@ -51,23 +50,17 @@ void _process() {
|
|||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportProcess();
|
transportProcess();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(MY_RADIO_FEATURE)
|
|
||||||
static inline bool isValidParent( const uint8_t parent ) {
|
|
||||||
return parent != AUTO;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void _infiniteLoop() {
|
void _infiniteLoop() {
|
||||||
while(1) {
|
while(1) {
|
||||||
#if defined(MY_GATEWAY_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
yield();
|
yield();
|
||||||
#endif
|
#endif
|
||||||
#if defined (MY_LEDS_BLINKING_FEATURE)
|
#if defined (MY_LEDS_BLINKING_FEATURE)
|
||||||
ledsProcess();
|
ledsProcess();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,20 +81,6 @@ void _begin() {
|
|||||||
|
|
||||||
signerInit();
|
signerInit();
|
||||||
|
|
||||||
#if defined(MY_RADIO_FEATURE)
|
|
||||||
_failedTransmissions = 0;
|
|
||||||
|
|
||||||
// Setup radio
|
|
||||||
if (!transportInit()) {
|
|
||||||
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
|
||||||
debug(PSTR("Radio init failed. Check wiring.\n"));
|
|
||||||
// Nothing more we can do
|
|
||||||
_infiniteLoop();
|
|
||||||
} else {
|
|
||||||
debug(PSTR("Radio init successful.\n"));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MY_GATEWAY_FEATURE)
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
#if defined(MY_INCLUSION_BUTTON_FEATURE)
|
#if defined(MY_INCLUSION_BUTTON_FEATURE)
|
||||||
inclusionInit();
|
inclusionInit();
|
||||||
@@ -109,101 +88,134 @@ void _begin() {
|
|||||||
|
|
||||||
// initialize the transport driver
|
// initialize the transport driver
|
||||||
if (!gatewayTransportInit()) {
|
if (!gatewayTransportInit()) {
|
||||||
setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
|
setIndication(INDICATION_ERR_INIT_GWTRANSPORT);
|
||||||
debug(PSTR("Transport driver init fail\n"));
|
debug(PSTR("Transport driver init fail\n"));
|
||||||
// Nothing more we can do
|
// Nothing more we can do
|
||||||
_infiniteLoop();
|
_infiniteLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Read latest received controller configuration from EEPROM
|
// Read latest received controller configuration from EEPROM
|
||||||
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
|
hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig));
|
||||||
if (_cc.isMetric == 0xff) {
|
// isMetric is bool, hence empty EEPROM (=0xFF) evaluates to true
|
||||||
// Eeprom empty, set default to metric
|
|
||||||
_cc.isMetric = 0x01;
|
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||||
}
|
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
|
||||||
|
readFirmwareSettings();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(MY_GATEWAY_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
// Set configuration for gateway
|
// Save static parent id in eeprom (used by bootloader)
|
||||||
_nc.parentNodeId = GATEWAY_ADDRESS;
|
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
|
||||||
_nc.distance = 0;
|
transportInitialize();
|
||||||
_nc.nodeId = GATEWAY_ADDRESS;
|
while (!isTransportOK()) {
|
||||||
#elif defined(MY_RADIO_FEATURE)
|
hwWatchdogReset();
|
||||||
// Read settings from eeprom
|
transportProcess();
|
||||||
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
yield();
|
||||||
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
|
#endif
|
||||||
readFirmwareSettings();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
_autoFindParent = MY_PARENT_NODE_ID == AUTO;
|
|
||||||
if (!_autoFindParent) {
|
|
||||||
_nc.parentNodeId = MY_PARENT_NODE_ID;
|
|
||||||
// Save static parent id in eeprom (used by bootloader)
|
|
||||||
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID);
|
|
||||||
// We don't actually know the distance to gw here. Let's pretend it is 1.
|
|
||||||
// If the current node is also repeater, be aware of this.
|
|
||||||
_nc.distance = 1;
|
|
||||||
setIndication(INDICATION_GOT_PARENT);
|
|
||||||
} else if (!isValidParent(_nc.parentNodeId)) {
|
|
||||||
// Auto find parent, but parent in eeprom is invalid. Try find one.
|
|
||||||
transportFindParentNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MY_NODE_ID != AUTO) {
|
|
||||||
// Set static id
|
|
||||||
_nc.nodeId = MY_NODE_ID;
|
|
||||||
// Save static id in eeprom
|
|
||||||
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
|
|
||||||
setIndication(INDICATION_GOT_NODEID);
|
|
||||||
} else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) {
|
|
||||||
// Try to fetch node-id from gateway
|
|
||||||
transportRequestNodeId();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MY_NODE_LOCK_FEATURE
|
|
||||||
// Check if node has been locked down
|
|
||||||
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
|
#ifdef MY_NODE_LOCK_FEATURE
|
||||||
// Node is locked, check if unlock pin is asserted, else hang the node
|
// Check if node has been locked down
|
||||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
|
if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) {
|
||||||
// Make a short delay so we are sure any large external nets are fully pulled
|
// Node is locked, check if unlock pin is asserted, else hang the node
|
||||||
unsigned long enter = hwMillis();
|
pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP);
|
||||||
while (hwMillis() - enter < 2);
|
// Make a short delay so we are sure any large external nets are fully pulled
|
||||||
if (digitalRead(MY_NODE_UNLOCK_PIN) == 0) {
|
unsigned long enter = hwMillis();
|
||||||
// Pin is grounded, reset lock counter
|
while (hwMillis() - enter < 2);
|
||||||
|
if (digitalRead(MY_NODE_UNLOCK_PIN) == 0) {
|
||||||
|
// Pin is grounded, reset lock counter
|
||||||
|
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
|
||||||
|
// Disable pullup
|
||||||
|
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
|
||||||
|
setIndication(INDICATION_ERR_LOCKED);
|
||||||
|
debug(PSTR("Node is unlocked.\n"));
|
||||||
|
} else {
|
||||||
|
// Disable pullup
|
||||||
|
pinMode(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);
|
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
|
||||||
// Disable pullup
|
|
||||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
|
|
||||||
debug(PSTR("Node is unlocked.\n"));
|
|
||||||
} else {
|
|
||||||
// Disable pullup
|
|
||||||
pinMode(MY_NODE_UNLOCK_PIN, INPUT);
|
|
||||||
setIndication(INDICATION_ERR_LOCKED);
|
|
||||||
nodeLock("LDB"); //Locked during boot
|
|
||||||
}
|
}
|
||||||
} else if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0xFF) {
|
#endif
|
||||||
// Reset walue
|
|
||||||
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, MY_NODE_LOCK_COUNTER_MAX);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Call sketch setup
|
// Call sketch setup
|
||||||
if (setup)
|
if (setup)
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
|
|
||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPresentNode();
|
presentNode();
|
||||||
#endif
|
#endif
|
||||||
if (presentation)
|
|
||||||
presentation();
|
// register node
|
||||||
|
_registerNode();
|
||||||
|
|
||||||
debug(PSTR("Init complete, id=%d, parent=%d, distance=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance);
|
debug(PSTR("Init complete, id=%d, parent=%d, distance=%d, registration=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance, _nodeRegistered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _registerNode() {
|
||||||
|
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||||
|
debug(PSTR("Request registration...\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 {
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_REQUEST, false).set(MY_CORE_VERSION));
|
||||||
|
} while (!wait(2000, C_INTERNAL, I_REGISTRATION_RESPONSE) && counter--);
|
||||||
|
|
||||||
|
#else
|
||||||
|
_nodeRegistered = true;
|
||||||
|
debug(PSTR("No registration required\n"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void presentNode() {
|
||||||
|
setIndication(INDICATION_PRESENT);
|
||||||
|
// Present node and request config
|
||||||
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
|
// Send presentation for this gateway device
|
||||||
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
|
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
|
||||||
|
#else
|
||||||
|
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||||
|
presentBootloaderInformation();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Send signing preferences for this node to the GW
|
||||||
|
signerPresentation(_msgTmp, GATEWAY_ADDRESS);
|
||||||
|
|
||||||
|
// Send presentation for this radio node
|
||||||
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
|
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
|
||||||
|
#else
|
||||||
|
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Send a configuration exchange request to controller
|
||||||
|
// Node sends parent node. Controller answers with latest node configuration
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
|
||||||
|
|
||||||
|
// Wait configuration reply.
|
||||||
|
wait(2000, C_INTERNAL, I_CONFIG);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (presentation)
|
||||||
|
presentation();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t getNodeId() {
|
uint8_t getNodeId() {
|
||||||
@@ -220,8 +232,6 @@ ControllerConfig getConfig() {
|
|||||||
|
|
||||||
|
|
||||||
boolean _sendRoute(MyMessage &message) {
|
boolean _sendRoute(MyMessage &message) {
|
||||||
// increment heartbeat counter
|
|
||||||
_heartbeat++;
|
|
||||||
#if defined(MY_CORE_ONLY)
|
#if defined(MY_CORE_ONLY)
|
||||||
(void)message;
|
(void)message;
|
||||||
#endif
|
#endif
|
||||||
@@ -241,132 +251,154 @@ boolean _sendRoute(MyMessage &message) {
|
|||||||
|
|
||||||
bool send(MyMessage &message, bool enableAck) {
|
bool send(MyMessage &message, bool enableAck) {
|
||||||
message.sender = _nc.nodeId;
|
message.sender = _nc.nodeId;
|
||||||
mSetCommand(message,C_SET);
|
mSetCommand(message, C_SET);
|
||||||
mSetRequestAck(message,enableAck);
|
mSetRequestAck(message, enableAck);
|
||||||
return _sendRoute(message);
|
|
||||||
}
|
#if defined(MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||||
|
if (_nodeRegistered) {
|
||||||
|
return _sendRoute(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug(PSTR("NODE:!REG\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return _sendRoute(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void sendBatteryLevel(uint8_t value, bool enableAck) {
|
void sendBatteryLevel(uint8_t value, bool enableAck) {
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_BATTERY_LEVEL, enableAck).set(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendHeartbeat(void) {
|
void sendHeartbeat(void) {
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, false).set(_heartbeat));
|
#if !defined(MY_CORE_ONLY)
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_HEARTBEAT_RESPONSE, false).set(transportGetHeartbeat()));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
|
void present(uint8_t childSensorId, uint8_t sensorType, const char *description, bool enableAck) {
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description));
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, childSensorId, C_PRESENTATION, sensorType, enableAck).set(childSensorId==NODE_SENSOR_ID?LIBRARY_VERSION:description));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendSketchInfo(const char *name, const char *version, bool enableAck) {
|
void sendSketchInfo(const char *name, const char *version, bool enableAck) {
|
||||||
if (name != NULL) {
|
if (name) _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_NAME, enableAck).set(name));
|
if (version) _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
|
||||||
}
|
|
||||||
if (version != NULL) {
|
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_SKETCH_VERSION, enableAck).set(version));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
|
void request(uint8_t childSensorId, uint8_t variableType, uint8_t destination) {
|
||||||
_sendRoute(build(_msg, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
|
_sendRoute(build(_msgTmp, _nc.nodeId, destination, childSensorId, C_REQ, variableType, false).set(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestTime() {
|
void requestTime() {
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_TIME, false).set(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message delivered through _msg
|
// Message delivered through _msg
|
||||||
void _processInternalMessages() {
|
bool _processInternalMessages() {
|
||||||
bool isMetric;
|
|
||||||
uint8_t type = _msg.type;
|
uint8_t type = _msg.type;
|
||||||
|
|
||||||
#if !defined(MY_DISABLE_REMOTE_RESET)
|
if (_msg.sender == GATEWAY_ADDRESS) {
|
||||||
if (type == I_REBOOT) {
|
if (type == I_REBOOT) {
|
||||||
// Requires MySensors or other bootloader with watchdogs enabled
|
#if !defined(MY_DISABLE_REMOTE_RESET)
|
||||||
setIndication(INDICATION_REBOOT);
|
// Requires MySensors or other bootloader with watchdogs enabled
|
||||||
hwReboot();
|
setIndication(INDICATION_REBOOT);
|
||||||
} else
|
hwReboot();
|
||||||
#endif
|
#endif
|
||||||
if (type == I_CONFIG) {
|
|
||||||
// Pick up configuration from controller (currently only metric/imperial)
|
|
||||||
// and store it in eeprom if changed
|
|
||||||
if (_msg.getString() == NULL) {
|
|
||||||
isMetric = true;
|
|
||||||
} else {
|
|
||||||
isMetric = _msg.getString()[0] == 'M';
|
|
||||||
}
|
}
|
||||||
_cc.isMetric = isMetric;
|
else if (type == I_REGISTRATION_RESPONSE) {
|
||||||
hwWriteConfig(EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric);
|
#if defined (MY_REGISTRATION_FEATURE) && !defined(MY_GATEWAY_FEATURE)
|
||||||
} else if (type == I_PRESENTATION) {
|
_nodeRegistered = _msg.getBool();
|
||||||
if (!mGetAck(_msg)) {
|
setIndication(INDICATION_GOT_REGISTRATION);
|
||||||
|
debug(PSTR("Node registration=%d\n"), _nodeRegistered);
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
else if (type == I_PRESENTATION) {
|
||||||
// Re-send node presentation to controller
|
// Re-send node presentation to controller
|
||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPresentNode();
|
presentNode();
|
||||||
#endif
|
#endif
|
||||||
if (presentation)
|
|
||||||
presentation();
|
|
||||||
}
|
}
|
||||||
} else if (type == I_HEARTBEAT) {
|
else if (type == I_HEARTBEAT) {
|
||||||
sendHeartbeat();
|
sendHeartbeat();
|
||||||
} else if (type == I_TIME) {
|
}
|
||||||
// Deliver time to callback
|
else if (type == I_TIME) {
|
||||||
if (receiveTime)
|
// Deliver time to callback
|
||||||
receiveTime(_msg.getULong());
|
if (receiveTime)
|
||||||
} else if (type == I_CHILDREN) {
|
receiveTime(_msg.getULong());
|
||||||
|
}
|
||||||
|
else if (type == I_CHILDREN) {
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
if (_msg.data[0] == 'C') {
|
if (_msg.data[0] == 'C') {
|
||||||
// Clears child relay data for this node
|
// Clears child relay data for this node
|
||||||
setIndication(INDICATION_CLEAR_ROUTING);
|
setIndication(INDICATION_CLEAR_ROUTING);
|
||||||
debug(PSTR("clear routing table\n"));
|
transportClearRoutingTable();
|
||||||
uint8_t i = 255;
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN, false).set("ok"));
|
||||||
do {
|
|
||||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+i, BROADCAST_ADDRESS);
|
|
||||||
} while (i--);
|
|
||||||
// Clear parent node id & distance to gw
|
|
||||||
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, AUTO);
|
|
||||||
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, DISTANCE_INVALID);
|
|
||||||
// Find parent node
|
|
||||||
transportFindParentNode();
|
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("ok"));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if (type == I_DEBUG) {
|
}
|
||||||
#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
|
else if (type == I_DEBUG) {
|
||||||
char debug_msg = _msg.data[0];
|
#if defined(MY_DEBUG) || defined(MY_SPECIAL_DEBUG)
|
||||||
if(debug_msg == 'R'){
|
char debug_msg = _msg.data[0];
|
||||||
|
if (debug_msg == 'R') { // routing table
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
// routing table
|
for (uint8_t cnt = 0; cnt != 255; cnt++) {
|
||||||
for(uint8_t cnt=0; cnt!=255;cnt++){
|
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS + cnt);
|
||||||
uint8_t route = hwReadConfig(EEPROM_ROUTES_ADDRESS+cnt);
|
if (route != BROADCAST_ADDRESS) {
|
||||||
if (route!=BROADCAST_ADDRESS){
|
debug(PSTR("ID: %d via %d\n"), cnt, route);
|
||||||
debug(PSTR("ID: %d via %d\n"),cnt,route);
|
uint8_t OutBuf[2] = { cnt,route };
|
||||||
uint8_t OutBuf[2] = {cnt,route};
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(OutBuf, 2));
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG,false).set(OutBuf,2));
|
wait(200);
|
||||||
wait(100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} else if(debug_msg == 'V'){
|
|
||||||
// CPU voltage
|
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG,false).set(hwCPUVoltage()));
|
|
||||||
} else if (debug_msg == 'F') {
|
|
||||||
// CPU frequency in 1/10Mhz
|
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG,false).set(hwCPUFrequency()));
|
|
||||||
} else if (debug_msg == 'M') {
|
|
||||||
// free memory
|
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG,false).set(hwFreeMem()));
|
|
||||||
} else if (debug_msg == 'E') {
|
|
||||||
// clear MySensors eeprom area and reboot
|
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("ok"));
|
|
||||||
for (int i=EEPROM_START;i<EEPROM_LOCAL_CONFIG_ADDRESS;i++) {
|
|
||||||
hwWriteConfig(i,0xFF);
|
|
||||||
}
|
}
|
||||||
setIndication(INDICATION_REBOOT);
|
else if (debug_msg == 'V') { // CPU voltage
|
||||||
hwReboot();
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUVoltage()));
|
||||||
}
|
}
|
||||||
#endif
|
else if (debug_msg == 'F') { // CPU frequency in 1/10Mhz
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwCPUFrequency()));
|
||||||
|
}
|
||||||
|
else if (debug_msg == 'M') { // free memory
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set(hwFreeMem()));
|
||||||
|
}
|
||||||
|
else if (debug_msg == 'E') { // clear MySensors eeprom area and reboot
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DEBUG, false).set("ok"));
|
||||||
|
for (int i = EEPROM_START; i<EEPROM_LOCAL_CONFIG_ADDRESS; i++) hwWriteConfig(i, 0xFF);
|
||||||
|
setIndication(INDICATION_REBOOT);
|
||||||
|
hwReboot();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// sender is a node
|
||||||
|
if (type == I_REGISTRATION_REQUEST) {
|
||||||
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
|
// register request are exclusively handled by GW/Controller
|
||||||
|
// !!! eventually define if AUTO ACK or register request forwarded to controller
|
||||||
|
#if !defined(MY_REGISTRATION_CONTROLLER)
|
||||||
|
// auto register if version compatible
|
||||||
|
bool approveRegistration = true;
|
||||||
|
|
||||||
|
#if defined(MY_CORE_COMPATIBILITY_CHECK)
|
||||||
|
approveRegistration = (_msg.getByte() >= MY_CORE_MIN_VERSION);
|
||||||
|
#endif
|
||||||
|
_sendRoute(build(_msgTmp, _nc.nodeId, _msg.sender, NODE_SENSOR_ID, C_INTERNAL, I_REGISTRATION_RESPONSE, false).set(approveRegistration));
|
||||||
|
#else
|
||||||
|
return false; // processing of this request via controller
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -382,7 +414,7 @@ void wait(unsigned long ms) {
|
|||||||
unsigned long enter = hwMillis();
|
unsigned long enter = hwMillis();
|
||||||
while (hwMillis() - enter < ms) {
|
while (hwMillis() - enter < ms) {
|
||||||
_process();
|
_process();
|
||||||
#if defined(MY_GATEWAY_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
yield();
|
yield();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -395,7 +427,7 @@ bool wait(unsigned long ms, uint8_t cmd, uint8_t msgtype) {
|
|||||||
bool expectedResponse = false;
|
bool expectedResponse = false;
|
||||||
while ( (hwMillis() - enter < ms) && !expectedResponse ) {
|
while ( (hwMillis() - enter < ms) && !expectedResponse ) {
|
||||||
_process();
|
_process();
|
||||||
#if defined(MY_GATEWAY_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
yield();
|
yield();
|
||||||
#endif
|
#endif
|
||||||
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
|
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
|
||||||
@@ -420,10 +452,10 @@ int8_t sleep(unsigned long ms) {
|
|||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPowerDown();
|
transportPowerDown();
|
||||||
#endif
|
#endif
|
||||||
setIndication(INDICATION_SLEEP);
|
setIndication(INDICATION_SLEEP);
|
||||||
const int8_t res = hwSleep(ms);
|
const int8_t res = hwSleep(ms);
|
||||||
setIndication(INDICATION_WAKEUP);
|
setIndication(INDICATION_WAKEUP);
|
||||||
return res;
|
return res;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,10 +485,10 @@ int8_t sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
|
|||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPowerDown();
|
transportPowerDown();
|
||||||
#endif
|
#endif
|
||||||
setIndication(INDICATION_SLEEP);
|
setIndication(INDICATION_SLEEP);
|
||||||
const int8_t res = hwSleep(interrupt, mode, ms);
|
const int8_t res = hwSleep(interrupt, mode, ms);
|
||||||
setIndication(INDICATION_WAKEUP);
|
setIndication(INDICATION_WAKEUP);
|
||||||
return res;
|
return res;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -488,10 +520,10 @@ int8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode
|
|||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPowerDown();
|
transportPowerDown();
|
||||||
#endif
|
#endif
|
||||||
setIndication(INDICATION_SLEEP);
|
setIndication(INDICATION_SLEEP);
|
||||||
const int8_t res = hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
|
const int8_t res = hwSleep(interrupt1, mode1, interrupt2, mode2, ms);
|
||||||
setIndication(INDICATION_WAKEUP);
|
setIndication(INDICATION_WAKEUP);
|
||||||
return res;
|
return res;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,19 +541,18 @@ void nodeLock(const char* str) {
|
|||||||
// Make sure EEPROM is updated to locked status
|
// Make sure EEPROM is updated to locked status
|
||||||
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
|
hwWriteConfig(EEPROM_NODE_LOCK_COUNTER, 0);
|
||||||
while (1) {
|
while (1) {
|
||||||
setIndication(INDICATION_ERR_LOCKED);
|
setIndication(INDICATION_ERR_LOCKED);
|
||||||
debug(PSTR("Node is locked. Ground pin %d and reset to unlock.\n"), MY_NODE_UNLOCK_PIN);
|
debug(PSTR("Node is locked. Ground pin %d and reset to unlock.\n"), MY_NODE_UNLOCK_PIN);
|
||||||
#if defined(MY_GATEWAY_ESP8266)
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
yield();
|
yield();
|
||||||
#endif
|
#endif
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID,
|
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID,C_INTERNAL, I_LOCKED, false).set(str));
|
||||||
C_INTERNAL, I_LOCKED, false).set(str));
|
|
||||||
#if defined(MY_RADIO_FEATURE)
|
#if defined(MY_RADIO_FEATURE)
|
||||||
transportPowerDown();
|
transportPowerDown();
|
||||||
#endif
|
#endif
|
||||||
setIndication(INDICATION_SLEEP);
|
setIndication(INDICATION_SLEEP);
|
||||||
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
|
(void)hwSleep((unsigned long)1000*60*30); // Sleep for 30 min before resending LOCKED message
|
||||||
setIndication(INDICATION_WAKEUP);
|
setIndication(INDICATION_WAKEUP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,15 +29,15 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#ifdef MY_DEBUG
|
#ifdef MY_DEBUG
|
||||||
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
#define debug(x,...) hwDebugPrint(x, ##__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define debug(x,...)
|
#define debug(x,...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This is the nodeId for sensor net gateway receiver sketch (where all sensors should send their data).
|
#define GATEWAY_ADDRESS ((uint8_t)0) //!< Node ID for GW sketch
|
||||||
#define GATEWAY_ADDRESS ((uint8_t)0)
|
#define NODE_SENSOR_ID 0xFF //!< Node child is always created/presented when a node is started
|
||||||
// Node child is always created/presented when a node is started
|
#define MY_CORE_VERSION ((uint8_t)2) //!< core version
|
||||||
#define NODE_SENSOR_ID 0xFF
|
#define MY_CORE_MIN_VERSION ((uint8_t)2) //!< min core version required for compatibility
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +72,11 @@ uint8_t getNodeId();
|
|||||||
*/
|
*/
|
||||||
uint8_t getParentNodeId();
|
uint8_t getParentNodeId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends node information to the gateway.
|
||||||
|
*/
|
||||||
|
void presentNode();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Each node must present all attached sensors before any values can be handled correctly by the controller.
|
* Each node must present all attached sensors before any values can be handled correctly by the controller.
|
||||||
* It is usually good to present all attached sensors after power-up in setup().
|
* It is usually good to present all attached sensors after power-up in setup().
|
||||||
@@ -236,10 +241,12 @@ void _begin();
|
|||||||
|
|
||||||
void _process(void);
|
void _process(void);
|
||||||
|
|
||||||
void _processInternalMessages();
|
bool _processInternalMessages();
|
||||||
|
|
||||||
void _infiniteLoop();
|
void _infiniteLoop();
|
||||||
|
|
||||||
|
void _registerNode();
|
||||||
|
|
||||||
boolean _sendRoute(MyMessage &message);
|
boolean _sendRoute(MyMessage &message);
|
||||||
|
|
||||||
extern NodeConfig _nc;
|
extern NodeConfig _nc;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -20,65 +20,370 @@
|
|||||||
|
|
||||||
#include "MyTransport.h"
|
#include "MyTransport.h"
|
||||||
|
|
||||||
bool _autoFindParent;
|
// SM: transitions and update states
|
||||||
uint8_t _failedTransmissions;
|
static State stInit = { stInitTransition, NULL };
|
||||||
|
static State stParent = { stParentTransition, stParentUpdate };
|
||||||
|
static State stID = { stIDTransition, stIDUpdate };
|
||||||
|
static State stUplink = { stUplinkTransition, NULL };
|
||||||
|
static State stOK = { stOKTransition, stOKUpdate };
|
||||||
|
static State stFailure = { stFailureTransition, stFailureUpdate };
|
||||||
|
|
||||||
static inline bool isValidDistance( const uint8_t distance ) {
|
static transportSM _transportSM;
|
||||||
return distance != DISTANCE_INVALID;
|
|
||||||
|
// stInit: initialize transport HW
|
||||||
|
void stInitTransition() {
|
||||||
|
debug(PSTR("TSM:INIT\n"));
|
||||||
|
// initialize status variables
|
||||||
|
_transportSM.failedUplinkTransmissions = 0;
|
||||||
|
_transportSM.pingActive = false;
|
||||||
|
|
||||||
|
// Read node settings (ID, parentId, GW distance) from EEPROM
|
||||||
|
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
|
||||||
|
|
||||||
|
// initialize radio
|
||||||
|
if (!transportInit()) {
|
||||||
|
debug(PSTR("!RADIO:fail\n"));
|
||||||
|
setIndication(INDICATION_ERR_INIT_TRANSPORT);
|
||||||
|
transportSwitchSM(stFailure);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug(PSTR("RADIO:OK\n"));
|
||||||
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
|
// Set configuration for gateway
|
||||||
|
debug(PSTR("TSM:GW MODE\n"));
|
||||||
|
_nc.parentNodeId = GATEWAY_ADDRESS;
|
||||||
|
_nc.distance = 0;
|
||||||
|
_nc.nodeId = GATEWAY_ADDRESS;
|
||||||
|
transportSetAddress(GATEWAY_ADDRESS);
|
||||||
|
transportSwitchSM(stOK);
|
||||||
|
#else
|
||||||
|
if (MY_NODE_ID != AUTO) {
|
||||||
|
// Set static id
|
||||||
|
_nc.nodeId = MY_NODE_ID;
|
||||||
|
// Save static id in eeprom
|
||||||
|
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID);
|
||||||
|
}
|
||||||
|
// set ID if static or set in EEPROM
|
||||||
|
if(_nc.nodeId!=AUTO) transportAssignNodeID(_nc.nodeId);
|
||||||
|
transportSwitchSM(stParent);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stParent: find parent
|
||||||
|
void stParentTransition() {
|
||||||
|
debug(PSTR("TSM:PARENT\n"));
|
||||||
|
_transportSM.preferredParentFound = false;
|
||||||
|
_transportSM.findingParentNode = true;
|
||||||
|
_transportSM.failedUplinkTransmissions = 0;
|
||||||
|
_transportSM.uplinkOk = false;
|
||||||
|
// Set distance to max and invalidate parent node id
|
||||||
|
_nc.distance = DISTANCE_INVALID;
|
||||||
|
_nc.parentNodeId = AUTO;
|
||||||
|
// Broadcast find parent request
|
||||||
|
setIndication(INDICATION_FIND_PARENT);
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// stParentUpdate
|
||||||
|
void stParentUpdate() {
|
||||||
|
if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) {
|
||||||
|
// timeout or preferred parent found
|
||||||
|
if (_nc.parentNodeId != AUTO) {
|
||||||
|
_transportSM.findingParentNode = false;
|
||||||
|
setIndication(INDICATION_GOT_PARENT);
|
||||||
|
transportSwitchSM(stID);
|
||||||
|
}
|
||||||
|
else if (transportTimeInState() > STATE_TIMEOUT) {
|
||||||
|
if (_transportSM.retries < STATE_RETRIES) {
|
||||||
|
// reenter if timeout and retries left
|
||||||
|
transportSwitchSM(stParent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setIndication(INDICATION_ERR_FIND_PARENT);
|
||||||
|
transportSwitchSM(stFailure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stID: verify and request ID if necessary
|
||||||
|
void stIDTransition() {
|
||||||
|
debug(PSTR("TSM:ID\n"));
|
||||||
|
if (_nc.nodeId == AUTO) {
|
||||||
|
// send ID request
|
||||||
|
setIndication(INDICATION_REQ_NODEID);
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set(""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stIDUpdate() {
|
||||||
|
if (_nc.nodeId != AUTO) {
|
||||||
|
// current node ID is valid, proceed to uplink check
|
||||||
|
debug(PSTR("node ID=%d\n"), _nc.nodeId);
|
||||||
|
setIndication(INDICATION_GOT_NODEID);
|
||||||
|
// check uplink
|
||||||
|
transportSwitchSM(stUplink);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (transportTimeInState() > STATE_TIMEOUT) {
|
||||||
|
if (_transportSM.retries < STATE_RETRIES) {
|
||||||
|
// re-enter if retries left
|
||||||
|
transportSwitchSM(stID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// fail
|
||||||
|
setIndication(INDICATION_ERR_GET_NODEID);
|
||||||
|
transportSwitchSM(stFailure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stUplinkTransition() {
|
||||||
|
if (transportPingNode(GATEWAY_ADDRESS) == _nc.distance) {
|
||||||
|
transportSwitchSM(stOK);
|
||||||
|
}
|
||||||
|
else transportSwitchSM(stParent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stOKTransition() {
|
||||||
|
debug(PSTR("TSM:OK\n"));
|
||||||
|
_transportSM.uplinkOk = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stOK update: monitors uplink failures
|
||||||
|
void stOKUpdate() {
|
||||||
|
#if !defined(MY_GATEWAY_FEATURE)
|
||||||
|
if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) {
|
||||||
|
// too many uplink transmissions failed, find new parent
|
||||||
|
#if !defined(MY_PARENT_NODE_IS_STATIC)
|
||||||
|
debug(PSTR("!TSM:UPL fail, SNP\n"));
|
||||||
|
transportSwitchSM(stParent);
|
||||||
|
#else
|
||||||
|
debug(PSTR("!TSM:UPL fail, STATP\n"));
|
||||||
|
_transportSM.failedUplinkTransmissions = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// stFailure: ented upon HW init failure or max retries exceeded
|
||||||
|
void stFailureTransition() {
|
||||||
|
debug(PSTR("!TSM:FAILURE\n"));
|
||||||
|
_transportSM.uplinkOk = false;
|
||||||
|
// power down transport, no need until re-init
|
||||||
|
debug(PSTR("TSM:PDT\n"));
|
||||||
|
transportPowerDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stFailureUpdate() {
|
||||||
|
if (transportTimeInState()> TIMEOUT_FAILURE_STATE) {
|
||||||
|
transportSwitchSM(stInit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportSwitchSM(State& newState) {
|
||||||
|
if (_transportSM.currentState != &newState) {
|
||||||
|
// state change, reset retry counter
|
||||||
|
_transportSM.retries = 0;
|
||||||
|
// change state
|
||||||
|
_transportSM.currentState = &newState;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_transportSM.retries++; // increment retries
|
||||||
|
}
|
||||||
|
// Transition event
|
||||||
|
if (_transportSM.currentState->Transition) _transportSM.currentState->Transition();
|
||||||
|
// save time
|
||||||
|
_transportSM.stateEnter = hwMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t transportTimeInState() {
|
||||||
|
return hwMillis() - _transportSM.stateEnter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportUpdateSM(){
|
||||||
|
if (_transportSM.currentState->Update) _transportSM.currentState->Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isTransportOK() {
|
||||||
|
return (_transportSM.uplinkOk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void transportProcess() {
|
void transportInitialize() {
|
||||||
uint8_t to = 0;
|
// intial state
|
||||||
if (!transportAvailable(&to))
|
_transportSM.currentState = &stFailure;
|
||||||
{
|
transportSwitchSM(stInit);
|
||||||
#ifdef MY_OTA_FIRMWARE_FEATURE
|
}
|
||||||
firmwareOTAUpdateRequest();
|
|
||||||
#endif
|
// update TSM and process incoming messages
|
||||||
return;
|
void transportProcess() {
|
||||||
|
// update state machine
|
||||||
|
transportUpdateSM();
|
||||||
|
// process transport FIFO
|
||||||
|
transportProcessFIFO();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void transportAssignNodeID(uint8_t newNodeId) {
|
||||||
|
// verify if ID valid
|
||||||
|
if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
|
||||||
|
_nc.nodeId = newNodeId;
|
||||||
|
transportSetAddress(newNodeId);
|
||||||
|
// Write ID to EEPROM
|
||||||
|
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
|
||||||
|
debug(PSTR("assign node ID=%d\n"),newNodeId);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug(PSTR("node ID invalid (%d)\n"),newNodeId);
|
||||||
|
setIndication(INDICATION_ERR_NET_FULL);
|
||||||
|
// Nothing else we can do...
|
||||||
|
transportSwitchSM(stFailure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transportRouteMessage(MyMessage &message) {
|
||||||
|
uint8_t destination = message.destination;
|
||||||
|
uint8_t route;
|
||||||
|
|
||||||
|
if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
|
||||||
|
debug(PSTR("finding parent active, msg not send\n"));
|
||||||
|
// request to send a non-BC message while finding parent active, abort
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (destination == GATEWAY_ADDRESS) {
|
||||||
|
route = _nc.parentNodeId; // message to GW always routes via parent
|
||||||
|
}
|
||||||
|
else if (destination == BROADCAST_ADDRESS) {
|
||||||
|
route = BROADCAST_ADDRESS; // message to BC does not require routing
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
|
// destination not GW & not BC, get route
|
||||||
|
route = hwReadConfig(EEPROM_ROUTES_ADDRESS + destination);
|
||||||
|
if (route == AUTO) {
|
||||||
|
// route unknown
|
||||||
|
if (message.last != _nc.parentNodeId) {
|
||||||
|
// message not from parent, i.e. child node - route it to parent
|
||||||
|
debug(PSTR("Destination %d unknown, send to parent %d\n"), destination, _nc.parentNodeId);
|
||||||
|
route = _nc.parentNodeId;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// route unknown and msg received from parent, send it to destination assuming in rx radius
|
||||||
|
route = destination;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
route = _nc.parentNodeId; // not a repeater, all traffic routed via parent
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// send message
|
||||||
|
bool ok = transportSendWrite(route, message);
|
||||||
|
#if !defined(MY_GATEWAY_FEATURE)
|
||||||
|
// update counter
|
||||||
|
if (route == _nc.parentNodeId) {
|
||||||
|
if (!ok) {
|
||||||
|
setIndication(INDICATION_ERR_TX);
|
||||||
|
_transportSM.failedUplinkTransmissions++;
|
||||||
|
}
|
||||||
|
else _transportSM.failedUplinkTransmissions = 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(!ok) setIndication(INDICATION_ERR_TX);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transportSendRoute(MyMessage &message) {
|
||||||
|
if (isTransportOK()) {
|
||||||
|
return transportRouteMessage(message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TNR: transport not ready
|
||||||
|
debug(PSTR("!TNR\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only be used inside transport
|
||||||
|
bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){
|
||||||
|
uint32_t enter = hwMillis();
|
||||||
|
// invalidate msg type
|
||||||
|
_msg.type = !msgtype;
|
||||||
|
bool expectedResponse = false;
|
||||||
|
while ((hwMillis() - enter < ms) && !expectedResponse) {
|
||||||
|
transportProcess();
|
||||||
|
#if defined(ARDUINO_ARCH_ESP8266)
|
||||||
|
yield();
|
||||||
|
#endif
|
||||||
|
expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgtype);
|
||||||
|
}
|
||||||
|
return expectedResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t transportPingNode(uint8_t targetId) {
|
||||||
|
if(!_transportSM.pingActive){
|
||||||
|
if(targetId == _nc.nodeId) {
|
||||||
|
// ping to ourself
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_transportSM.pingActive = true;
|
||||||
|
_transportSM.pingResponse = INVALID_HOPS;
|
||||||
|
debug(PSTR("pinging node %d\n"), targetId);
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01));
|
||||||
|
// Wait for ping reply or timeout
|
||||||
|
transportWait(2000, C_INTERNAL, I_PONG);
|
||||||
|
return _transportSM.pingResponse;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return INVALID_HOPS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportClearRoutingTable() {
|
||||||
|
debug(PSTR("CRT\n")); // clear routing table
|
||||||
|
for(uint8_t i = 0; i!=255; i++)
|
||||||
|
hwWriteConfig(EEPROM_ROUTES_ADDRESS+i, BROADCAST_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t transportGetHeartbeat() {
|
||||||
|
return transportTimeInState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void transportProcessMessage() {
|
||||||
(void)signerCheckTimer(); // Manage signing timeout
|
(void)signerCheckTimer(); // Manage signing timeout
|
||||||
|
|
||||||
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
|
uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
|
||||||
(void)payloadLength; //until somebody makes use of it
|
(void)payloadLength; // currently not used, but good to test for CRC-ok but corrupt msgs
|
||||||
setIndication(INDICATION_RX);
|
setIndication(INDICATION_RX);
|
||||||
|
|
||||||
|
// verify protocol version
|
||||||
|
if(mGetVersion(_msg) != PROTOCOL_VERSION) {
|
||||||
|
setIndication(INDICATION_ERR_VERSION);
|
||||||
|
debug(PSTR("!PVER mismatch\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reject massages that do not pass verification
|
||||||
|
if (!signerVerifyMsg(_msg)) {
|
||||||
|
setIndication(INDICATION_ERR_SIGN);
|
||||||
|
debug(PSTR("!SIGN verify fail\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t command = mGetCommand(_msg);
|
uint8_t command = mGetCommand(_msg);
|
||||||
uint8_t type = _msg.type;
|
uint8_t type = _msg.type;
|
||||||
uint8_t sender = _msg.sender;
|
uint8_t sender = _msg.sender;
|
||||||
uint8_t last = _msg.last;
|
uint8_t last = _msg.last;
|
||||||
uint8_t destination = _msg.destination;
|
uint8_t destination = _msg.destination;
|
||||||
|
|
||||||
|
|
||||||
// Reject massages that do not pass verification
|
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
||||||
if (!signerVerifyMsg(_msg)) {
|
sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
|
||||||
setIndication(INDICATION_ERR_SIGN);
|
|
||||||
debug(PSTR("verify fail\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destination == _nc.nodeId) {
|
|
||||||
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
|
||||||
sender, _msg.last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
|
||||||
debug(PSTR("read and forward: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d\n"),
|
|
||||||
sender, _msg.last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
|
|
||||||
#else
|
|
||||||
debug(PSTR("read and drop: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
|
|
||||||
sender, _msg.last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(mGetVersion(_msg) == PROTOCOL_VERSION)) {
|
|
||||||
setIndication(INDICATION_ERR_VERSION);
|
|
||||||
debug(PSTR("ver mismatch\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destination == _nc.nodeId) {
|
if (destination == _nc.nodeId) {
|
||||||
// This message is addressed to this node
|
// This message is addressed to this node
|
||||||
@@ -86,326 +391,215 @@ inline void transportProcess() {
|
|||||||
mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
|
mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD));
|
||||||
// null terminate data
|
// null terminate data
|
||||||
_msg.data[mGetLength(_msg)] = 0x00;
|
_msg.data[mGetLength(_msg)] = 0x00;
|
||||||
|
|
||||||
|
// update routing table if msg not from parent
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
if (_msg.last != _nc.parentNodeId) {
|
if (last != _nc.parentNodeId) {
|
||||||
// Message is from one of the child nodes. Add it to routing table.
|
// Message is from one of the child nodes. Add it to routing table.
|
||||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, _msg.last);
|
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check if sender requests an ack back.
|
// Check if sender requests an ack back.
|
||||||
if (mGetRequestAck(_msg)) {
|
if (mGetRequestAck(_msg)) {
|
||||||
// Copy message
|
_msgTmp = _msg; // Copy message
|
||||||
_msgTmp = _msg;
|
mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop)
|
||||||
mSetRequestAck(_msgTmp,false); // Reply without ack flag (otherwise we would end up in an eternal loop)
|
mSetAck(_msgTmp, true); // set ACK flag
|
||||||
mSetAck(_msgTmp,true);
|
|
||||||
_msgTmp.sender = _nc.nodeId;
|
_msgTmp.sender = _nc.nodeId;
|
||||||
_msgTmp.destination = sender;
|
_msgTmp.destination = sender;
|
||||||
_sendRoute(_msgTmp);
|
// send ACK
|
||||||
}
|
debug(PSTR("ACK msg\n"));
|
||||||
|
// use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
|
||||||
|
transportSendRoute(_msgTmp);
|
||||||
|
}
|
||||||
|
if(!mGetAck(_msg)) {
|
||||||
|
// only process if not ACK
|
||||||
|
if (command == C_INTERNAL) {
|
||||||
|
// Process signing related internal messages
|
||||||
|
if (signerProcessInternal(_msg)) {
|
||||||
|
return; // Signer processing indicated no further action needed
|
||||||
|
}
|
||||||
|
#if !defined(MY_GATEWAY_FEATURE)
|
||||||
|
if (type == I_ID_RESPONSE) {
|
||||||
|
#if (MY_NODE_ID == AUTO)
|
||||||
|
// only active if node ID dynamic
|
||||||
|
transportAssignNodeID(_msg.getByte());
|
||||||
|
#endif
|
||||||
|
return; // no further processing required
|
||||||
|
}
|
||||||
|
if (type == I_FIND_PARENT_RESPONSE) {
|
||||||
|
#if !defined(MY_GATEWAY_FEATURE)
|
||||||
|
// Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have.
|
||||||
|
uint8_t distance = _msg.getByte();
|
||||||
|
debug(PSTR("node %d replied, d=%d, _nc.distance=%d\n"), sender, distance,_nc.distance);
|
||||||
|
if (isValidDistance(distance)) {
|
||||||
|
// Distance to gateway is one more for us w.r.t. parent
|
||||||
|
distance++;
|
||||||
|
// update settings if distance shorter or preferred parent found
|
||||||
|
if (((isValidDistance(distance) && distance < _nc.distance) || (!_autoFindParent && sender == MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) {
|
||||||
|
// Found a neighbor closer to GW than previously found
|
||||||
|
if (!_autoFindParent && sender == MY_PARENT_NODE_ID) {
|
||||||
|
_transportSM.preferredParentFound = true;
|
||||||
|
debug(PSTR("preferred parent found\n"));
|
||||||
|
}
|
||||||
|
_nc.distance = distance;
|
||||||
|
_nc.parentNodeId = sender;
|
||||||
|
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// general
|
||||||
|
if (type == I_PING) {
|
||||||
|
debug(PSTR("node pinged by %d, hops=%d\n"), sender, _msg.getByte());
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01));
|
||||||
|
return; // no further processing required
|
||||||
|
}
|
||||||
|
if (type == I_PONG) {
|
||||||
|
if (_transportSM.pingActive) {
|
||||||
|
_transportSM.pingActive = false;
|
||||||
|
_transportSM.pingResponse = _msg.getByte();
|
||||||
|
debug(PSTR("ping replied, hops=%d\n"), _transportSM.pingResponse);
|
||||||
|
}
|
||||||
|
return; // no further processing required
|
||||||
|
}
|
||||||
|
|
||||||
if (command == C_INTERNAL) {
|
if (_processInternalMessages()) {
|
||||||
// Process signing related internal messages
|
return; // no further processing required
|
||||||
if (signerProcessInternal(_msg)) {
|
}
|
||||||
return; // Signer processing indicated no further action needed
|
} else if (command == C_STREAM) {
|
||||||
}
|
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||||
if (type == I_FIND_PARENT_RESPONSE) {
|
if(firmwareOTAUpdateProcess()){
|
||||||
if (_autoFindParent) {
|
return; // OTA FW update processing indicated no further action needed
|
||||||
// We've received a reply to a FIND_PARENT message. Check if the distance is
|
|
||||||
// shorter than we already have.
|
|
||||||
uint8_t distance = _msg.getByte();
|
|
||||||
if (isValidDistance(distance))
|
|
||||||
{
|
|
||||||
// Distance to gateway is one more for us w.r.t. parent
|
|
||||||
distance++;
|
|
||||||
if (isValidDistance(distance) && (distance < _nc.distance)) {
|
|
||||||
// Found a neighbor closer to GW than previously found
|
|
||||||
setIndication(INDICATION_GOT_PARENT);
|
|
||||||
_nc.distance = distance;
|
|
||||||
_nc.parentNodeId = sender;
|
|
||||||
hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, _nc.parentNodeId);
|
|
||||||
hwWriteConfig(EEPROM_DISTANCE_ADDRESS, _nc.distance);
|
|
||||||
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
return;
|
|
||||||
} else if (sender == GATEWAY_ADDRESS) {
|
|
||||||
if (type == I_ID_RESPONSE) {
|
|
||||||
_nc.nodeId = _msg.getByte();
|
|
||||||
if (_nc.nodeId == AUTO) {
|
|
||||||
// sensor net gateway will return max id if all sensor id are taken
|
|
||||||
setIndication(INDICATION_ERR_NET_FULL);
|
|
||||||
debug(PSTR("full\n"));
|
|
||||||
// Nothing else we can do...
|
|
||||||
_infiniteLoop();
|
|
||||||
|
|
||||||
}
|
|
||||||
setIndication(INDICATION_GOT_NODEID);
|
|
||||||
transportPresentNode();
|
|
||||||
if (presentation)
|
|
||||||
presentation();
|
|
||||||
// Write id to EEPROM
|
|
||||||
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, _nc.nodeId);
|
|
||||||
debug(PSTR("id=%d\n"), _nc.nodeId);
|
|
||||||
} else {
|
|
||||||
_processInternalMessages();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
} else if (command == C_STREAM) {
|
|
||||||
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
|
||||||
if(firmwareOTAUpdateProcess()){
|
|
||||||
return; // OTA FW update processing indicated no further action needed
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#if defined(MY_GATEWAY_FEATURE)
|
#if defined(MY_GATEWAY_FEATURE)
|
||||||
// Hand over message to controller
|
// Hand over message to controller
|
||||||
gatewayTransportSend(_msg);
|
gatewayTransportSend(_msg);
|
||||||
|
#else
|
||||||
|
// Call incoming message callback if available
|
||||||
|
if (receive) {
|
||||||
|
receive(_msg);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
// Call incoming message callback if available
|
return;
|
||||||
if (receive) {
|
}
|
||||||
|
else if (destination == BROADCAST_ADDRESS) {
|
||||||
|
// broadcast
|
||||||
|
debug(PSTR("MSG:BC\n"));
|
||||||
|
if (command == C_INTERNAL) {
|
||||||
|
if (isTransportOK()) {
|
||||||
|
// only reply if node is fully operational
|
||||||
|
if (type == I_FIND_PARENT) {
|
||||||
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
|
if (sender != _nc.parentNodeId) { // no circular reference
|
||||||
|
debug(PSTR("FPREQ from %d\n"), sender); // FPR: find parent request
|
||||||
|
// node is in our range, update routing table - important if node has new repeater as parent
|
||||||
|
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender);
|
||||||
|
// check if uplink functional - node can only be parent node if link to GW functional
|
||||||
|
// this also prevents circular references in case GW ooo
|
||||||
|
if (transportPingNode(GATEWAY_ADDRESS) != INVALID_HOPS) { // eventually use _nc.distance instead of INVALID_HOPS
|
||||||
|
// random wait to minimize collisions
|
||||||
|
debug(PSTR("GWL ok, FPRES to %d\n"), sender); // GW link ok, respond to request
|
||||||
|
delay(hwMillis() & 0x3ff);
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
return; // no further processing required
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == I_DISCOVER) {
|
||||||
|
if (last == _nc.parentNodeId) {
|
||||||
|
// random wait to minimize collisions
|
||||||
|
delay(hwMillis() & 0x3ff);
|
||||||
|
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId));
|
||||||
|
// no return here (for fwd if repeater)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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 && isTransportOK()){
|
||||||
|
debug(PSTR("FWD BC msg\n")); // forward BC msg
|
||||||
|
transportRouteMessage(_msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Call incoming message callback if available, but only if message received from parent
|
||||||
|
if (command != C_INTERNAL && last == _nc.parentNodeId && receive) {
|
||||||
receive(_msg);
|
receive(_msg);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
} else if (destination == BROADCAST_ADDRESS) {
|
}
|
||||||
if (command == C_INTERNAL) {
|
else {
|
||||||
if (type==I_DISCOVER && last==_nc.parentNodeId) {
|
// msg not no us and not BC, relay msg
|
||||||
// only process if received from parent
|
#if defined(MY_REPEATER_FEATURE)
|
||||||
debug(PSTR("discovery signal\n"));
|
if (isTransportOK()) {
|
||||||
// random wait to minimize collisions
|
debug(PSTR("REL MSG\n")); // relay msg
|
||||||
wait(hwMillis() & 0x3ff);
|
// only update routing table to uplink
|
||||||
_sendRoute(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE, false).set(_nc.parentNodeId));
|
if (last != _nc.parentNodeId) {
|
||||||
// repeat bc signal
|
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last);
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
|
||||||
// controlled repeating
|
|
||||||
debug(PSTR("repeat discovery signal\n"));
|
|
||||||
_sendRoute(_msg);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
if (command == C_INTERNAL) {
|
||||||
}
|
if (type == I_PING || type == I_PONG) {
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
uint8_t hopsCnt = _msg.getByte();
|
||||||
if (_nc.nodeId != AUTO) {
|
if (hopsCnt != MAX_HOPS) {
|
||||||
// If this node have an id, relay the message
|
debug(PSTR("relay ping/pong, hops=%d\n"), hopsCnt);
|
||||||
if (command == C_INTERNAL && type == I_FIND_PARENT) {
|
_msg.set((uint8_t)(hopsCnt + 1));
|
||||||
if (sender != _nc.parentNodeId) {
|
|
||||||
if (_nc.distance == DISTANCE_INVALID)
|
|
||||||
transportFindParentNode();
|
|
||||||
|
|
||||||
if (_nc.distance != DISTANCE_INVALID) {
|
|
||||||
// Relaying nodes should always answer ping messages
|
|
||||||
// Wait a random delay of 0-2 seconds to minimize collision
|
|
||||||
// between ping ack messages from other relaying nodes
|
|
||||||
wait(hwMillis() & 0x3ff);
|
|
||||||
transportSendWrite(sender, build(_msg, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (to == _nc.nodeId) {
|
|
||||||
// We should try to relay this message to another node
|
|
||||||
_sendRoute(_msg);
|
|
||||||
}
|
}
|
||||||
|
// Relay this message to another node
|
||||||
|
transportRouteMessage(_msg);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
debug(PSTR("REL MSG, but not a repeater\n"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void transportProcessFIFO() {
|
||||||
|
// process all msgs in FIFO
|
||||||
|
while (transportAvailable()) {
|
||||||
|
transportProcessMessage();
|
||||||
|
}
|
||||||
|
#if defined(MY_OTA_FIRMWARE_FEATURE)
|
||||||
|
if (isTransportOK()) {
|
||||||
|
// only process if transport ok
|
||||||
|
firmwareOTAUpdateRequest();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean transportSendWrite(uint8_t to, MyMessage &message) {
|
bool transportSendWrite(uint8_t to, MyMessage &message) {
|
||||||
|
// set protocol version and update last
|
||||||
mSetVersion(message, PROTOCOL_VERSION);
|
mSetVersion(message, PROTOCOL_VERSION);
|
||||||
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
|
|
||||||
message.last = _nc.nodeId;
|
message.last = _nc.nodeId;
|
||||||
setIndication(INDICATION_TX);
|
|
||||||
|
|
||||||
|
// sign message if required
|
||||||
|
if (!signerSignMsg(message)) {
|
||||||
|
debug(PSTR("!sign fail\n"));
|
||||||
|
setIndication(INDICATION_ERR_SIGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// msg length changes if signed
|
||||||
|
uint8_t length = mGetSigned(message) ? MAX_MESSAGE_LENGTH : mGetLength(message);
|
||||||
|
|
||||||
|
// send
|
||||||
|
setIndication(INDICATION_TX);
|
||||||
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
|
bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length));
|
||||||
if (!ok) setIndication(INDICATION_ERR_TX);
|
|
||||||
|
|
||||||
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
|
debug(PSTR("send: %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,st=%s:%s\n"),
|
||||||
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
|
message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type,
|
||||||
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf));
|
mGetPayloadType(message), mGetLength(message), mGetSigned(message), to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf));
|
||||||
|
|
||||||
return ok;
|
return (ok || to==BROADCAST_ADDRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
boolean transportSendRoute(MyMessage &message) {
|
|
||||||
#if defined(MY_REPEATER_FEATURE)
|
|
||||||
uint8_t last = message.last;
|
|
||||||
#endif
|
|
||||||
bool ok;
|
|
||||||
|
|
||||||
// If we still don't have any parent id, re-request and skip this message.
|
|
||||||
if (_nc.parentNodeId == AUTO) {
|
|
||||||
setIndication(INDICATION_ERR_FIND_PARENT);
|
|
||||||
transportFindParentNode();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we still don't have any node id, re-request and skip this message.
|
|
||||||
if (_nc.nodeId == AUTO) {
|
|
||||||
setIndication(INDICATION_ERR_GET_NODEID);
|
|
||||||
transportRequestNodeId();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSetVersion(message, PROTOCOL_VERSION);
|
|
||||||
|
|
||||||
if (!signerSignMsg(message)) {
|
|
||||||
setIndication(INDICATION_ERR_SIGN);
|
|
||||||
debug(PSTR("sign fail\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(MY_REPEATER_FEATURE)
|
|
||||||
|
|
||||||
// None repeating node... We can only send to our parent
|
|
||||||
ok = transportSendWrite(_nc.parentNodeId, message);
|
|
||||||
#else
|
|
||||||
uint8_t sender = message.sender;
|
|
||||||
uint8_t dest = message.destination;
|
|
||||||
if (dest == GATEWAY_ADDRESS) {
|
|
||||||
// Store this address in routing table (if repeater)
|
|
||||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
|
|
||||||
// If destination is the gateway or if we aren't a repeater, let
|
|
||||||
// our parent take care of the message
|
|
||||||
ok = transportSendWrite(_nc.parentNodeId, message);
|
|
||||||
} else {
|
|
||||||
// Relay the message
|
|
||||||
uint8_t route;
|
|
||||||
// INTERMEDIATE FIX: make sure corrupted routing table is not interfering with BC - observed several cases -tekka
|
|
||||||
if (dest!=BROADCAST_ADDRESS) {
|
|
||||||
route = hwReadConfig(EEPROM_ROUTES_ADDRESS+dest);
|
|
||||||
} else route = BROADCAST_ADDRESS;
|
|
||||||
if (route > GATEWAY_ADDRESS && route < BROADCAST_ADDRESS) {
|
|
||||||
// This message should be forwarded to a child node. If we send message
|
|
||||||
// to this nodes pipe then all children will receive it because the are
|
|
||||||
// all listening to this nodes pipe.
|
|
||||||
//
|
|
||||||
// +----B
|
|
||||||
// -A
|
|
||||||
// +----C------D
|
|
||||||
//
|
|
||||||
// We're node C, Message comes from A and has destination D
|
|
||||||
//
|
|
||||||
// Message destination is not gateway and is in routing table for this node.
|
|
||||||
// Send it downstream
|
|
||||||
return transportSendWrite(route, message);
|
|
||||||
} else if (sender == GATEWAY_ADDRESS && dest == BROADCAST_ADDRESS) {
|
|
||||||
// Node has not yet received any id. We need to send it
|
|
||||||
// by doing a broadcast sending,
|
|
||||||
return transportSendWrite(BROADCAST_ADDRESS, message);
|
|
||||||
}
|
|
||||||
#if defined (MY_GATEWAY_FEATURE)
|
|
||||||
// Destination isn't in our routing table and isn't a broadcast address
|
|
||||||
// Nothing to do here
|
|
||||||
debug(PSTR("Destination %d unknown\n"), dest);
|
|
||||||
return false;
|
|
||||||
# else
|
|
||||||
// A message comes from a child node and we have no
|
|
||||||
// route for it.
|
|
||||||
//
|
|
||||||
// +----B
|
|
||||||
// -A
|
|
||||||
// +----C------D <-- Message comes from D
|
|
||||||
//
|
|
||||||
// We're node C
|
|
||||||
//
|
|
||||||
// Message should be passed to node A (this nodes relay)
|
|
||||||
|
|
||||||
// This message should be routed back towards sensor net gateway
|
|
||||||
ok = transportSendWrite(_nc.parentNodeId, message);
|
|
||||||
|
|
||||||
// Add this child to our "routing table" if it not already exist
|
|
||||||
hwWriteConfig(EEPROM_ROUTES_ADDRESS+sender, last);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
// Failure when sending to parent node. The parent node might be down and we
|
|
||||||
// need to find another route to gateway.
|
|
||||||
_failedTransmissions++;
|
|
||||||
if (_autoFindParent && _failedTransmissions > SEARCH_FAILURES) {
|
|
||||||
transportFindParentNode();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_failedTransmissions = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void transportRequestNodeId() {
|
|
||||||
setIndication(INDICATION_REQ_NODEID);
|
|
||||||
debug(PSTR("req id\n"));
|
|
||||||
transportSetAddress(_nc.nodeId);
|
|
||||||
build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("");
|
|
||||||
transportSendWrite(_nc.parentNodeId, _msg);
|
|
||||||
wait(2000, C_INTERNAL, I_ID_RESPONSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void transportPresentNode() {
|
|
||||||
setIndication(INDICATION_PRESENT);
|
|
||||||
// Open reading pipe for messages directed to this node (set write pipe to same)
|
|
||||||
transportSetAddress(_nc.nodeId);
|
|
||||||
// Present node and request config
|
|
||||||
#ifdef MY_GATEWAY_FEATURE
|
|
||||||
// Send presentation for this gateway device
|
|
||||||
#ifdef MY_REPEATER_FEATURE
|
|
||||||
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
|
|
||||||
#else
|
|
||||||
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
if (_nc.nodeId != AUTO) {
|
|
||||||
#ifdef MY_OTA_FIRMWARE_FEATURE
|
|
||||||
presentBootloaderInformation();
|
|
||||||
#endif
|
|
||||||
// Send signing preferences for this node to the GW
|
|
||||||
signerPresentation(_msg, GATEWAY_ADDRESS);
|
|
||||||
|
|
||||||
// Send presentation for this radio node
|
|
||||||
#ifdef MY_REPEATER_FEATURE
|
|
||||||
present(NODE_SENSOR_ID, S_ARDUINO_REPEATER_NODE);
|
|
||||||
#else
|
|
||||||
present(NODE_SENSOR_ID, S_ARDUINO_NODE);
|
|
||||||
#endif
|
|
||||||
// Send a configuration exchange request to controller
|
|
||||||
// Node sends parent node. Controller answers with latest node configuration
|
|
||||||
// which is picked up in process()
|
|
||||||
_sendRoute(build(_msg, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CONFIG, false).set(_nc.parentNodeId));
|
|
||||||
|
|
||||||
// Wait configuration reply.
|
|
||||||
wait(2000, C_INTERNAL, I_CONFIG);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void transportFindParentNode() {
|
|
||||||
static boolean findingParentNode = false;
|
|
||||||
|
|
||||||
if (findingParentNode)
|
|
||||||
return;
|
|
||||||
findingParentNode = true;
|
|
||||||
|
|
||||||
setIndication(INDICATION_FIND_PARENT);
|
|
||||||
|
|
||||||
_failedTransmissions = 0;
|
|
||||||
|
|
||||||
// Set distance to max
|
|
||||||
_nc.distance = 255;
|
|
||||||
|
|
||||||
// Send ping message to BROADCAST_ADDRESS (to which all relaying nodes and gateway listens and should reply to)
|
|
||||||
debug(PSTR("find parent\n"));
|
|
||||||
|
|
||||||
build(_msg, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("");
|
|
||||||
// Write msg, but suppress recursive parent search
|
|
||||||
transportSendWrite(BROADCAST_ADDRESS, _msg);
|
|
||||||
|
|
||||||
// Wait for ping response.
|
|
||||||
wait(2000);
|
|
||||||
findingParentNode = false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,43 +17,228 @@
|
|||||||
* version 2 as published by the Free Software Foundation.
|
* version 2 as published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file MyTransport.h
|
||||||
|
*
|
||||||
|
* @brief API declaration for MyTransport
|
||||||
|
*/
|
||||||
#ifndef MyTransport_h
|
#ifndef MyTransport_h
|
||||||
#define MyTransport_h
|
#define MyTransport_h
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "MySensorsCore.h"
|
#include "MySensorsCore.h"
|
||||||
|
|
||||||
// Search for a new parent node after this many transmission failures
|
#define TRANSMISSION_FAILURES 5 //!< search for a new parent node after this many transmission failures
|
||||||
#define SEARCH_FAILURES 5
|
#define TIMEOUT_FAILURE_STATE 10000 //!< duration failure state
|
||||||
|
#define STATE_TIMEOUT 2000 //!< general state timeout
|
||||||
|
#define STATE_RETRIES 3 //!< retries before switching to FAILURE
|
||||||
|
#define AUTO 255 //!< ID 255 is reserved for auto initialization of nodeId.
|
||||||
|
#define BROADCAST_ADDRESS ((uint8_t)255) //!< broadcasts are addressed to ID 255
|
||||||
|
#define DISTANCE_INVALID ((uint8_t)255) //!< invalid distance when searching for parent
|
||||||
|
#define MAX_HOPS ((uint8_t)254) //!< maximal mumber of hops for ping/pong
|
||||||
|
#define INVALID_HOPS ((uint8_t)255) //!< invalid hops
|
||||||
|
|
||||||
#define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId.
|
#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 BROADCAST_ADDRESS ((uint8_t)0xFF)
|
/**
|
||||||
|
* @brief SM state
|
||||||
|
*
|
||||||
|
* This structure stores SM state definitions
|
||||||
|
*/
|
||||||
|
struct State {
|
||||||
|
void(*Transition)(); //!< state transition function
|
||||||
|
void(*Update)(); //!< state update function
|
||||||
|
};
|
||||||
|
|
||||||
// invalid distance when searching for parent
|
/**
|
||||||
#define DISTANCE_INVALID (0xFF)
|
* @brief Status variables and SM state
|
||||||
|
*
|
||||||
// Common functions in all radio drivers
|
* This structure stores transport status and SM variables
|
||||||
#ifdef MY_OTA_FIRMWARE_FEATURE
|
*/
|
||||||
// do a crc16 on the whole received firmware
|
typedef struct {
|
||||||
bool transportIsValidFirmware();
|
State* currentState; //!< pointer to current fsm state
|
||||||
#endif
|
uint32_t stateEnter; //!< state enter timepoint
|
||||||
|
bool findingParentNode : 1; //!< flag finding parent node is active
|
||||||
|
bool preferredParentFound : 1; //!< flag preferred parent found
|
||||||
|
bool uplinkOk : 1; //!< flag uplink ok
|
||||||
|
bool pingActive : 1; //!< flag ping active
|
||||||
|
uint8_t reserved : 4; //!< reserved
|
||||||
|
uint8_t retries : 4; //!< retries / state re-enter
|
||||||
|
uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions
|
||||||
|
uint8_t pingResponse; //!< stores hops received in I_PONG
|
||||||
|
} __attribute__((packed)) transportSM;
|
||||||
|
|
||||||
|
|
||||||
|
// PRIVATE functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize SM variables and transport HW
|
||||||
|
*/
|
||||||
|
void stInitTransition();
|
||||||
|
/**
|
||||||
|
* @brief Find parent
|
||||||
|
*/
|
||||||
|
void stParentTransition();
|
||||||
|
/**
|
||||||
|
* @brief Verify find parent responses
|
||||||
|
*/
|
||||||
|
void stParentUpdate();
|
||||||
|
/**
|
||||||
|
* @brief Send ID request
|
||||||
|
*/
|
||||||
|
void stIDTransition();
|
||||||
|
/**
|
||||||
|
* @brief Verify ID response and GW link
|
||||||
|
*/
|
||||||
|
void stIDUpdate();
|
||||||
|
/**
|
||||||
|
* @brief Send uplink ping request
|
||||||
|
*/
|
||||||
|
void stUplinkTransition();
|
||||||
|
/**
|
||||||
|
* @brief Set transport OK
|
||||||
|
*/
|
||||||
|
void stOKTransition();
|
||||||
|
/**
|
||||||
|
* @brief Monitor transport link
|
||||||
|
*/
|
||||||
|
void stOKUpdate();
|
||||||
|
/**
|
||||||
|
* @brief Transport failure and power down radio
|
||||||
|
*/
|
||||||
|
void stFailureTransition();
|
||||||
|
/**
|
||||||
|
* @brief Re-initialize transport after timeout
|
||||||
|
*/
|
||||||
|
void stFailureUpdate();
|
||||||
|
/**
|
||||||
|
* @brief Switch SM state
|
||||||
|
* @param newState New state to switch SM to
|
||||||
|
*/
|
||||||
|
void transportSwitchSM(State& newState);
|
||||||
|
/**
|
||||||
|
* @brief Update SM state
|
||||||
|
*/
|
||||||
|
void transportUpdateSM();
|
||||||
|
/**
|
||||||
|
* @brief Request time in current SM state
|
||||||
|
* @return ms in current state
|
||||||
|
*/
|
||||||
|
uint32_t transportTimeInState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process all pending messages in RX FIFO
|
||||||
|
*/
|
||||||
|
void transportProcessFIFO();
|
||||||
|
/**
|
||||||
|
* @brief Receive message from RX FIFO and process
|
||||||
|
*/
|
||||||
|
void transportProcessMessage();
|
||||||
|
/**
|
||||||
|
* @brief Assign node ID
|
||||||
|
* @param newNodeId New node ID
|
||||||
|
*/
|
||||||
|
void transportAssignNodeID(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 cmd Specific command
|
||||||
|
* @param msgtype Specific message type
|
||||||
|
* @return true if specified command received within waiting time
|
||||||
|
*/
|
||||||
|
bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype);
|
||||||
|
/**
|
||||||
|
* @brief Ping node
|
||||||
|
* @param targetId Node to be pinged
|
||||||
|
* @return hops from pinged node or 255 if no answer received within 2000ms
|
||||||
|
*/
|
||||||
|
uint8_t transportPingNode(uint8_t targetId);
|
||||||
|
/**
|
||||||
|
* @brief Send and route message according to destination
|
||||||
|
*
|
||||||
|
* This function is used in MyTransport and omits the transport state check, i.e. message can be sent even if transport is !OK
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @return true if message sent successfully
|
||||||
|
*/
|
||||||
|
bool transportRouteMessage(MyMessage &message);
|
||||||
|
/**
|
||||||
|
* @brief Send and route message according to destination with transport state check
|
||||||
|
* @param message
|
||||||
|
* @return true if message sent successfully and false if sending error or transport !OK
|
||||||
|
*/
|
||||||
|
bool transportSendRoute(MyMessage &message);
|
||||||
|
/**
|
||||||
|
* @brief Send message to recipient
|
||||||
|
* @param to Recipient of message
|
||||||
|
* @param message
|
||||||
|
* @return true if message sent successfully
|
||||||
|
*/
|
||||||
|
bool transportSendWrite(uint8_t to, MyMessage &message);
|
||||||
|
|
||||||
|
// PUBLIC functions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize transport and SM
|
||||||
|
*/
|
||||||
|
void transportInitialize();
|
||||||
|
/**
|
||||||
|
* @brief Process FIFO msg and update SM
|
||||||
|
*/
|
||||||
void transportProcess();
|
void transportProcess();
|
||||||
void transportRequestNodeId();
|
/**
|
||||||
void transportPresentNode();
|
* @brief Verify transport status
|
||||||
void transportFindParentNode();
|
* @return true if transport is initialize and ready
|
||||||
boolean transportSendRoute(MyMessage &message);
|
*/
|
||||||
boolean transportSendWrite(uint8_t to, MyMessage &message);
|
bool isTransportOK();
|
||||||
|
/**
|
||||||
|
* @brief Clear routing table
|
||||||
|
*/
|
||||||
|
void transportClearRoutingTable();
|
||||||
|
/**
|
||||||
|
* @brief Return heart beat, i.e. ms in current state
|
||||||
|
*/
|
||||||
|
uint32_t transportGetHeartbeat();
|
||||||
|
|
||||||
// "Interface" functions for radio driver
|
// interface functions for radio driver
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize transport HW
|
||||||
|
* @return true if initalization successful
|
||||||
|
*/
|
||||||
bool transportInit();
|
bool transportInit();
|
||||||
|
/**
|
||||||
|
* @brief Set node address
|
||||||
|
*/
|
||||||
void transportSetAddress(uint8_t address);
|
void transportSetAddress(uint8_t address);
|
||||||
|
/**
|
||||||
|
* @brief Retrieve node address
|
||||||
|
*/
|
||||||
uint8_t transportGetAddress();
|
uint8_t transportGetAddress();
|
||||||
|
/**
|
||||||
|
* @brief Send message
|
||||||
|
* @param to recipient
|
||||||
|
* @param data message to be sent
|
||||||
|
* @param len length of message (header + payload)
|
||||||
|
* @return true if message sent successfully
|
||||||
|
*/
|
||||||
bool transportSend(uint8_t to, const void* data, uint8_t len);
|
bool transportSend(uint8_t to, const void* data, uint8_t len);
|
||||||
bool transportAvailable(uint8_t *to);
|
/**
|
||||||
uint8_t transportReceive(void* data);
|
* @brief Verify if RX FIFO has pending messages
|
||||||
|
* @return true if message available in RX FIFO
|
||||||
|
*/
|
||||||
|
bool transportAvailable();
|
||||||
|
/**
|
||||||
|
* @brief Receive message from FIFO
|
||||||
|
* @return length of recevied message (header + payload)
|
||||||
|
*/
|
||||||
|
uint8_t transportReceive(void* data);
|
||||||
|
/**
|
||||||
|
* @brief Power down transport HW
|
||||||
|
*/
|
||||||
void transportPowerDown();
|
void transportPowerDown();
|
||||||
|
|
||||||
#endif
|
|
||||||
|
#endif // MyTransport_h
|
||||||
|
/** @}*/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -70,8 +70,8 @@ bool transportSend(uint8_t recipient, const void* data, uint8_t len) {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool transportAvailable(uint8_t *to) {
|
bool transportAvailable() {
|
||||||
bool avail = RF24_isDataAvailable(to);
|
bool avail = RF24_isDataAvailable();
|
||||||
return avail;
|
return avail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -53,11 +53,7 @@ bool transportSend(uint8_t to, const void* data, uint8_t len) {
|
|||||||
return _radio.sendWithRetry(to,data,len);
|
return _radio.sendWithRetry(to,data,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool transportAvailable(uint8_t *to) {
|
bool transportAvailable() {
|
||||||
if (_radio.TARGETID == BROADCAST_ADDRESS)
|
|
||||||
*to = BROADCAST_ADDRESS;
|
|
||||||
else
|
|
||||||
*to = _address;
|
|
||||||
return _radio.receiveDone();
|
return _radio.receiveDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/**
|
/*
|
||||||
* The MySensors Arduino library handles the wireless radio link and protocol
|
* The MySensors Arduino library handles the wireless radio link and protocol
|
||||||
* between your home built sensors/actuators and HA controller of choice.
|
* between your home built sensors/actuators and HA controller of choice.
|
||||||
* The sensors forms a self healing radio network with optional repeaters. Each
|
* The sensors forms a self healing radio network with optional repeaters. Each
|
||||||
@@ -332,14 +332,8 @@ uint8_t transportGetAddress() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool transportAvailable(uint8_t *to) {
|
bool transportAvailable() {
|
||||||
_serialProcess();
|
_serialProcess();
|
||||||
if (_packet_received == true) {
|
|
||||||
if (_recStation==BROADCAST_ADDRESS) {
|
|
||||||
*to = BROADCAST_ADDRESS;
|
|
||||||
}
|
|
||||||
else { *to = _nodeId; }
|
|
||||||
}
|
|
||||||
return _packet_received;
|
return _packet_received;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -165,16 +165,12 @@ LOCAL uint8_t RF24_getDynamicPayloadSize(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LOCAL bool RF24_isDataAvailable(uint8_t* to) {
|
LOCAL bool RF24_isDataAvailable() {
|
||||||
uint8_t pipe_num = ( RF24_getStatus() >> RX_P_NO ) & 0b0111;
|
uint8_t pipe_num = ( RF24_getStatus() >> RX_P_NO ) & 0b0111;
|
||||||
#if defined(MY_DEBUG_VERBOSE_RF24)
|
#if defined(MY_DEBUG_VERBOSE_RF24) && defined(MY_DEBUG)
|
||||||
if(pipe_num <= 5)
|
if(pipe_num <= 5)
|
||||||
RF24_DEBUG(PSTR("Data available on pipe %d\n"),pipe_num);
|
RF24_DEBUG(PSTR("Data available on pipe %d\n"),pipe_num);
|
||||||
#endif
|
#endif
|
||||||
if (pipe_num == NODE_PIPE)
|
|
||||||
*to = MY_RF24_NODE_ADDRESS;
|
|
||||||
else if (pipe_num == BROADCAST_PIPE)
|
|
||||||
*to = BROADCAST_ADDRESS;
|
|
||||||
return (pipe_num <= 5);
|
return (pipe_num <= 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ LOCAL void RF24_stopListening(void);
|
|||||||
LOCAL void RF24_powerDown(void);
|
LOCAL void RF24_powerDown(void);
|
||||||
LOCAL bool RF24_sendMessage(uint8_t recipient, const void* buf, uint8_t len);
|
LOCAL bool RF24_sendMessage(uint8_t recipient, const void* buf, uint8_t len);
|
||||||
LOCAL uint8_t RF24_getDynamicPayloadSize(void);
|
LOCAL uint8_t RF24_getDynamicPayloadSize(void);
|
||||||
LOCAL bool RF24_isDataAvailable(uint8_t* to);
|
LOCAL bool RF24_isDataAvailable();
|
||||||
LOCAL uint8_t RF24_readMessage(void* buf);
|
LOCAL uint8_t RF24_readMessage(void* buf);
|
||||||
LOCAL void RF24_setNodeAddress(uint8_t address);
|
LOCAL void RF24_setNodeAddress(uint8_t address);
|
||||||
LOCAL uint8_t RF24_getNodeID(void);
|
LOCAL uint8_t RF24_getNodeID(void);
|
||||||
|
|||||||
@@ -41,8 +41,13 @@ MY_REPEATER_FEATURE LITERAL1
|
|||||||
MY_RADIO_NRF24 LITERAL1
|
MY_RADIO_NRF24 LITERAL1
|
||||||
MY_RADIO_RFM69 LITERAL1
|
MY_RADIO_RFM69 LITERAL1
|
||||||
MY_BAUD_RATE LITERAL1
|
MY_BAUD_RATE LITERAL1
|
||||||
|
MY_REGISTRATION_FEATURE LITERAL1
|
||||||
|
MY_REGISTRATION_DEFAULT LITERAL1
|
||||||
|
MY_REGISTRATION_CONTROLLER LITERAL1
|
||||||
|
MY_CORE_COMPATIBILITY_CHECK LITERAL1
|
||||||
MY_NODE_ID LITERAL1
|
MY_NODE_ID LITERAL1
|
||||||
MY_PARENT_NODE_ID LITERAL1
|
MY_PARENT_NODE_ID LITERAL1
|
||||||
|
MY_PARENT_NODE_IS_STATIC LITERAL1
|
||||||
MY_OTA_FIRMWARE_FEATURE LITERAL1
|
MY_OTA_FIRMWARE_FEATURE LITERAL1
|
||||||
MY_OTA_FLASH_SS LITERAL1
|
MY_OTA_FLASH_SS LITERAL1
|
||||||
MY_OTA_FLASH_JDECID LITERAL1
|
MY_OTA_FLASH_JDECID LITERAL1
|
||||||
|
|||||||
Reference in New Issue
Block a user