Fix possible transport deadlock, add sanity check (#500)

This commit is contained in:
Olivier
2016-07-04 15:08:41 +02:00
committed by GitHub
parent 77bb94c16f
commit 751a5dcfb8
10 changed files with 216 additions and 99 deletions
+13
View File
@@ -81,6 +81,18 @@
//#define MY_RADIO_RFM69 //#define MY_RADIO_RFM69
//#define MY_RS485 //#define MY_RS485
/**
* @def MY_TRANSPORT_SANITY_CHECK
* @brief If enabled, node will check transport in regular intervals to detect HW issues and re-initialize in case of failure. This feature is enabled for all repeater nodes (incl. GW)
*/
//#define MY_TRANSPORT_SANITY_CHECK
/**
* @def MY_TRANSPORT_SANITY_CHECK_INTERVAL
* @brief Interval (in ms) of transport sanity checks
*/
#ifndef MY_TRANSPORT_SANITY_CHECK_INTERVAL
#define MY_TRANSPORT_SANITY_CHECK_INTERVAL ((uint32_t)60000)
#endif
/** /**
* @def MY_REGISTRATION_FEATURE * @def MY_REGISTRATION_FEATURE
* @brief If enabled, node has to register to gateway/controller before allowed to send sensor data. * @brief If enabled, node has to register to gateway/controller before allowed to send sensor data.
@@ -687,5 +699,6 @@
#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_PARENT_NODE_IS_STATIC
#define MY_REGISTRATION_CONTROLLER
#define MY_DEBUG_VERBOSE_RF24 #define MY_DEBUG_VERBOSE_RF24
#endif #endif
+1
View File
@@ -46,6 +46,7 @@ typedef enum {
INDICATION_ERR_START = 100, INDICATION_ERR_START = 100,
INDICATION_ERR_TX, //!< Failed to transmit message. INDICATION_ERR_TX, //!< Failed to transmit message.
INDICATION_ERR_TRANSPORT_FAILURE, //!< Transport failure.
INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure. INDICATION_ERR_INIT_TRANSPORT, //!< MySensors transport hardware (radio) init failure.
INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node. INDICATION_ERR_FIND_PARENT, //!< Failed to find parent node.
INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID. INDICATION_ERR_GET_NODEID, //!< Failed to receive node ID.
+132 -61
View File
@@ -20,6 +20,13 @@
#include "MyTransport.h" #include "MyTransport.h"
static uint32_t _transport_lastUplinkCheck; //!< last uplink check, to prevent GW flooding
// repeaters (this includes GW) should perform regular sanity checks for network reliability
#if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
uint32_t _transport_lastSanityCheck; //!< last sanity check
#endif
// SM: transitions and update states // SM: transitions and update states
static State stInit = { stInitTransition, NULL }; static State stInit = { stInitTransition, NULL };
static State stParent = { stParentTransition, stParentUpdate }; static State stParent = { stParentTransition, stParentUpdate };
@@ -37,18 +44,21 @@ void stInitTransition() {
_transportSM.failedUplinkTransmissions = 0; _transportSM.failedUplinkTransmissions = 0;
_transportSM.pingActive = false; _transportSM.pingActive = false;
_transportSM.transportActive = false; _transportSM.transportActive = false;
#if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_GATEWAY_FEATURE) || defined(MY_REPEATER_FEATURE)
_transport_lastSanityCheck = hwMillis();
#endif
_transport_lastUplinkCheck = 0;
// Read node settings (ID, parentId, GW distance) from EEPROM // Read node settings (ID, parentId, GW distance) from EEPROM
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
// initialize radio // initialize radio
if (!transportInit()) { if (!transportInit()) {
debug(PSTR("!RADIO:fail\n")); debug(PSTR("!TSM:RADIO:FAIL\n"));
setIndication(INDICATION_ERR_INIT_TRANSPORT); setIndication(INDICATION_ERR_INIT_TRANSPORT);
transportSwitchSM(stFailure); transportSwitchSM(stFailure);
} }
else { else {
debug(PSTR("RADIO:OK\n")); debug(PSTR("TSM:RADIO:OK\n"));
_transportSM.transportActive = true; _transportSM.transportActive = true;
#if defined(MY_GATEWAY_FEATURE) #if defined(MY_GATEWAY_FEATURE)
// Set configuration for gateway // Set configuration for gateway
@@ -74,7 +84,7 @@ void stInitTransition() {
// stParent: find parent // stParent: find parent
void stParentTransition() { void stParentTransition() {
debug(PSTR("TSM:PARENT\n")); debug(PSTR("TSM:FPAR\n")); // find parent
_transportSM.preferredParentFound = false; _transportSM.preferredParentFound = false;
_transportSM.findingParentNode = true; _transportSM.findingParentNode = true;
_transportSM.failedUplinkTransmissions = 0; _transportSM.failedUplinkTransmissions = 0;
@@ -92,6 +102,7 @@ void stParentUpdate() {
if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) { if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) {
// timeout or preferred parent found // timeout or preferred parent found
if (_nc.parentNodeId != AUTO) { if (_nc.parentNodeId != AUTO) {
debug(PSTR("TSM:FPAR:OK\n")); // find parent ok
_transportSM.findingParentNode = false; _transportSM.findingParentNode = false;
setIndication(INDICATION_GOT_PARENT); setIndication(INDICATION_GOT_PARENT);
transportSwitchSM(stID); transportSwitchSM(stID);
@@ -102,6 +113,7 @@ void stParentUpdate() {
transportSwitchSM(stParent); transportSwitchSM(stParent);
} }
else { else {
debug(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail
setIndication(INDICATION_ERR_FIND_PARENT); setIndication(INDICATION_ERR_FIND_PARENT);
transportSwitchSM(stFailure); transportSwitchSM(stFailure);
} }
@@ -122,7 +134,7 @@ void stIDTransition() {
void stIDUpdate() { void stIDUpdate() {
if (_nc.nodeId != AUTO) { if (_nc.nodeId != AUTO) {
// current node ID is valid, proceed to uplink check // current node ID is valid, proceed to uplink check
debug(PSTR("node ID=%d\n"), _nc.nodeId); debug(PSTR("TSM:CHKID:OK (ID=%d)\n"), _nc.nodeId);
setIndication(INDICATION_GOT_NODEID); setIndication(INDICATION_GOT_NODEID);
// check uplink // check uplink
transportSwitchSM(stUplink); transportSwitchSM(stUplink);
@@ -134,6 +146,7 @@ void stIDUpdate() {
} }
else { else {
// fail // fail
debug(PSTR("!TSM:CHKID:FAIL (ID=%d)\n"), _nc.nodeId);
setIndication(INDICATION_ERR_GET_NODEID); setIndication(INDICATION_ERR_GET_NODEID);
transportSwitchSM(stFailure); transportSwitchSM(stFailure);
} }
@@ -141,14 +154,19 @@ void stIDUpdate() {
} }
void stUplinkTransition() { void stUplinkTransition() {
if (transportPingNode(GATEWAY_ADDRESS) == _nc.distance) { debug(PSTR("TSM:UPL\n"));
if(transportCheckUplink(true)) {
debug(PSTR("TSM:UPL:OK\n"));
transportSwitchSM(stOK); transportSwitchSM(stOK);
} }
else transportSwitchSM(stParent); else {
debug(PSTR("!TSM:UPL:FAIL\n"));
transportSwitchSM(stParent);
}
} }
void stOKTransition() { void stOKTransition() {
debug(PSTR("TSM:OK\n")); debug(PSTR("TSM:READY\n")); // transport is ready
_transportSM.uplinkOk = true; _transportSM.uplinkOk = true;
} }
@@ -158,21 +176,22 @@ void stOKUpdate() {
if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) { if (_transportSM.failedUplinkTransmissions > TRANSMISSION_FAILURES) {
// too many uplink transmissions failed, find new parent // too many uplink transmissions failed, find new parent
#if !defined(MY_PARENT_NODE_IS_STATIC) #if !defined(MY_PARENT_NODE_IS_STATIC)
debug(PSTR("!TSM:UPL fail, SNP\n")); debug(PSTR("!TSM:UPL FAIL, SNP\n"));
transportSwitchSM(stParent); transportSwitchSM(stParent);
#else #else
debug(PSTR("!TSM:UPL fail, STATP\n")); debug(PSTR("!TSM:UPL FAIL, STATP\n"));
_transportSM.failedUplinkTransmissions = 0; _transportSM.failedUplinkTransmissions = 0;
#endif #endif
} }
#endif #endif
} }
// stFailure: ented upon HW init failure or max retries exceeded // stFailure: entered upon HW init failure or max retries exceeded
void stFailureTransition() { void stFailureTransition() {
debug(PSTR("!TSM:FAILURE\n")); debug(PSTR("!TSM:FAILURE\n"));
_transportSM.uplinkOk = false; _transportSM.uplinkOk = false;
_transportSM.transportActive = false; _transportSM.transportActive = false;
setIndication(INDICATION_ERR_INIT_TRANSPORT);
// power down transport, no need until re-init // power down transport, no need until re-init
debug(PSTR("TSM:PDT\n")); debug(PSTR("TSM:PDT\n"));
transportPowerDown(); transportPowerDown();
@@ -224,10 +243,35 @@ void transportProcess() {
// update state machine // update state machine
transportUpdateSM(); transportUpdateSM();
// process transport FIFO // process transport FIFO
if (_transportSM.transportActive) transportProcessFIFO(); transportProcessFIFO();
} }
bool transportCheckUplink(bool force) {
if (!force && (hwMillis() - _transport_lastUplinkCheck) < CHKUPL_INTERVAL) {
debug(PSTR("TSP:CHKUPL:OK (FLDCTRL)\n")); // flood control
return true;
}
// ping GW
uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS);
// verify hops
if (hopsCount != INVALID_HOPS) {
// update
_transport_lastUplinkCheck = hwMillis();
debug(PSTR("TSP:CHKUPL:OK\n"));
// did distance to GW change upstream, i.e. re-routing of uplink nodes?
if (hopsCount != _nc.distance) {
debug(PSTR("TSP:CHKUPL:DGWC (old=%d,new=%d)"), _nc.distance, hopsCount); // distance to GW changed
_nc.distance = hopsCount;
}
return true;
}
else {
debug(PSTR("TSP:CHKUPL:FAIL (hops=%d)\n"), hopsCount);
return false;
}
}
void transportAssignNodeID(uint8_t newNodeId) { void transportAssignNodeID(uint8_t newNodeId) {
// verify if ID valid // verify if ID valid
if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) { if (newNodeId != GATEWAY_ADDRESS && newNodeId != AUTO) {
@@ -235,10 +279,10 @@ void transportAssignNodeID(uint8_t newNodeId) {
transportSetAddress(newNodeId); transportSetAddress(newNodeId);
// Write ID to EEPROM // Write ID to EEPROM
hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId); hwWriteConfig(EEPROM_NODE_ID_ADDRESS, newNodeId);
debug(PSTR("assign node ID=%d\n"),newNodeId); debug(PSTR("TSP:ASSIGNID:OK (ID=%d)\n"),newNodeId);
} }
else { else {
debug(PSTR("node ID invalid (%d)\n"),newNodeId); debug(PSTR("!TSP:ASSIGNID:FAIL (ID=%d)\n"),newNodeId);
setIndication(INDICATION_ERR_NET_FULL); setIndication(INDICATION_ERR_NET_FULL);
// Nothing else we can do... // Nothing else we can do...
transportSwitchSM(stFailure); transportSwitchSM(stFailure);
@@ -250,7 +294,7 @@ bool transportRouteMessage(MyMessage &message) {
uint8_t route; uint8_t route;
if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) { if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) {
debug(PSTR("finding parent active, msg not send\n")); debug(PSTR("!TSP:FPAR:ACTIVE (msg not send)\n"));
// request to send a non-BC message while finding parent active, abort // request to send a non-BC message while finding parent active, abort
return false; return false;
} }
@@ -269,7 +313,7 @@ bool transportRouteMessage(MyMessage &message) {
// route unknown // route unknown
if (message.last != _nc.parentNodeId) { if (message.last != _nc.parentNodeId) {
// message not from parent, i.e. child node - route it to parent // 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); debug(PSTR("!TSP:ROUTING:DEST UNKNOWN (dest=%d, STP=%d)\n"), destination, _nc.parentNodeId);
route = _nc.parentNodeId; route = _nc.parentNodeId;
} }
else { else {
@@ -306,7 +350,7 @@ bool transportSendRoute(MyMessage &message) {
} }
else { else {
// TNR: transport not ready // TNR: transport not ready
debug(PSTR("!TNR\n")); debug(PSTR("!TSP:SEND:TNR\n"));
return false; return false;
} }
} }
@@ -318,7 +362,8 @@ bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){
_msg.type = !msgtype; _msg.type = !msgtype;
bool expectedResponse = false; bool expectedResponse = false;
while ((hwMillis() - enter < ms) && !expectedResponse) { while ((hwMillis() - enter < ms) && !expectedResponse) {
transportProcess(); // process incoming messages
transportProcessFIFO();
#if defined(ARDUINO_ARCH_ESP8266) #if defined(ARDUINO_ARCH_ESP8266)
yield(); yield();
#endif #endif
@@ -330,15 +375,17 @@ bool transportWait(uint32_t ms, uint8_t cmd, uint8_t msgtype){
uint8_t transportPingNode(uint8_t targetId) { uint8_t transportPingNode(uint8_t targetId) {
if(!_transportSM.pingActive){ if(!_transportSM.pingActive){
if(targetId == _nc.nodeId) { if(targetId == _nc.nodeId) {
// ping to ourself // ping to ourself, pingActive remains false
return 0; return 0;
} }
_transportSM.pingActive = true; _transportSM.pingActive = true;
_transportSM.pingResponse = INVALID_HOPS; _transportSM.pingResponse = INVALID_HOPS;
debug(PSTR("pinging node %d\n"), targetId); debug(PSTR("TSP:PING:SEND (dest=%d)\n"), targetId);
transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01)); transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01));
// Wait for ping reply or timeout // Wait for ping reply or timeout
transportWait(2000, C_INTERNAL, I_PONG); transportWait(2000, C_INTERNAL, I_PONG);
// make sure missing I_PONG msg does not block pinging function by leaving pignActive=true
_transportSM.pingActive = false;
return _transportSM.pingResponse; return _transportSM.pingResponse;
} }
else { else {
@@ -347,9 +394,10 @@ uint8_t transportPingNode(uint8_t targetId) {
} }
void transportClearRoutingTable() { void transportClearRoutingTable() {
debug(PSTR("CRT\n")); // clear routing table for (uint8_t i = 0; i != 255; i++) {
for(uint8_t i = 0; i!=255; i++) hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, BROADCAST_ADDRESS);
hwWriteConfig(EEPROM_ROUTES_ADDRESS+i, BROADCAST_ADDRESS); }
debug(PSTR("TSP:CRT:OK\n")); // clear routing table
} }
uint32_t transportGetHeartbeat() { uint32_t transportGetHeartbeat() {
@@ -361,31 +409,32 @@ void transportProcessMessage() {
uint8_t payloadLength = transportReceive((uint8_t *)&_msg); uint8_t payloadLength = transportReceive((uint8_t *)&_msg);
(void)payloadLength; // currently not used, but good to test for CRC-ok but corrupt msgs (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;
debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), debug(PSTR("TSP:MSG:READ %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"),
sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf)); sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf));
// verify protocol version
if(mGetVersion(_msg) != PROTOCOL_VERSION) {
setIndication(INDICATION_ERR_VERSION);
debug(PSTR("!TSP:MSG:PVER mismatch\n")); // protocol version
return;
}
// Reject massages that do not pass verification
if (!signerVerifyMsg(_msg)) {
setIndication(INDICATION_ERR_SIGN);
debug(PSTR("!TSP:MSG:SIGN verify fail\n"));
return;
}
if (destination == _nc.nodeId) { if (destination == _nc.nodeId) {
// This message is addressed to this node // This message is addressed to this node
@@ -410,7 +459,7 @@ void transportProcessMessage() {
_msgTmp.sender = _nc.nodeId; _msgTmp.sender = _nc.nodeId;
_msgTmp.destination = sender; _msgTmp.destination = sender;
// send ACK // send ACK
debug(PSTR("ACK msg\n")); debug(PSTR("TSP:MSG:ACK msg\n"));
// use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply // use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply
transportSendRoute(_msgTmp); transportSendRoute(_msgTmp);
} }
@@ -433,7 +482,7 @@ void transportProcessMessage() {
#if !defined(MY_GATEWAY_FEATURE) #if !defined(MY_GATEWAY_FEATURE)
// Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have. // Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have.
uint8_t distance = _msg.getByte(); uint8_t distance = _msg.getByte();
debug(PSTR("node %d replied, d=%d, _nc.distance=%d\n"), sender, distance,_nc.distance); debug(PSTR("TSP:MSG:FPAR RES (ID=%d, dist=%d)\n"), sender, distance);
if (isValidDistance(distance)) { if (isValidDistance(distance)) {
// Distance to gateway is one more for us w.r.t. parent // Distance to gateway is one more for us w.r.t. parent
distance++; distance++;
@@ -442,11 +491,11 @@ void transportProcessMessage() {
// Found a neighbor closer to GW than previously found // Found a neighbor closer to GW than previously found
if (!_autoFindParent && sender == MY_PARENT_NODE_ID) { if (!_autoFindParent && sender == MY_PARENT_NODE_ID) {
_transportSM.preferredParentFound = true; _transportSM.preferredParentFound = true;
debug(PSTR("preferred parent found\n")); debug(PSTR("TSP:MSG:FPAR (PPAR FOUND)\n")); // preferred parent found
} }
_nc.distance = distance; _nc.distance = distance;
_nc.parentNodeId = sender; _nc.parentNodeId = sender;
debug(PSTR("parent=%d, d=%d\n"), _nc.parentNodeId, _nc.distance); debug(PSTR("TSP:MSG:PAR OK (ID=%d, dist=%d)\n"), _nc.parentNodeId, _nc.distance);
} }
} }
return; return;
@@ -455,7 +504,7 @@ void transportProcessMessage() {
#endif #endif
// general // general
if (type == I_PING) { if (type == I_PING) {
debug(PSTR("node pinged by %d, hops=%d\n"), sender, _msg.getByte()); debug(PSTR("TSP:MSG:PINGED (ID=%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)); transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01));
return; // no further processing required return; // no further processing required
} }
@@ -463,7 +512,7 @@ void transportProcessMessage() {
if (_transportSM.pingActive) { if (_transportSM.pingActive) {
_transportSM.pingActive = false; _transportSM.pingActive = false;
_transportSM.pingResponse = _msg.getByte(); _transportSM.pingResponse = _msg.getByte();
debug(PSTR("ping replied, hops=%d\n"), _transportSM.pingResponse); debug(PSTR("TSP:MSG:PONG RECV (hops=%d)\n"), _transportSM.pingResponse);
} }
return; // no further processing required return; // no further processing required
} }
@@ -492,22 +541,24 @@ void transportProcessMessage() {
} }
else if (destination == BROADCAST_ADDRESS) { else if (destination == BROADCAST_ADDRESS) {
// broadcast // broadcast
debug(PSTR("MSG:BC\n")); debug(PSTR("TSP:MSG:BC\n"));
if (command == C_INTERNAL) { if (command == C_INTERNAL) {
if (isTransportOK()) { if (isTransportOK()) {
// only reply if node is fully operational // only reply if node is fully operational
if (type == I_FIND_PARENT) { if (type == I_FIND_PARENT) {
#if defined(MY_REPEATER_FEATURE) #if defined(MY_REPEATER_FEATURE)
if (sender != _nc.parentNodeId) { // no circular reference if (sender != _nc.parentNodeId) { // no circular reference
debug(PSTR("FPREQ from %d\n"), sender); // FPR: find parent request debug(PSTR("TSP:MSG:FPAR REQ (sender=%d)\n"), sender); // FPR: find parent request
// node is in our range, update routing table - important if node has new repeater as parent // node is in our range, update routing table - important if node has new repeater as parent
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender); hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender);
// check if uplink functional - node can only be parent node if link to GW functional // check if uplink functional - node can only be parent node if link to GW functional
// this also prevents circular references in case GW ooo // this also prevents circular references in case GW ooo
if (transportPingNode(GATEWAY_ADDRESS) != INVALID_HOPS) { // eventually use _nc.distance instead of INVALID_HOPS if(transportCheckUplink(false)){
// random wait to minimize collisions #if defined(MY_REPEATER_FEATURE)
debug(PSTR("GWL ok, FPRES to %d\n"), sender); // GW link ok, respond to request _transport_lastUplinkCheck = hwMillis();
delay(hwMillis() & 0x3ff); #endif
debug(PSTR("TSP:MSG:GWL OK\n")); // GW uplink ok
//delay(hwMillis() & 0x3ff);
transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance)); transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance));
} }
} }
@@ -529,7 +580,7 @@ void transportProcessMessage() {
#if defined(MY_REPEATER_FEATURE) #if defined(MY_REPEATER_FEATURE)
// controlled BC repeating: forward only if message received from parent and sender not self to prevent circular fwds // 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()){ if(last == _nc.parentNodeId && sender != _nc.nodeId && isTransportOK()){
debug(PSTR("FWD BC msg\n")); // forward BC msg debug(PSTR("TSP:MSG:FWD BC MSG\n")); // forward BC msg
transportRouteMessage(_msg); transportRouteMessage(_msg);
} }
#endif #endif
@@ -544,8 +595,8 @@ void transportProcessMessage() {
// msg not no us and not BC, relay msg // msg not no us and not BC, relay msg
#if defined(MY_REPEATER_FEATURE) #if defined(MY_REPEATER_FEATURE)
if (isTransportOK()) { if (isTransportOK()) {
debug(PSTR("REL MSG\n")); // relay msg debug(PSTR("TSP:MSG:REL MSG\n")); // relay msg
// only update routing table to uplink // update routing table if message not received from parent
if (last != _nc.parentNodeId) { if (last != _nc.parentNodeId) {
hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last); hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last);
} }
@@ -553,7 +604,7 @@ void transportProcessMessage() {
if (type == I_PING || type == I_PONG) { if (type == I_PING || type == I_PONG) {
uint8_t hopsCnt = _msg.getByte(); uint8_t hopsCnt = _msg.getByte();
if (hopsCnt != MAX_HOPS) { if (hopsCnt != MAX_HOPS) {
debug(PSTR("relay ping/pong, hops=%d\n"), hopsCnt); debug(PSTR("TSP:MSG:REL PxNG (hops=%d)\n"), hopsCnt);
_msg.set((uint8_t)(hopsCnt + 1)); _msg.set((uint8_t)(hopsCnt + 1));
} }
} }
@@ -562,14 +613,34 @@ void transportProcessMessage() {
transportRouteMessage(_msg); transportRouteMessage(_msg);
} }
#else #else
debug(PSTR("REL MSG, but not a repeater\n")); debug(PSTR("!TSM:MSG:REL MSG, but not a repeater\n"));
#endif #endif
} }
} }
inline void transportProcessFIFO() { inline void transportProcessFIFO() {
// process all msgs in FIFO if (_transportSM.transportActive) {
while (transportAvailable()) { #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE)
if (hwMillis() > (_transport_lastSanityCheck + MY_TRANSPORT_SANITY_CHECK_INTERVAL)) {
_transport_lastSanityCheck = hwMillis();
if (!transportSanityCheck()) {
debug(PSTR("!TSP:SANCHK:FAIL\n")); // sanity check fail
transportSwitchSM(stFailure);
return;
}
else {
debug(PSTR("TSP:SANCHK:OK\n")); // sanity check ok
}
}
#endif
}
else {
// transport not active, nothing to be done
return;
}
uint8_t _processedMessages = MAX_SUBSEQ_MSGS;
// process all msgs in FIFO or counter exit
while (transportAvailable() && _processedMessages--) {
transportProcessMessage(); transportProcessMessage();
} }
#if defined(MY_OTA_FIRMWARE_FEATURE) #if defined(MY_OTA_FIRMWARE_FEATURE)
@@ -587,7 +658,7 @@ bool transportSendWrite(uint8_t to, MyMessage &message) {
// sign message if required // sign message if required
if (!signerSignMsg(message)) { if (!signerSignMsg(message)) {
debug(PSTR("!sign fail\n")); debug(PSTR("!TSP:MSG:SIGN fail\n"));
setIndication(INDICATION_ERR_SIGN); setIndication(INDICATION_ERR_SIGN);
} }
@@ -598,8 +669,8 @@ bool transportSendWrite(uint8_t to, MyMessage &message) {
setIndication(INDICATION_TX); 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));
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("%sTSP:MSG: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, (ok || to == BROADCAST_ADDRESS ? "" : "!"),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 || to==BROADCAST_ADDRESS); return (ok || to==BROADCAST_ADDRESS);
+22 -9
View File
@@ -27,15 +27,17 @@
#include "MySensorsCore.h" #include "MySensorsCore.h"
#define TRANSMISSION_FAILURES 5 //!< 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 TIMEOUT_FAILURE_STATE 10000 //!< duration failure state #define TIMEOUT_FAILURE_STATE 10000 //!< duration failure state
#define STATE_TIMEOUT 2000 //!< general state timeout #define STATE_TIMEOUT 2000 //!< general state timeout
#define STATE_RETRIES 3 //!< retries before switching to FAILURE #define STATE_RETRIES 3 //!< retries before switching to FAILURE
#define AUTO 255 //!< ID 255 is reserved for auto initialization of nodeId. #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 BROADCAST_ADDRESS ((uint8_t)255) //!< broadcasts are addressed to ID 255
#define DISTANCE_INVALID ((uint8_t)255) //!< invalid distance when searching for parent #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 MAX_HOPS ((uint8_t)254) //!< maximal mumber of hops for ping/pong
#define INVALID_HOPS ((uint8_t)255) //!< invalid hops #define INVALID_HOPS ((uint8_t)255) //!< invalid hops
#define MAX_SUBSEQ_MSGS 5 //!< Maximum number of subsequentially processed messages in FIFO (to prevent transport deadlock if HW issue)
#define CHKUPL_INTERVAL ((uint32_t)5000) //!< Minimum time interval to re-check uplink
#define _autoFindParent (bool)(MY_PARENT_NODE_ID == AUTO) //!< returns true if static parent id is undefined #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 isValidDistance(distance) (bool)(distance!=DISTANCE_INVALID) //!< returns true if distance is valid
@@ -177,6 +179,12 @@ bool transportSendRoute(MyMessage &message);
* @return true if message sent successfully * @return true if message sent successfully
*/ */
bool transportSendWrite(uint8_t to, MyMessage &message); bool transportSendWrite(uint8_t to, MyMessage &message);
/**
* @brief Check uplink to GW, includes flooding control
* @param force to override flood control timer
* @return true if uplink ok
*/
bool transportCheckUplink(bool force);
// PUBLIC functions // PUBLIC functions
@@ -231,6 +239,11 @@ bool transportSend(uint8_t to, const void* data, uint8_t len);
*/ */
bool transportAvailable(); bool transportAvailable();
/** /**
* @brief Sanity check for transport: is transport still responsive?
* @return true transport ok
*/
bool transportSanityCheck();
/**
* @brief Receive message from FIFO * @brief Receive message from FIFO
* @return length of recevied message (header + payload) * @return length of recevied message (header + payload)
*/ */
@@ -75,6 +75,10 @@ bool transportAvailable() {
return avail; return avail;
} }
bool transportSanityCheck() {
return RF24_sanityCheck();
}
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data) {
uint8_t len = RF24_readMessage(data); uint8_t len = RF24_readMessage(data);
#if defined(MY_RF24_ENABLE_ENCRYPTION) #if defined(MY_RF24_ENABLE_ENCRYPTION)
@@ -57,6 +57,11 @@ bool transportAvailable() {
return _radio.receiveDone(); return _radio.receiveDone();
} }
bool transportSanityCheck() {
// not implemented yet
return true;
}
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data) {
memcpy(data,(const void *)_radio.DATA, _radio.DATALEN); memcpy(data,(const void *)_radio.DATA, _radio.DATALEN);
// Send ack back if this message wasn't a broadcast // Send ack back if this message wasn't a broadcast
@@ -337,6 +337,11 @@ bool transportAvailable() {
return _packet_received; return _packet_received;
} }
bool transportSanityCheck() {
// not implemented yet
return true;
}
uint8_t transportReceive(void* data) { uint8_t transportReceive(void* data) {
if (_packet_received) { if (_packet_received) {
memcpy(data,_data,_packet_len); memcpy(data,_data,_packet_len);
+31 -29
View File
@@ -63,22 +63,22 @@ LOCAL uint8_t RF24_spiByteTransfer(uint8_t cmd) {
LOCAL uint8_t RF24_RAW_readByteRegister(uint8_t cmd) { LOCAL uint8_t RF24_RAW_readByteRegister(uint8_t cmd) {
uint8_t value = RF24_spiMultiByteTransfer( cmd, NULL, 1, true); uint8_t value = RF24_spiMultiByteTransfer( cmd, NULL, 1, true);
RF24_DEBUG(PSTR("read register, reg=%d, value=%d\n"), cmd & (R_REGISTER ^ 0xFF), value); RF24_DEBUG(PSTR("RF24:read register, reg=%d, value=%d\n"), cmd & (R_REGISTER ^ 0xFF), value);
return value; return value;
} }
LOCAL uint8_t RF24_RAW_writeByteRegister(uint8_t cmd, uint8_t value) { LOCAL uint8_t RF24_RAW_writeByteRegister(uint8_t cmd, uint8_t value) {
RF24_DEBUG(PSTR("write register, reg=%d, value=%d\n"), cmd & (W_REGISTER ^ 0xFF), value); RF24_DEBUG(PSTR("RF24:write register, reg=%d, value=%d\n"), cmd & (W_REGISTER ^ 0xFF), value);
return RF24_spiMultiByteTransfer( cmd , &value, 1, false); return RF24_spiMultiByteTransfer( cmd , &value, 1, false);
} }
LOCAL void RF24_flushRX(void) { LOCAL void RF24_flushRX(void) {
RF24_DEBUG(PSTR("RF24_flushRX\n")); RF24_DEBUG(PSTR("RF24:flushRX\n"));
RF24_spiByteTransfer( FLUSH_RX ); RF24_spiByteTransfer( FLUSH_RX );
} }
LOCAL void RF24_flushTX(void) { LOCAL void RF24_flushTX(void) {
RF24_DEBUG(PSTR("RF24_flushTX\n")); RF24_DEBUG(PSTR("RF24:flushTX\n"));
RF24_spiByteTransfer( FLUSH_TX ); RF24_spiByteTransfer( FLUSH_TX );
} }
@@ -138,14 +138,14 @@ LOCAL void RF24_enableFeatures(void) {
} }
LOCAL void RF24_openWritingPipe(uint8_t recipient) { LOCAL void RF24_openWritingPipe(uint8_t recipient) {
RF24_DEBUG(PSTR("open writing pipe, recipient=%d\n"), recipient); RF24_DEBUG(PSTR("RF24:open writing pipe, recipient=%d\n"), recipient);
// only write LSB of RX0 and TX pipe // only write LSB of RX0 and TX pipe
RF24_setPipeLSB(RX_ADDR_P0, recipient); RF24_setPipeLSB(RX_ADDR_P0, recipient);
RF24_setPipeLSB(TX_ADDR, recipient); RF24_setPipeLSB(TX_ADDR, recipient);
} }
LOCAL void RF24_startListening(void) { LOCAL void RF24_startListening(void) {
RF24_DEBUG(PSTR("start listening\n")); RF24_DEBUG(PSTR("RF24:start listening\n"));
// toggle PRX // toggle PRX
RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) | _BV(PRIM_RX) ); RF24_setRFConfiguration(MY_RF24_CONFIGURATION | _BV(PWR_UP) | _BV(PRIM_RX) );
// all RX pipe addresses must be unique, therefore skip if node ID is 0xFF // all RX pipe addresses must be unique, therefore skip if node ID is 0xFF
@@ -155,7 +155,7 @@ LOCAL void RF24_startListening(void) {
} }
LOCAL void RF24_stopListening(void) { LOCAL void RF24_stopListening(void) {
RF24_DEBUG(PSTR("stop listening\n")); RF24_DEBUG(PSTR("RF24:stop listening\n"));
RF24_ce(LOW); RF24_ce(LOW);
// timing // timing
delayMicroseconds(130); delayMicroseconds(130);
@@ -167,7 +167,7 @@ LOCAL void RF24_stopListening(void) {
LOCAL void RF24_powerDown(void) { LOCAL void RF24_powerDown(void) {
RF24_ce(LOW); RF24_ce(LOW);
RF24_setRFConfiguration(MY_RF24_CONFIGURATION); RF24_setRFConfiguration(MY_RF24_CONFIGURATION);
RF24_DEBUG(PSTR("power down\n")); RF24_DEBUG(PSTR("RF24:power down\n"));
} }
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 ) {
@@ -175,40 +175,41 @@ LOCAL bool RF24_sendMessage( uint8_t recipient, const void* buf, uint8_t len ) {
RF24_stopListening(); RF24_stopListening();
RF24_openWritingPipe( recipient ); RF24_openWritingPipe( recipient );
RF24_DEBUG(PSTR("send message to %d, len=%d\n"),recipient,len); RF24_DEBUG(PSTR("RF24:send message to %d, len=%d\n"),recipient,len);
// flush TX FIFO // flush TX FIFO
RF24_flushTX(); RF24_flushTX();
// this command is affected in clones (e.g. Si24R1): flipped NoACK bit when using W_TX_PAYLOAD_NO_ACK / W_TX_PAYLOAD // this command is affected in clones (e.g. Si24R1): flipped NoACK bit when using W_TX_PAYLOAD_NO_ACK / W_TX_PAYLOAD
// RF24_spiMultiByteTransfer( recipient==BROADCAST_ADDRESS ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, (uint8_t*)buf, len, false ); // RF24_spiMultiByteTransfer( recipient==BROADCAST_ADDRESS ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD, (uint8_t*)buf, len, false );
// we are safe by disabling AutoACK on the broadcasting pipe // we are safe by disabling AutoACK on the broadcasting pipe
RF24_spiMultiByteTransfer( W_TX_PAYLOAD, (uint8_t*)buf, len, false ); RF24_spiMultiByteTransfer( W_TX_PAYLOAD, (uint8_t*)buf, len, false );
// go // go, TX starts after ~10us
RF24_ce(HIGH); RF24_ce(HIGH);
// TX starts after ~10us // timeout counter to detect HW issues
uint16_t timeout = 0xFFFF;
do { do {
status = RF24_getStatus(); status = RF24_getStatus();
} while (!(status & ( _BV(MAX_RT) | _BV(TX_DS) ))); } while (!(status & ( _BV(MAX_RT) | _BV(TX_DS) )) && timeout--);
// timeout value after successful TX on 16Mhz AVR ~ 65500, i.e. msg is transmitted after ~36 loop cycles
RF24_ce(LOW); RF24_ce(LOW);
// reset interrupts // reset interrupts
RF24_setStatus(_BV(TX_DS) | _BV(MAX_RT) ); RF24_setStatus(_BV(TX_DS) | _BV(MAX_RT) );
// Max retries exceeded // Max retries exceeded
if( status & _BV(MAX_RT)){ if( status & _BV(MAX_RT)){
// flush packet // flush packet
RF24_DEBUG(PSTR("MAX_RT\n")); RF24_DEBUG(PSTR("RF24:MAX_RT\n"));
RF24_flushTX(); RF24_flushTX();
} }
RF24_startListening(); RF24_startListening();
// true if message sent and not timeout
return (status & _BV(TX_DS)); return (status & _BV(TX_DS) && timeout);
} }
LOCAL uint8_t RF24_getDynamicPayloadSize(void) { LOCAL uint8_t RF24_getDynamicPayloadSize(void) {
uint8_t result = RF24_spiMultiByteTransfer(R_RX_PL_WID, NULL, 1, true); uint8_t result = RF24_spiMultiByteTransfer(R_RX_PL_WID, NULL, 1, true);
// check if payload size invalid // check if payload size invalid
if(result > 32) { if(result > 32) {
RF24_DEBUG(PSTR("invalid payload length = %d\n"),result); RF24_DEBUG(PSTR("RF24:invalid payload length = %d\n"),result);
RF24_flushRX(); RF24_flushRX();
result = 0; result = 0;
} }
@@ -217,18 +218,14 @@ LOCAL uint8_t RF24_getDynamicPayloadSize(void) {
LOCAL bool RF24_isDataAvailable() { LOCAL bool RF24_isDataAvailable() {
uint8_t pipe_num = ( RF24_getStatus() >> RX_P_NO ) & 0b0111; uint8_t status = RF24_getStatus();
#if defined(MY_DEBUG_VERBOSE_RF24) && defined(MY_DEBUG) return (((status >> RX_P_NO) & 0b0111) <= 5);
if(pipe_num <= 5)
RF24_DEBUG(PSTR("Data available on pipe %d\n"),pipe_num);
#endif
return (pipe_num <= 5);
} }
LOCAL uint8_t RF24_readMessage( void* buf) { LOCAL uint8_t RF24_readMessage( void* buf) {
uint8_t len = RF24_getDynamicPayloadSize(); uint8_t len = RF24_getDynamicPayloadSize();
RF24_DEBUG(PSTR("read message, len=%d\n"), len); RF24_DEBUG(PSTR("RF24:read message, len=%d\n"), len);
RF24_spiMultiByteTransfer( R_RX_PAYLOAD , (uint8_t*)buf, len, true ); RF24_spiMultiByteTransfer( R_RX_PAYLOAD , (uint8_t*)buf, len, true );
// clear RX interrupt // clear RX interrupt
RF24_setStatus(_BV(RX_DR) ); RF24_setStatus(_BV(RX_DR) );
@@ -249,6 +246,12 @@ LOCAL uint8_t RF24_getNodeID(void) {
return MY_RF24_NODE_ADDRESS; return MY_RF24_NODE_ADDRESS;
} }
LOCAL bool RF24_sanityCheck(void) {
// detect HW defect ot interrupted SPI line, CE disconnect cannot be detected
bool status = RF24_readByteRegister(RF_SETUP) == MY_RF24_RF_SETUP;
status &= RF24_readByteRegister(RF_CH) == MY_RF24_CHANNEL;
return status;
}
LOCAL bool RF24_initialize(void) { LOCAL bool RF24_initialize(void) {
// Initialize pins // Initialize pins
@@ -272,11 +275,10 @@ LOCAL bool RF24_initialize(void) {
RF24_setRFSetup(MY_RF24_RF_SETUP); RF24_setRFSetup(MY_RF24_RF_SETUP);
// sanity check // sanity check
#if defined(MY_RF24_SANITY_CHECK) #if defined(MY_RF24_SANITY_CHECK)
uint8_t _rf_setup = RF24_readByteRegister(RF_SETUP); if (!RF24_sanityCheck()) {
if(_rf_setup!=MY_RF24_RF_SETUP) { RF24_DEBUG(PSTR("RF24:Sanity check failed: configuration mismatch! Check wiring, replace module or non-P version\n"));
RF24_DEBUG(PSTR("Sanity check failed: RF_SETUP register=%d instead of %d, check wiring, replace module or non-P version\n"), _rf_setup, MY_RF24_RF_SETUP); return false;
return false; }
}
#endif #endif
// toggle features (necessary on some clones) // toggle features (necessary on some clones)
RF24_enableFeatures(); RF24_enableFeatures();
+1
View File
@@ -232,6 +232,7 @@ 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);
LOCAL bool RF24_sanityCheck(void);
LOCAL bool RF24_initialize(void); LOCAL bool RF24_initialize(void);
LOCAL void RF24_setChannel(uint8_t channel); LOCAL void RF24_setChannel(uint8_t channel);
LOCAL void RF24_setRetries(uint8_t retransmitDelay, uint8_t retransmitCount); LOCAL void RF24_setRetries(uint8_t retransmitDelay, uint8_t retransmitCount);
+2
View File
@@ -45,6 +45,8 @@ MY_REGISTRATION_FEATURE LITERAL1
MY_REGISTRATION_DEFAULT LITERAL1 MY_REGISTRATION_DEFAULT LITERAL1
MY_REGISTRATION_CONTROLLER LITERAL1 MY_REGISTRATION_CONTROLLER LITERAL1
MY_CORE_COMPATIBILITY_CHECK LITERAL1 MY_CORE_COMPATIBILITY_CHECK LITERAL1
MY_TRANSPORT_SANITY_CHECK LITERAL1
MY_TRANSPORT_SANITY_CHECK_INTERVAL 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_PARENT_NODE_IS_STATIC LITERAL1