diff --git a/core/MySensorsCore.cpp b/core/MySensorsCore.cpp index bbe1566..41c6c72 100644 --- a/core/MySensorsCore.cpp +++ b/core/MySensorsCore.cpp @@ -94,7 +94,7 @@ void _begin() { // Save static parent id in eeprom (used by bootloader) hwWriteConfig(EEPROM_PARENT_NODE_ID_ADDRESS, MY_PARENT_NODE_ID); transportInitialize(); - while (!isTransportOK()) { + while (!isTransportReady()) { hwWatchdogReset(); transportProcess(); #if defined(ARDUINO_ARCH_ESP8266) diff --git a/core/MyTransport.cpp b/core/MyTransport.cpp index 4061808..eb80ad7 100644 --- a/core/MyTransport.cpp +++ b/core/MyTransport.cpp @@ -17,136 +17,161 @@ * version 2 as published by the Free Software Foundation. */ - #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 +// debug +#if defined(MY_DEBUG) + #define TRANSPORT_DEBUG(x,...) debug(x, ##__VA_ARGS__) +#else + #define TRANSPORT_DEBUG(x,...) #endif -// SM: transitions and update states -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 }; +// SM: transitions and update states +static transportState stInit = { stInitTransition, NULL }; +static transportState stParent = { stParentTransition, stParentUpdate }; +static transportState stID = { stIDTransition, stIDUpdate }; +static transportState stUplink = { stUplinkTransition, NULL }; +static transportState stReady = { stReadyTransition, stReadyUpdate }; +static transportState stFailure = { stFailureTransition, stFailureUpdate }; + +// transport SM variables static transportSM _transportSM; // stInit: initialize transport HW void stInitTransition() { - debug(PSTR("TSM:INIT\n")); + TRANSPORT_DEBUG(PSTR("TSM:INIT\n")); // initialize status variables - _transportSM.failedUplinkTransmissions = 0; _transportSM.pingActive = false; _transportSM.transportActive = false; #if defined(MY_TRANSPORT_SANITY_CHECK) || defined(MY_REPEATER_FEATURE) - _transport_lastSanityCheck = hwMillis(); + _transportSM.lastSanityCheck = hwMillis(); #endif - _transport_lastUplinkCheck = 0; + _transportSM.lastUplinkCheck = 0; // 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("!TSM:RADIO:FAIL\n")); + TRANSPORT_DEBUG(PSTR("!TSM:INIT:TSP FAIL\n")); setIndication(INDICATION_ERR_INIT_TRANSPORT); transportSwitchSM(stFailure); } else { - debug(PSTR("TSM:RADIO:OK\n")); + TRANSPORT_DEBUG(PSTR("TSM:INIT:TSP OK\n")); _transportSM.transportActive = true; #if defined(MY_GATEWAY_FEATURE) // Set configuration for gateway - debug(PSTR("TSM:GW MODE\n")); + TRANSPORT_DEBUG(PSTR("TSM:INIT:GW MODE\n")); _nc.parentNodeId = GATEWAY_ADDRESS; _nc.distance = 0; _nc.nodeId = GATEWAY_ADDRESS; transportSetAddress(GATEWAY_ADDRESS); - transportSwitchSM(stOK); + // GW mode: skip FPAR,ID,UPL states + transportSwitchSM(stReady); #else - if (MY_NODE_ID != AUTO) { + if ((uint8_t)MY_NODE_ID != AUTO) { + TRANSPORT_DEBUG(PSTR("TSM:INIT:STATID,ID=%d\n"),MY_NODE_ID); // Set static id _nc.nodeId = MY_NODE_ID; // Save static id in eeprom hwWriteConfig(EEPROM_NODE_ID_ADDRESS, MY_NODE_ID); } // set ID if static or set in EEPROM - if(_nc.nodeId!=AUTO) transportAssignNodeID(_nc.nodeId); - transportSwitchSM(stParent); + if (_nc.nodeId == AUTO || transportAssignNodeID(_nc.nodeId)) { + // if node ID > 0, proceed to next state + transportSwitchSM(stParent); + } + else { + // ID invalid (=0), nothing we can do + transportSwitchSM(stFailure); + } #endif } } // stParent: find parent void stParentTransition() { - debug(PSTR("TSM:FPAR\n")); // find parent - _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 + TRANSPORT_DEBUG(PSTR("TSM:FPAR\n")); // find parent setIndication(INDICATION_FIND_PARENT); - transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("")); + _transportSM.uplinkOk = false; + _transportSM.preferredParentFound = false; + #if defined(MY_PARENT_NODE_IS_STATIC) + TRANSPORT_DEBUG(PSTR("TSM:FPAR:STATP=%d\n"),MY_PARENT_NODE_ID); // static parent + _transportSM.findingParentNode = false; + _nc.distance = 1; // assumption, CHKUPL:GWDC will update this variable + _nc.parentNodeId = MY_PARENT_NODE_ID; + // skipping find parent + transportSwitchSM(stID); + #else + _transportSM.findingParentNode = true; + _nc.distance = DISTANCE_INVALID; // Set distance to max and invalidate parent node ID + _nc.parentNodeId = AUTO; + // Broadcast find parent request + transportRouteMessage(build(_msgTmp, _nc.nodeId, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT, false).set("")); + #endif } // stParentUpdate void stParentUpdate() { - if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) { - // timeout or preferred parent found - if (_nc.parentNodeId != AUTO) { - debug(PSTR("TSM:FPAR:OK\n")); // find parent ok - _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); + #if !defined(MY_PARENT_NODE_IS_STATIC) + if (transportTimeInState() > STATE_TIMEOUT || _transportSM.preferredParentFound) { + // timeout or preferred parent found + if (_nc.parentNodeId != AUTO) { + // parent assigned + TRANSPORT_DEBUG(PSTR("TSM:FPAR:OK\n")); // find parent ok + _transportSM.findingParentNode = false; + setIndication(INDICATION_GOT_PARENT); + // go to next state + transportSwitchSM(stID); } - else { - debug(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail - setIndication(INDICATION_ERR_FIND_PARENT); - transportSwitchSM(stFailure); + else if (transportTimeInState() > STATE_TIMEOUT) { + // timeout w/o reply or valid parent + if (_transportSM.retries < STATE_RETRIES) { + // retries left + TRANSPORT_DEBUG(PSTR("!TSM:FPAR:NO REPLY\n")); // find parent, no reply + // reenter state + transportSwitchSM(stParent); + } + else { + // no retries left, finding parent failed + TRANSPORT_DEBUG(PSTR("!TSM:FPAR:FAIL\n")); // find parent fail + setIndication(INDICATION_ERR_FIND_PARENT); + transportSwitchSM(stFailure); + } } } - } + #endif } // stID: verify and request ID if necessary void stIDTransition() { - debug(PSTR("TSM:ID\n")); + TRANSPORT_DEBUG(PSTR("TSM:ID\n")); // verify/request node ID if (_nc.nodeId == AUTO) { // send ID request setIndication(INDICATION_REQ_NODEID); + TRANSPORT_DEBUG(PSTR("TSM:ID:REQ\n")); // request node ID transportRouteMessage(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_ID_REQUEST, false).set("")); } } void stIDUpdate() { if (_nc.nodeId != AUTO) { - // current node ID is valid, proceed to uplink check - debug(PSTR("TSM:CHKID:OK (ID=%d)\n"), _nc.nodeId); + // current node ID is valid + TRANSPORT_DEBUG(PSTR("TSM:ID:OK,ID=%d\n"), _nc.nodeId); setIndication(INDICATION_GOT_NODEID); - // check uplink + // proceed to next state transportSwitchSM(stUplink); } else if (transportTimeInState() > STATE_TIMEOUT) { + // timeout if (_transportSM.retries < STATE_RETRIES) { - // re-enter if retries left + // reenter transportSwitchSM(stID); } else { - // fail - debug(PSTR("!TSM:CHKID:FAIL (ID=%d)\n"), _nc.nodeId); + // no retries left + TRANSPORT_DEBUG(PSTR("!TSM:ID:FAIL,ID=%d\n"), _nc.nodeId); setIndication(INDICATION_ERR_GET_NODEID); transportSwitchSM(stFailure); } @@ -154,56 +179,65 @@ void stIDUpdate() { } void stUplinkTransition() { - debug(PSTR("TSM:UPL\n")); + TRANSPORT_DEBUG(PSTR("TSM:UPL\n")); + // check uplink if(transportCheckUplink(true)) { - debug(PSTR("TSM:UPL:OK\n")); - transportSwitchSM(stOK); + // uplink ok, i.e. GW replied + TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok + // proceed to next state + transportSwitchSM(stReady); } else { - debug(PSTR("!TSM:UPL:FAIL\n")); + // uplink failed, at this point, no retries or timeout + TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink failed + // go back to stParent transportSwitchSM(stParent); } } -void stOKTransition() { - debug(PSTR("TSM:READY\n")); // transport is ready +void stReadyTransition() { + // transport is ready and fully operational + TRANSPORT_DEBUG(PSTR("TSM:READY\n")); // transport is ready _transportSM.uplinkOk = true; + _transportSM.failedUplinkTransmissions = 0; // reset counter } -// stOK update: monitors uplink failures -void stOKUpdate() { +// stReadyUpdate: monitors uplink failures +void stReadyUpdate() { #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 + // too many uplink transmissions failed, find new parent (if non-static) + #if !defined(MY_PARENT_NODE_IS_STATIC) + TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,SNP\n")); // uplink failed, search new parent + transportSwitchSM(stParent); + #else + TRANSPORT_DEBUG(PSTR("!TSM:READY:UPL FAIL,STATP\n")); // uplink failed, static parent + // reset counter + _transportSM.failedUplinkTransmissions = 0; + #endif } #endif } // stFailure: entered upon HW init failure or max retries exceeded void stFailureTransition() { - debug(PSTR("!TSM:FAILURE\n")); - _transportSM.uplinkOk = false; - _transportSM.transportActive = false; + TRANSPORT_DEBUG(PSTR("TSM:FAILURE\n")); + _transportSM.uplinkOk = false; // uplink nok + _transportSM.transportActive = false; // transport inactive setIndication(INDICATION_ERR_INIT_TRANSPORT); // power down transport, no need until re-init - debug(PSTR("TSM:PDT\n")); + TRANSPORT_DEBUG(PSTR("TSM:FAILURE:PDT\n")); // power down transport transportPowerDown(); } void stFailureUpdate() { if (transportTimeInState()> TIMEOUT_FAILURE_STATE) { + TRANSPORT_DEBUG(PSTR("TSM:FAILURE:RE-INIT\n")); // attempt to re-initialize transport transportSwitchSM(stInit); } } -void transportSwitchSM(State& newState) { +void transportSwitchSM(transportState& newState) { if (_transportSM.currentState != &newState) { // state change, reset retry counter _transportSM.retries = 0; @@ -227,10 +261,13 @@ void transportUpdateSM(){ if (_transportSM.currentState->Update) _transportSM.currentState->Update(); } -bool isTransportOK() { - return (_transportSM.uplinkOk); +bool isTransportReady() { + return _transportSM.uplinkOk; } +bool isTransportSearchingParent() { + return _transportSM.findingParentNode; +} void transportInitialize() { // intial state @@ -248,8 +285,8 @@ void transportProcess() { bool transportCheckUplink(bool force) { - if (!force && (hwMillis() - _transport_lastUplinkCheck) < CHKUPL_INTERVAL) { - debug(PSTR("TSP:CHKUPL:OK (FLDCTRL)\n")); // flood control + if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < CHKUPL_INTERVAL) { + TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK,FCTRL\n")); // flood control return true; } // ping GW @@ -257,35 +294,35 @@ bool transportCheckUplink(bool force) { // 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? + _transportSM.lastUplinkCheck = hwMillis(); + TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:OK\n")); + // did distance to GW change upstream, eg. 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 + TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:DGWC,O=%d,N=%d\n"), _nc.distance, hopsCount); // distance to GW changed _nc.distance = hopsCount; } return true; } else { - debug(PSTR("TSP:CHKUPL:FAIL (hops=%d)\n"), hopsCount); + TRANSPORT_DEBUG(PSTR("TSF:CHKUPL:FAIL\n")); return false; } } -void transportAssignNodeID(uint8_t newNodeId) { +bool 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("TSP:ASSIGNID:OK (ID=%d)\n"),newNodeId); + TRANSPORT_DEBUG(PSTR("TSF:ASID:OK,ID=%d\n"),newNodeId); + return true; } else { - debug(PSTR("!TSP:ASSIGNID:FAIL (ID=%d)\n"),newNodeId); + TRANSPORT_DEBUG(PSTR("!TSF:ASID:FAIL,ID=%d\n"),newNodeId); setIndication(INDICATION_ERR_NET_FULL); - // Nothing else we can do... - transportSwitchSM(stFailure); + return false; } } @@ -294,7 +331,7 @@ bool transportRouteMessage(MyMessage &message) { uint8_t route; if (_transportSM.findingParentNode && destination != BROADCAST_ADDRESS) { - debug(PSTR("!TSP:FPAR:ACTIVE (msg not send)\n")); + TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:FPAR ACTIVE\n")); // find parent active, message not sent // request to send a non-BC message while finding parent active, abort return false; } @@ -313,7 +350,7 @@ bool transportRouteMessage(MyMessage &message) { // route unknown if (message.last != _nc.parentNodeId) { // message not from parent, i.e. child node - route it to parent - debug(PSTR("!TSP:ROUTING:DEST UNKNOWN (dest=%d, STP=%d)\n"), destination, _nc.parentNodeId); + TRANSPORT_DEBUG(PSTR("!TSF:ROUTE:%d UNKNOWN\n"), destination); route = _nc.parentNodeId; } else { @@ -338,19 +375,18 @@ bool transportRouteMessage(MyMessage &message) { } #else if(!ok) setIndication(INDICATION_ERR_TX); - #endif return ok; } bool transportSendRoute(MyMessage &message) { - if (isTransportOK()) { + if (isTransportReady()) { return transportRouteMessage(message); } else { // TNR: transport not ready - debug(PSTR("!TSP:SEND:TNR\n")); + TRANSPORT_DEBUG(PSTR("!TSF:SEND:TNR\n")); return false; } } @@ -380,7 +416,7 @@ uint8_t transportPingNode(uint8_t targetId) { } _transportSM.pingActive = true; _transportSM.pingResponse = INVALID_HOPS; - debug(PSTR("TSP:PING:SEND (dest=%d)\n"), targetId); + TRANSPORT_DEBUG(PSTR("TSF:PING:SEND,TO=%d\n"), targetId); transportRouteMessage(build(_msgTmp, _nc.nodeId, targetId, NODE_SENSOR_ID, C_INTERNAL, I_PING, false).set((uint8_t)0x01)); // Wait for ping reply or timeout transportWait(2000, C_INTERNAL, I_PONG); @@ -397,7 +433,7 @@ void transportClearRoutingTable() { for (uint8_t i = 0; i != 255; i++) { hwWriteConfig(EEPROM_ROUTES_ADDRESS + i, BROADCAST_ADDRESS); } - debug(PSTR("TSP:CRT:OK\n")); // clear routing table + TRANSPORT_DEBUG(PSTR("TSF:CRT:OK\n")); // clear routing table } uint32_t transportGetHeartbeat() { @@ -408,7 +444,7 @@ void transportProcessMessage() { (void)signerCheckTimer(); // Manage signing timeout 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 setIndication(INDICATION_RX); @@ -418,26 +454,25 @@ void transportProcessMessage() { uint8_t last = _msg.last; uint8_t destination = _msg.destination; - debug(PSTR("TSP:MSG:READ %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), + TRANSPORT_DEBUG(PSTR("TSF:MSG:READ,%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s\n"), sender, last, destination, _msg.sensor, mGetCommand(_msg), type, mGetPayloadType(_msg), mGetLength(_msg), mGetSigned(_msg), _msg.getString(_convBuf)); // verify protocol version if(mGetVersion(_msg) != PROTOCOL_VERSION) { setIndication(INDICATION_ERR_VERSION); - debug(PSTR("!TSP:MSG:PVER mismatch\n")); // protocol version + TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d!=%d\n"), mGetVersion(_msg),PROTOCOL_VERSION); // protocol version mismatch return; } - // Reject massages that do not pass verification + // Reject messages that do not pass verification if (!signerVerifyMsg(_msg)) { setIndication(INDICATION_ERR_SIGN); - debug(PSTR("!TSP:MSG:SIGN verify fail\n")); + TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n")); return; } - + // Is message addressed to this node? if (destination == _nc.nodeId) { - // This message is addressed to this node // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) mSetLength(_msg, min(mGetLength(_msg),MAX_PAYLOAD)); // null terminate data @@ -459,7 +494,7 @@ void transportProcessMessage() { _msgTmp.sender = _nc.nodeId; _msgTmp.destination = sender; // send ACK - debug(PSTR("TSP:MSG:ACK msg\n")); + TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested // use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply transportSendRoute(_msgTmp); } @@ -479,32 +514,37 @@ void transportProcessMessage() { 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("TSP:MSG:FPAR RES (ID=%d, dist=%d)\n"), sender, 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("TSP:MSG:FPAR (PPAR FOUND)\n")); // preferred parent found + #if !defined(MY_GATEWAY_FEATURE) && !defined(MY_PARENT_NODE_IS_STATIC) + if (_transportSM.findingParentNode) { // only process if find parent active + // Reply to a I_FIND_PARENT message. Check if the distance is shorter than we already have. + uint8_t distance = _msg.getByte(); + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR RES,ID=%d,D=%d\n"), sender, distance); // find parent response + if (isValidDistance(distance)) { + // Distance to gateway is one more for us w.r.t. parent + distance++; + // 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; + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF FOUND\n")); // find parent, preferred parent found + } + _nc.distance = distance; + _nc.parentNodeId = sender; + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _nc.parentNodeId, _nc.distance); } - _nc.distance = distance; - _nc.parentNodeId = sender; - debug(PSTR("TSP:MSG:PAR OK (ID=%d, dist=%d)\n"), _nc.parentNodeId, _nc.distance); } } + else { + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive + } return; #endif } #endif // general if (type == I_PING) { - debug(PSTR("TSP:MSG:PINGED (ID=%d, hops=%d)\n"), sender, _msg.getByte()); + TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG, false).set((uint8_t)0x01)); return; // no further processing required } @@ -512,7 +552,7 @@ void transportProcessMessage() { if (_transportSM.pingActive) { _transportSM.pingActive = false; _transportSM.pingResponse = _msg.getByte(); - debug(PSTR("TSP:MSG:PONG RECV (hops=%d)\n"), _transportSM.pingResponse); + TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received } return; // no further processing required } @@ -528,40 +568,42 @@ void transportProcessMessage() { #endif } } + else { + TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK\n")); // received message is ACK, no internal processing, handover to msg callback + } #if defined(MY_GATEWAY_FEATURE) // Hand over message to controller gatewayTransportSend(_msg); - #else - // Call incoming message callback if available - if (receive) { - receive(_msg); - } #endif + // Call incoming message callback if available + if (receive) { + receive(_msg); + } return; } else if (destination == BROADCAST_ADDRESS) { - // broadcast - debug(PSTR("TSP:MSG:BC\n")); + TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg if (command == C_INTERNAL) { - if (isTransportOK()) { + if (isTransportReady()) { // 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("TSP:MSG:FPAR REQ (sender=%d)\n"), sender); // FPR: find parent request + TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPR: find parent request // node is in our range, update routing table - important if node has new repeater as parent hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, sender); // check if uplink functional - node can only be parent node if link to GW functional // this also prevents circular references in case GW ooo if(transportCheckUplink(false)){ - #if defined(MY_REPEATER_FEATURE) - _transport_lastUplinkCheck = hwMillis(); - #endif - debug(PSTR("TSP:MSG:GWL OK\n")); // GW uplink ok + _transportSM.lastUplinkCheck = hwMillis(); + TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok // delay minimizes collisions delay(hwMillis() & 0x3ff); transportRouteMessage(build(_msgTmp, _nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(_nc.distance)); } + else { + TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request + } } #endif @@ -580,8 +622,8 @@ void transportProcessMessage() { // 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("TSP:MSG:FWD BC MSG\n")); // forward BC msg + if(last == _nc.parentNodeId && sender != _nc.nodeId && isTransportReady()){ + TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding transportRouteMessage(_msg); } #endif @@ -593,10 +635,10 @@ void transportProcessMessage() { } else { - // msg not no us and not BC, relay msg + // msg not to us and not BC, relay msg #if defined(MY_REPEATER_FEATURE) - if (isTransportOK()) { - debug(PSTR("TSP:MSG:REL MSG\n")); // relay msg + if (isTransportReady()) { + TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg // update routing table if message not received from parent if (last != _nc.parentNodeId) { hwWriteConfig(EEPROM_ROUTES_ADDRESS + sender, last); @@ -605,7 +647,7 @@ void transportProcessMessage() { if (type == I_PING || type == I_PONG) { uint8_t hopsCnt = _msg.getByte(); if (hopsCnt != MAX_HOPS) { - debug(PSTR("TSP:MSG:REL PxNG (hops=%d)\n"), hopsCnt); + TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt); _msg.set((uint8_t)(hopsCnt + 1)); } } @@ -614,24 +656,28 @@ void transportProcessMessage() { transportRouteMessage(_msg); } #else - debug(PSTR("!TSM:MSG:REL MSG, but not a repeater\n")); + TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NORP\n")); // message relaying request, but not a repeater #endif } } +void transportInvokeSanityCheck() { + if (!transportSanityCheck()) { + TRANSPORT_DEBUG(PSTR("!TSF:SANCHK:FAIL\n")); // sanity check fail + transportSwitchSM(stFailure); + return; + } + else { + TRANSPORT_DEBUG(PSTR("TSF:SANCHK:OK\n")); // sanity check ok + } +} + inline void transportProcessFIFO() { if (_transportSM.transportActive) { #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 - } + if (hwMillis() - _transportSM.lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL) { + _transportSM.lastSanityCheck = hwMillis(); + transportInvokeSanityCheck(); } #endif } @@ -645,7 +691,7 @@ inline void transportProcessFIFO() { transportProcessMessage(); } #if defined(MY_OTA_FIRMWARE_FEATURE) - if (isTransportOK()) { + if (isTransportReady()) { // only process if transport ok firmwareOTAUpdateRequest(); } @@ -659,7 +705,7 @@ bool transportSendWrite(uint8_t to, MyMessage &message) { // sign message if required if (!signerSignMsg(message)) { - debug(PSTR("!TSP:MSG:SIGN fail\n")); + TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n")); setIndication(INDICATION_ERR_SIGN); return false; } @@ -671,10 +717,11 @@ bool transportSendWrite(uint8_t to, MyMessage &message) { setIndication(INDICATION_TX); bool ok = transportSend(to, &message, min(MAX_MESSAGE_LENGTH, HEADER_SIZE + length)); - debug(PSTR("%sTSP:MSG:SEND %d-%d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"), + TRANSPORT_DEBUG(PSTR("%sTSF:MSG:SEND,%d-%d-%d-%d,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s\n"), (ok || to == BROADCAST_ADDRESS ? "" : "!"),message.sender,message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, - mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, to==BROADCAST_ADDRESS ? "bc" : (ok ? "ok":"fail"), message.getString(_convBuf)); + mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, to==BROADCAST_ADDRESS ? "bc" : (ok ? "OK":"NACK"), message.getString(_convBuf)); return (ok || to==BROADCAST_ADDRESS); } +// EOF MyTransport.cpp \ No newline at end of file diff --git a/core/MyTransport.h b/core/MyTransport.h index c4800e9..ad038a5 100644 --- a/core/MyTransport.h +++ b/core/MyTransport.h @@ -20,8 +20,126 @@ /** * @file MyTransport.h * + * @defgroup MyTransportgrp MyTransport + * @ingroup internals + * @{ + * + * Transport-related log messages, format: [!]SYSTEM:[SUB SYSTEM:]MESSAGE + * - [!] Exclamation mark is prepended in case of error + * - SYSTEM: + * - TSM: messages emitted by the transport state machine + * - TSF: messages emitted by transport support functions + * - SUB SYSTEMS: + * - Transport state machine (TSM) + * - TSM:INIT from stInit Initialize transport and radio + * - TSM:FPAR from stParent Find parent + * - TSM:ID from stID Check/request node ID, if dynamic node ID set + * - TSM:UPL from stUplink Verify uplink connection by pinging GW + * - TSM:READY from stReady Transport is ready and fully operational + * - TSM:FAILURE from stFailure Failure in transport link or transport HW + * - Transport support function (TSF) + * - TSF:CHKUPL from @ref transportCheckUplink(..), checks connection to GW + * - TSF:ASID from @ref transportAssignNodeID(..), assigns node ID + * - TSF:PING from @ref transportPingNode(..), pings a node + * - TSF:CRT from @ref transportClearRoutingTable(..), clears routing table stored in EEPROM + * - TSF:MSG from @ref transportProcessMessage(..), processes incoming message + * - TSF:SANCHK from @ref transportInvokeSanityCheck(..), calls transport-specific sanity check + * - TSF:ROUTE from @ref transportRouteMessage(..), sends message + * - TSF:SEND from @ref transportSendRoute(..), sends message if transport is ready (exposed) + * + * TSM state stInit log status / errors + * - TSM:INIT Transition to stInit state + * - TSM:INIT:STATID,ID=x Node ID x is static + * - TSM:INIT:TSP OK Transport device configured and fully operational + * - TSM:INIT:GW MODE Node is set up as GW, thus omitting ID and findParent states + * - !TSM:INIT:TSP FAIL Transport device initialization failed + * + * TSM state stParent log status / errors + * - TSM:FPAR Transition to stParent state + * - TSM:FPAR:STATP=x Static parent x set, skip finding parent + * - TSM:FPAR:OK Parent node identified + * - !TSM:FPAR:NO REPLY No potential parents replied to find parent request, retry + * - !TSM:FPAR:FAIL Finding parent failed, go to failure state + * + * TSM state stID log status / errors + * - TSM:ID Transition to stID state + * - TSM:ID:OK,ID=x Node ID x is valid + * - TSM:ID:REQ Request node ID from controller + * - !TSM:ID:FAIL,ID=x ID verification failed, ID x invalid + * + * TSM state stUplink log status / errors + * - TSM:UPL Transition to stUplink state + * - TSM:UPL:OK Uplink OK, GW returned ping + * - !TSM:UPL:FAIL Uplink check failed, i.e. GW could not be pinged + * + * TSM state stReady log status / errors + * - TSM:READY Transition to stReady, i.e. transport is ready and fully operational + * - !TSM:READY:UPL FAIL,SNP Too many failed uplink transmissions, search new parent + * - !TSM:READY:UPL FAIL,STATP Too many failed uplink transmissions, no SNP, static parent enforced + * + * TSM state stFailure information / status + * - TSM:FAILURE Transition to stFailure state + * - TSM:FAILURE:PDT Power-down transport + * - TSM:FAILURE:RE-INIT Attempt to re-initialize transport + * + * TSF information / status + * - TSF:CHKUPL:OK Uplink OK + * - TSF:CHKUPL:OK,FCTRL Uplink OK, flood control prevents pinging GW in too short intervals + * - TSF:CHKUPL:DGWC,O=x,N=y Checking uplink revealed a changed network topology, old distance x, new distance y + * - TSF:CHKUPL:FAIL No reply received when checking uplink + * - TSF:ASID:OK,ID=x Node ID x assigned + * - TSF:PING:SEND,TO=x Send ping to destination x + * - TSF:MSG:ACK REQ ACK message requested + * - TSF:MSG:ACK ACK message, do not proceed but forward to callback + * - TSF:MSG:FPAR RES,ID=x,D=y Response to find parent received from node x with distance y to GW + * - TSF:MSG:FPAR PREF FOUND Preferred parent found + * - TSF:MSG:FPAR OK,ID=x,D=y Find parent reponse from node x is valid, distance y to GW + * - TSF:MSG:FPAR INACTIVE Find parent response received, but no find parent request active, skip response + * - TSF:MSG:FPAR REQ,ID=x Find parent request from node x + * - TSF:MSG:PINGED,ID=x,HP=y Node pinged by node x with y hops + * - TSF:MSG:PONG RECV,HP=x Pinged node replied with x hops + * - TSF:MSG:BC Broadcast message received + * - TSF:MSG:GWL OK Link to GW ok + * - TSF:MSG:FWD BC MSG Controlled broadcast message forwarding + * - TSF:MSG:REL MSG Relay message + * - TSF:MSG:REL PxNG,HP=x Relay PING/PONG message, increment hop counter x + * - TSF:SANCHK:OK Sanity check passed. This ensures radio functionality and automatically attempts to re-initialize in case of failure + * - TSF:CRT:OK Clearing routing table successful + * + * Incoming / outgoing messages + * + * See here for more detail on the format and definitons. + * + * Receiving a message + * - TSF:MSG:READ,sender-last-destination,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d:%s + * Sending a message + * - [!]TSF:MSG:SEND,sender-last-next-destination,s=%d,c=%d,t=%d,pt=%d,l=%d,sg=%d,ft=%d,st=%s:%s + * + * - Message fields: + * - s=sensor ID + * - c=command + * - t=msg type + * - pt=payload type + * - l=length + * - ft=failed uplink transmission counter + * - sg=signing flag + * + * TSF errors + * - !TSF:ASID:FAIL,ID=x Assigned ID x is invalid, e.g. 0 (GATEWAY) + * - !TSF:ROUTE:FPAR ACTIVE Finding parent active, message not sent + * - !TSF:ROUTE:DST x UNKNOWN Routing for destination x unkown, send message to parent + * - !TSF:SEND:TNR Transport not ready, message cannot be sent + * - !TSF:MSG:PVER,x!=y Message protocol version mismatch + * - !TSF:MSG:SIGN VERIFY FAIL Signing verficiation failed + * - !TSF:MSG:REL MSG,NORP Node received a message for relaying, but node is not a repeater, message skipped + * - !TSF:MSG:SIGN FAIL Signing message failed + * - !TSF:MSG:GWL FAIL GW uplink failed + * - !TSF:SANCHK:FAIL Sanity check failed, attempt to re-intialize radio + * * @brief API declaration for MyTransport + * */ + #ifndef MyTransport_h #define MyTransport_h @@ -52,7 +170,7 @@ * * This structure stores SM state definitions */ -struct State { +struct transportState { void(*Transition)(); //!< state transition function void(*Update)(); //!< state update function }; @@ -63,16 +181,21 @@ struct State { * This structure stores transport status and SM variables */ typedef struct { - State* currentState; //!< pointer to current fsm state + transportState* currentState; //!< pointer to current fsm state uint32_t stateEnter; //!< state enter timepoint + uint32_t lastUplinkCheck; //!< last uplink check, required to prevent GW flooding + uint32_t lastSanityCheck; //!< last sanity check + // 8 bits bool findingParentNode : 1; //!< flag finding parent node is active bool preferredParentFound : 1; //!< flag preferred parent found bool uplinkOk : 1; //!< flag uplink ok bool pingActive : 1; //!< flag ping active bool transportActive : 1; //!< flag transport active uint8_t reserved : 3; //!< reserved + // 8 bits uint8_t retries : 4; //!< retries / state re-enter uint8_t failedUplinkTransmissions : 4; //!< counter failed uplink transmissions + // 8 bits uint8_t pingResponse; //!< stores hops received in I_PONG } __attribute__((packed)) transportSM; @@ -106,11 +229,11 @@ void stUplinkTransition(); /** * @brief Set transport OK */ -void stOKTransition(); +void stReadyTransition(); /** * @brief Monitor transport link */ -void stOKUpdate(); +void stReadyUpdate(); /** * @brief Transport failure and power down radio */ @@ -123,7 +246,7 @@ void stFailureUpdate(); * @brief Switch SM state * @param newState New state to switch SM to */ -void transportSwitchSM(State& newState); +void transportSwitchSM(transportState& newState); /** * @brief Update SM state */ @@ -133,7 +256,10 @@ void transportUpdateSM(); * @return ms in current state */ uint32_t transportTimeInState(); - +/** +* @brief Call transport driver sanity check +*/ +void transportInvokeSanityCheck(); /** * @brief Process all pending messages in RX FIFO */ @@ -145,8 +271,9 @@ void transportProcessMessage(); /** * @brief Assign node ID * @param newNodeId New node ID +* @return true if node ID valid and successfully assigned */ -void transportAssignNodeID(uint8_t newNodeId); +bool 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 @@ -164,7 +291,7 @@ 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 +* This function is used in MyTransport and omits the transport state check, i.e. message can be sent even if transport is not ready * * @param message * @return true if message sent successfully @@ -201,10 +328,15 @@ void transportInitialize(); */ void transportProcess(); /** -* @brief Verify transport status +* @brief Flag transport ready * @return true if transport is initialize and ready */ -bool isTransportOK(); +bool isTransportReady(); +/** +* @brief Flag searching parent ongoing +* @return true if transport is searching for parent +*/ +bool isTransportSearchingParent(); /** * @brief Clear routing table */