// stReadyUpdate: monitors link void stReadyUpdate(void) { #if defined(MY_GATEWAY_FEATURE) if (hwMillis() - _lastNetworkDiscovery > MY_TRANSPORT_DISCOVERY_INTERVAL_MS) { _lastNetworkDiscovery = hwMillis(); TRANSPORT_DEBUG(PSTR("TSM:READY:NWD REQ\n")); // send transport network discovery (void)transportRouteMessage(build(_msgTmp, BROADCAST_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_REQUEST).set("")); } #else if (_transportSM.failedUplinkTransmissions > MY_TRANSPORT_MAX_TX_FAILURES) { // 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 = 0u; #endif } #endif #if defined(MY_RAM_ROUTING_TABLE_ENABLED) if (hwMillis() - _lastRoutingTableSave > MY_ROUTING_TABLE_SAVE_INTERVAL_MS) { _lastRoutingTableSave = hwMillis(); transportSaveRoutingTable(); } #endif }
bool signerSignMsg(MyMessage &msg) { bool ret; #if defined(MY_SIGNING_FEATURE) // If destination is known to require signed messages and we are the sender, // sign this message unless it is identified as an exception if (DO_SIGN(msg.destination) && msg.sender == getNodeId()) { if (skipSign(msg)) { ret = true; } else { // Send nonce-request _signingNonceStatus=SIGN_WAITING_FOR_NONCE; if (!_sendRoute(build(_msgSign, msg.destination, msg.sensor, C_INTERNAL, I_NONCE_REQUEST).set(""))) { SIGN_DEBUG(PSTR("Failed to transmit nonce request!\n")); ret = false; } else { SIGN_DEBUG(PSTR("Nonce requested from %d. Waiting...\n"), msg.destination); // We have to wait for the nonce to arrive before we can sign our original message // Other messages could come in-between. We trust _process() takes care of them unsigned long enter = hwMillis(); _msgSign = msg; // Copy the message to sign since message buffer might be touched in _process() while (hwMillis() - enter < MY_VERIFICATION_TIMEOUT_MS && _signingNonceStatus==SIGN_WAITING_FOR_NONCE) { _process(); } if (hwMillis() - enter > MY_VERIFICATION_TIMEOUT_MS) { SIGN_DEBUG(PSTR("Timeout waiting for nonce!\n")); ret = false; } else { if (_signingNonceStatus == SIGN_OK) { // process() received a nonce and signerProcessInternal successfully signed the message msg = _msgSign; // Write the signed message back SIGN_DEBUG(PSTR("Message to send has been signed\n")); ret = true; // After this point, only the 'last' member of the message structure is allowed to be altered if the // message has been signed, or signature will become invalid and the message rejected by the receiver } else { SIGN_DEBUG(PSTR("Message to send could not be signed!\n")); ret = false; } } } } } else if (getNodeId() == msg.sender) { mSetSigned(msg, 0); // Message is not supposed to be signed, make sure it is marked unsigned SIGN_DEBUG(PSTR("Will not sign message for destination %d as it does not require it\n"), msg.destination); ret = true; } else { SIGN_DEBUG(PSTR("Will not sign message since it was from %d and we are %d\n"), msg.sender, getNodeId()); ret = true; } #else (void)msg; ret = true; #endif // MY_SIGNING_FEATURE return ret; }
void hwDebugPrint(const char *fmt, ...) { #ifndef MY_DEBUGDEVICE #define MY_DEBUGDEVICE MY_SERIALDEVICE #endif #ifndef MY_DISABLED_SERIAL char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; #ifdef MY_GATEWAY_SERIAL // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%" PRIu8 ";0;%" PRIu8 ";%" PRIu32 " "), C_INTERNAL, I_LOG_MESSAGE, hwMillis()); MY_DEBUGDEVICE.print(fmtBuffer); #else // prepend timestamp MY_DEBUGDEVICE.print(hwMillis()); MY_DEBUGDEVICE.print(F(" ")); #endif va_list args; va_start(args, fmt); vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); #ifdef MY_GATEWAY_SERIAL // Truncate message if this is gateway node fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; #endif va_end(args); MY_DEBUGDEVICE.print(fmtBuffer); #else (void)fmt; #endif }
bool transportCheckUplink(const bool force) { if (!force && (hwMillis() - _transportSM.lastUplinkCheck) < MY_TRANSPORT_CHKUPL_INTERVAL_MS) { TRANSPORT_DEBUG(PSTR("TSF:CKU:OK,FCTRL\n")); // flood control return true; } // ping GW const uint8_t hopsCount = transportPingNode(GATEWAY_ADDRESS); // verify hops if (hopsCount != INVALID_HOPS) { // update _transportSM.lastUplinkCheck = hwMillis(); TRANSPORT_DEBUG(PSTR("TSF:CKU:OK\n")); // did distance to GW change upstream, eg. re-routing of uplink nodes if (hopsCount != _transportConfig.distanceGW) { TRANSPORT_DEBUG(PSTR("TSF:CKU:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW, hopsCount); // distance to GW changed _transportConfig.distanceGW = hopsCount; } return true; } else { TRANSPORT_DEBUG(PSTR("TSF:CKU:FAIL\n")); return false; } }
void wait(unsigned long ms) { unsigned long enter = hwMillis(); while (hwMillis() - enter < ms) { _process(); #if defined(MY_GATEWAY_ESP8266) yield(); #endif } }
void wait(unsigned long ms, uint8_t cmd, uint8_t msgtype) { unsigned long enter = hwMillis(); // invalidate msg type _msg.type = !msgtype; while ( (hwMillis() - enter < ms) && !(mGetCommand(_msg)==cmd && _msg.type==msgtype) ) { _process(); #if defined(MY_GATEWAY_ESP8266) yield(); #endif } }
bool transportWaitUntilReady(const uint32_t waitingMS) { // check if transport ready TRANSPORT_DEBUG(PSTR("TSF:WUR:MS=%lu\n"), waitingMS); // timeout uint32_t enterMS = hwMillis(); bool result = false; while (!result && ( hwMillis() - enterMS < waitingMS || !waitingMS)) { transportProcess(); result = isTransportReady(); doYield(); } return result; }
// only be used inside transport bool transportWait(const uint32_t waitingMS, const uint8_t cmd, const uint8_t msgType) { const uint32_t enterMS = hwMillis(); // invalidate msg type _msg.type = !msgType; bool expectedResponse = false; while ((hwMillis() - enterMS < waitingMS) && !expectedResponse) { // process incoming messages transportProcessFIFO(); doYield(); expectedResponse = (mGetCommand(_msg) == cmd && _msg.type == msgType); } return expectedResponse; }
void stUplinkUpdate(void) { #if !defined(MY_TRANSPORT_UPLINK_CHECK_DISABLED) if (_transportSM.pingResponse != INVALID_HOPS) { _transportSM.lastUplinkCheck = hwMillis(); // uplink ok, i.e. GW replied TRANSPORT_DEBUG(PSTR("TSM:UPL:OK\n")); // uplink ok if (_transportSM.pingResponse != _transportConfig.distanceGW) { TRANSPORT_DEBUG(PSTR("TSM:UPL:DGWC,O=%d,N=%d\n"), _transportConfig.distanceGW, _transportSM.pingResponse); // distance to GW changed _transportConfig.distanceGW = _transportSM.pingResponse; } transportSwitchSM(stReady); // proceed to next state } else if (transportTimeInState() > MY_TRANSPORT_STATE_TIMEOUT_MS) { // timeout if (_transportSM.stateRetries < MY_TRANSPORT_STATE_RETRIES) { // retries left: reenter state transportSwitchSM(stUplink); } else { // no retries left TRANSPORT_DEBUG(PSTR("!TSM:UPL:FAIL\n")); // uplink check failed _transportSM.pingActive = false; setIndication(INDICATION_ERR_CHECK_UPLINK); transportSwitchSM(stParent); // go back to stParent } } #else TRANSPORT_DEBUG(PSTR("TSM:UPL:DISABLED\n")); // uplink check disabled transportSwitchSM(stReady); #endif }
void hwDebugPrint(const char *fmt, ... ) { char fmtBuffer[MY_SERIAL_OUTPUT_SIZE]; #ifdef MY_GATEWAY_FEATURE // prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE) snprintf_P(fmtBuffer, sizeof(fmtBuffer), PSTR("0;255;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE); MY_SERIALDEVICE.print(fmtBuffer); #else // prepend timestamp (AVR nodes) MY_SERIALDEVICE.print(hwMillis()); MY_SERIALDEVICE.print(" "); #endif va_list args; va_start (args, fmt ); #ifdef MY_GATEWAY_FEATURE // Truncate message if this is gateway node vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); fmtBuffer[sizeof(fmtBuffer) - 2] = '\n'; fmtBuffer[sizeof(fmtBuffer) - 1] = '\0'; #else vsnprintf_P(fmtBuffer, sizeof(fmtBuffer), fmt, args); #endif va_end (args); MY_SERIALDEVICE.print(fmtBuffer); MY_SERIALDEVICE.flush(); //MY_SERIALDEVICE.write(freeRam()); }
inline void ledsProcess() { // Just return if it is not the time... // http://playground.arduino.cc/Code/TimingRollover if ((long)(hwMillis() - _blink_next_time) < 0) return; else _blink_next_time = hwMillis() + MY_DEFAULT_LED_BLINK_PERIOD; // do the actual blinking if(_countRx && _countRx != 255) { // switch led on hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_ON); } else if(!_countRx) { // switching off hwDigitalWrite(MY_DEFAULT_RX_LED_PIN, LED_OFF); } if(_countRx != 255) --_countRx; if(_countTx && _countTx != 255) { // switch led on hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_ON); } else if(!_countTx) { // switching off hwDigitalWrite(MY_DEFAULT_TX_LED_PIN, LED_OFF); } if(_countTx != 255) --_countTx; if(_countErr && _countErr != 255) { // switch led on hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_ON); } else if(!_countErr) { // switching off hwDigitalWrite(MY_DEFAULT_ERR_LED_PIN, LED_OFF); } if(_countErr != 255) --_countErr; }
void transportSwitchSM(transportState_t& newState) { if (_transportSM.currentState != &newState) { _transportSM.stateRetries = 0u; // state change, reset retry counter _transportSM.currentState = &newState; // change state } else { _transportSM.stateRetries++; // increment retries } if (_transportSM.currentState->Transition) { _transportSM.currentState->Transition(); // State transition } _transportSM.stateEnter = hwMillis(); // save time }
// stInit: initialise transport HW void stInitTransition(void) { TRANSPORT_DEBUG(PSTR("TSM:INIT\n")); // initialise status variables _transportSM.pingActive = false; _transportSM.transportActive = false; _transportSM.lastUplinkCheck = 0ul; #if defined(MY_TRANSPORT_SANITY_CHECK) _lastSanityCheck = hwMillis(); #endif #if defined(MY_GATEWAY_FEATURE) _lastNetworkDiscovery = 0ul; #endif #if defined(MY_RAM_ROUTING_TABLE_ENABLED) _lastRoutingTableSave = hwMillis(); #endif // Read node settings (ID, parent ID, GW distance) from EEPROM hwReadConfigBlock((void*)&_transportConfig, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(transportConfig_t)); }
void gatewayTransportRenewIP() { /* renew/rebind IP address 0 - nothing happened 1 - renew failed 2 - renew success 3 - rebinf failed 4 - rebind success */ static unsigned long next_time = hwMillis() + MY_IP_RENEWAL_INTERVAL; unsigned long now = hwMillis(); // http://playground.arduino.cc/Code/TimingRollover if ((long)(now - next_time) < 0) return; if (Ethernet.maintain() & ~(0x06)) { debug(PSTR("IP was not renewed correctly\n")); /* Error occured -> IP was not renewed */ return; } _w5100_spi_en(false); next_time = now + MY_IP_RENEWAL_INTERVAL; }
void _begin() { #if !defined(MY_DISABLED_SERIAL) hwInit(); #endif // Call before() in sketch (if it exists) if (before) before(); debug(PSTR("Starting " MY_NODE_TYPE " (" MY_CAPABILITIES ", " LIBRARY_VERSION ")\n")); signerInit(); #if defined(MY_RADIO_FEATURE) _failedTransmissions = 0; // Setup radio if (!transportInit()) { 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_INCLUSION_BUTTON_FEATURE) inclusionInit(); #endif // initialize the transport driver if (!gatewayTransportInit()) { debug(PSTR("Transport driver init fail\n")); // Nothing more we can do _infiniteLoop(); } #endif #if defined(MY_LEDS_BLINKING_FEATURE) ledsInit(); #endif // Read latest received controller configuration from EEPROM hwReadConfigBlock((void*)&_cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); if (_cc.isMetric == 0xff) { // Eeprom empty, set default to metric _cc.isMetric = 0x01; } #if defined(MY_GATEWAY_FEATURE) // Set configuration for gateway _nc.parentNodeId = GATEWAY_ADDRESS; _nc.distance = 0; _nc.nodeId = GATEWAY_ADDRESS; #elif defined(MY_RADIO_FEATURE) // Read settings from eeprom hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); #ifdef MY_OTA_FIRMWARE_FEATURE // Read firmware config from EEPROM, i.e. type, version, CRC, blocks hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); #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; } 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); } else if (_nc.nodeId == AUTO && isValidParent(_nc.parentNodeId)) { // Try to fetch node-id from gateway transportRequestNodeId(); } #endif #ifdef MY_NODE_LOCK_FEATURE // Check if node has been locked down if (hwReadConfig(EEPROM_NODE_LOCK_COUNTER) == 0) { // Node is locked, check if unlock pin is asserted, else hang the node pinMode(MY_NODE_UNLOCK_PIN, INPUT_PULLUP); // Make a short delay so we are sure any large external nets are fully pulled unsigned long enter = hwMillis(); while (hwMillis() - enter < 2); if (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); 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); } #endif // Call sketch setup if (setup) setup(); #if defined(MY_RADIO_FEATURE) transportPresentNode(); #endif if (presentation) presentation(); debug(PSTR("Init complete, id=%d, parent=%d, distance=%d\n"), _nc.nodeId, _nc.parentNodeId, _nc.distance); }
void transportProcessMessage(void) { // Manage signing timeout (void)signerCheckTimer(); // receive message setIndication(INDICATION_RX); uint8_t payloadLength = transportReceive((uint8_t *)&_msg); // get message length and limit size const uint8_t msgLength = min(mGetLength(_msg), (uint8_t)MAX_PAYLOAD); // calculate expected length const uint8_t expectedMessageLength = HEADER_SIZE + (mGetSigned(_msg) ? MAX_PAYLOAD : msgLength); #if defined(MY_RF24_ENABLE_ENCRYPTION) // payload length = a multiple of blocksize length for decrypted messages, i.e. cannot be used for payload length check payloadLength = expectedMessageLength; #endif const uint8_t command = mGetCommand(_msg); const uint8_t type = _msg.type; const uint8_t sender = _msg.sender; const uint8_t last = _msg.last; const uint8_t destination = _msg.destination; 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, command, type, mGetPayloadType(_msg), msgLength, mGetSigned(_msg), _msg.getString(_convBuf)); // Reject payloads with incorrect length if (payloadLength != expectedMessageLength) { setIndication(INDICATION_ERR_LENGTH); TRANSPORT_DEBUG(PSTR("!TSF:MSG:LEN,%d!=%d\n"), payloadLength, expectedMessageLength); // invalid payload length return; } // Reject messages with incorrect protocol version if (mGetVersion(_msg) != PROTOCOL_VERSION) { setIndication(INDICATION_ERR_VERSION); TRANSPORT_DEBUG(PSTR("!TSF:MSG:PVER,%d=%d\n"), mGetVersion(_msg), PROTOCOL_VERSION); // protocol version mismatch return; } // Reject messages that do not pass verification if (!signerVerifyMsg(_msg)) { setIndication(INDICATION_ERR_SIGN); TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN VERIFY FAIL\n")); return; } // update routing table if msg not from parent #if defined(MY_REPEATER_FEATURE) #if !defined(MY_GATEWAY_FEATURE) if (last != _transportConfig.parentNodeId) { #else // GW doesn't have parent { #endif // Message is from one of the child nodes and not sent from this node. Add it to routing table. if (sender != _transportConfig.nodeId) { transportSetRoute(sender, last); } } #endif // MY_REPEATER_FEATURE // set message received flag _transportSM.msgReceived = true; // Is message addressed to this node? if (destination == _transportConfig.nodeId) { // prevent buffer overflow by limiting max. possible message length (5 bits=31 bytes max) to MAX_PAYLOAD (25 bytes) mSetLength(_msg, min(mGetLength(_msg),(uint8_t)MAX_PAYLOAD)); // null terminate data _msg.data[msgLength] = 0u; // Check if sender requests an ack back. if (mGetRequestAck(_msg)) { TRANSPORT_DEBUG(PSTR("TSF:MSG:ACK REQ\n")); // ACK requested _msgTmp = _msg; // Copy message mSetRequestAck(_msgTmp, false); // Reply without ack flag (otherwise we would end up in an eternal loop) mSetAck(_msgTmp, true); // set ACK flag _msgTmp.sender = _transportConfig.nodeId; _msgTmp.destination = sender; // send ACK, use transportSendRoute since ACK reply is not internal, i.e. if !transportOK do not reply (void)transportSendRoute(_msgTmp); } 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 (void)transportAssignNodeID(_msg.getByte()); #endif return; // no further processing required } if (type == I_FIND_PARENT_RESPONSE) { #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_REQUEST message. Check if the distance is shorter than we already have. uint8_t distance = _msg.getByte(); if (isValidDistance(distance)) { distance++; // Distance to gateway is one more for us w.r.t. parent // update settings if distance shorter or preferred parent found if (((isValidDistance(distance) && distance < _transportConfig.distanceGW) || (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID)) && !_transportSM.preferredParentFound) { // Found a neighbor closer to GW than previously found if (!_autoFindParent && sender == (uint8_t)MY_PARENT_NODE_ID) { _transportSM.preferredParentFound = true; TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR PREF\n")); // find parent, preferred parent found } _transportConfig.distanceGW = distance; _transportConfig.parentNodeId = sender; TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR OK,ID=%d,D=%d\n"), _transportConfig.parentNodeId, _transportConfig.distanceGW); } } } else { TRANSPORT_DEBUG(PSTR("!TSF:MSG:FPAR INACTIVE\n")); // find parent response received, but inactive } return; #endif } #endif // general if (type == I_PING) { TRANSPORT_DEBUG(PSTR("TSF:MSG:PINGED,ID=%d,HP=%d\n"), sender, _msg.getByte()); // node pinged #if defined(MY_GATEWAY_FEATURE) && (F_CPU>16000000) // delay for fast GW and slow nodes delay(5); #endif (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_PONG).set((uint8_t)1)); return; // no further processing required } if (type == I_PONG) { if (_transportSM.pingActive) { _transportSM.pingActive = false; _transportSM.pingResponse = _msg.getByte(); TRANSPORT_DEBUG(PSTR("TSF:MSG:PONG RECV,HP=%d\n"), _transportSM.pingResponse); // pong received } else { TRANSPORT_DEBUG(PSTR("!TSF:MSG:PONG RECV,INACTIVE\n")); // pong received, but !pingActive } return; // no further processing required } if (_processInternalMessages()) { return; // no further processing required } } else if (command == C_STREAM) { #if defined(MY_OTA_FIRMWARE_FEATURE) if(firmwareOTAUpdateProcess()) { return; // OTA FW update processing indicated no further action needed } #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 (void)gatewayTransportSend(_msg); #endif // Call incoming message callback if available if (receive) { receive(_msg); } } else if (destination == BROADCAST_ADDRESS) { TRANSPORT_DEBUG(PSTR("TSF:MSG:BC\n")); // broadcast msg if (command == C_INTERNAL) { if (isTransportReady()) { // only reply if node is fully operational if (type == I_FIND_PARENT_REQUEST) { #if defined(MY_REPEATER_FEATURE) if (sender != _transportConfig.parentNodeId) { // no circular reference TRANSPORT_DEBUG(PSTR("TSF:MSG:FPAR REQ,ID=%d\n"), sender); // FPAR: find parent request // check if uplink functional - node can only be parent node if link to GW functional // this also prevents circular references in case GW ooo if (transportCheckUplink()) { _transportSM.lastUplinkCheck = hwMillis(); TRANSPORT_DEBUG(PSTR("TSF:MSG:GWL OK\n")); // GW uplink ok // random delay minimizes collisions delay(hwMillis() & 0x3ff); (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE).set(_transportConfig.distanceGW)); } else { TRANSPORT_DEBUG(PSTR("!TSF:MSG:GWL FAIL\n")); // GW uplink fail, do not respond to parent request } } #endif return; // no further processing required, do not forward } } // isTransportReady if (type == I_FIND_PARENT_RESPONSE) { return; // no further processing required, do not forward } #if !defined(MY_GATEWAY_FEATURE) if (type == I_DISCOVER_REQUEST) { if (last == _transportConfig.parentNodeId) { // random wait to minimize collisions delay(hwMillis() & 0x3ff); (void)transportRouteMessage(build(_msgTmp, sender, NODE_SENSOR_ID, C_INTERNAL, I_DISCOVER_RESPONSE).set(_transportConfig.parentNodeId)); // no return here (for fwd if repeater) } } #endif } // 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 == _transportConfig.parentNodeId && sender != _transportConfig.nodeId && isTransportReady()) { TRANSPORT_DEBUG(PSTR("TSF:MSG:FWD BC MSG\n")); // controlled broadcast msg forwarding (void)transportRouteMessage(_msg); } #endif // Callback for BC, only for non-internal messages if (command != C_INTERNAL) { #if !defined(MY_GATEWAY_FEATURE) // only proceed if message received from parent if (last != _transportConfig.parentNodeId) { return; } #endif #if defined(MY_GATEWAY_FEATURE) // Hand over message to controller (void)gatewayTransportSend(_msg); #endif if (receive) { receive(_msg); } } } else { // msg not to us and not BC, relay msg #if defined(MY_REPEATER_FEATURE) if (isTransportReady()) { TRANSPORT_DEBUG(PSTR("TSF:MSG:REL MSG\n")); // relay msg if (command == C_INTERNAL) { if (type == I_PING || type == I_PONG) { uint8_t hopsCnt = _msg.getByte(); if (hopsCnt != MAX_HOPS) { TRANSPORT_DEBUG(PSTR("TSF:MSG:REL PxNG,HP=%d\n"), hopsCnt); hopsCnt++; _msg.set(hopsCnt); } } } // Relay this message to another node (void)transportRouteMessage(_msg); } #else TRANSPORT_DEBUG(PSTR("!TSF:MSG:REL MSG,NREP\n")); // message relaying request, but not a repeater #endif } } void transportInvokeSanityCheck(void) { if (!transportSanityCheck()) { TRANSPORT_DEBUG(PSTR("!TSF:SAN:FAIL\n")); // sanity check fail transportSwitchSM(stFailure); } else { TRANSPORT_DEBUG(PSTR("TSF:SAN:OK\n")); // sanity check ok } } void transportProcessFIFO(void) { if (!_transportSM.transportActive) { // transport not active, no further processing required return; } #if defined(MY_TRANSPORT_SANITY_CHECK) if (hwMillis() - _lastSanityCheck > MY_TRANSPORT_SANITY_CHECK_INTERVAL_MS) { _lastSanityCheck = hwMillis(); transportInvokeSanityCheck(); } #endif uint8_t _processedMessages = MAX_SUBSEQ_MSGS; // process all msgs in FIFO or counter exit while (transportAvailable() && _processedMessages--) { transportProcessMessage(); } #if defined(MY_OTA_FIRMWARE_FEATURE) if (isTransportReady()) { // only process if transport ok firmwareOTAUpdateRequest(); } #endif } bool transportSendWrite(const uint8_t to, MyMessage &message) { message.last = _transportConfig.nodeId; // Update last // sign message if required if (!signerSignMsg(message)) { TRANSPORT_DEBUG(PSTR("!TSF:MSG:SIGN FAIL\n")); setIndication(INDICATION_ERR_SIGN); return false; } // msg length changes if signed const uint8_t totalMsgLength = HEADER_SIZE + ( mGetSigned(message) ? MAX_PAYLOAD : mGetLength( message) ); // send setIndication(INDICATION_TX); bool result = transportSend(to, &message, min((uint8_t)MAX_MESSAGE_LENGTH, totalMsgLength)); // broadcasting (workaround counterfeits) result |= (to == BROADCAST_ADDRESS); 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"), (result ? "" : "!"), message.sender, message.last, to, message.destination, message.sensor, mGetCommand(message), message.type, mGetPayloadType(message), mGetLength(message), mGetSigned(message), _transportSM.failedUplinkTransmissions, (result ? "OK" : "NACK"), message.getString(_convBuf)); return result; }
uint32_t transportTimeInState(void) { return hwMillis() - _transportSM.stateEnter; }