// Inline function and macros inline MyMessage& build (MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck) { msg.sender = sender; msg.destination = destination; msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); mSetRequestAck(msg,enableAck); mSetAck(msg,false); return msg; }
boolean MySensor::process() { uint8_t pipe; boolean available = RF24::available(&pipe); if (!available || pipe>6) return false; uint8_t len = RF24::getDynamicPayloadSize(); RF24::read(&msg, len); RF24::writeAckPayload(pipe,&pipe, 1 ); // Add string termination, good if we later would want to print it. msg.data[mGetLength(msg)] = '\0'; debug(PSTR("read: %d-%d-%d s=%d,c=%d,t=%d,pt=%d,l=%d:%s\n"), msg.sender, msg.last, msg.destination, msg.sensor, mGetCommand(msg), msg.type, mGetPayloadType(msg), mGetLength(msg), msg.getString(convBuf)); if(!(mGetVersion(msg) == PROTOCOL_VERSION)) { debug(PSTR("version mismatch\n")); return false; } uint8_t command = mGetCommand(msg); uint8_t type = msg.type; uint8_t sender = msg.sender; uint8_t last = msg.last; uint8_t destination = msg.destination; if (repeaterMode && command == C_INTERNAL && type == I_FIND_PARENT) { // Relaying nodes should always answer ping messages // Wait a random delay of 0-2 seconds to minimize collision // between ping ack messages from other relaying nodes delay(millis() & 0x3ff); sendWrite(sender, build(msg, nc.nodeId, sender, NODE_SENSOR_ID, C_INTERNAL, I_FIND_PARENT_RESPONSE, false).set(nc.distance), true); return false; } else if (destination == nc.nodeId) { // Check if sender requests an ack back. if (mGetRequestAck(msg)) { // Copy message ack = msg; mSetRequestAck(ack,false); // Reply without ack flag (otherwise we would end up in an eternal loop) mSetAck(ack,true); ack.sender = nc.nodeId; ack.destination = msg.sender; sendRoute(ack); } // This message is addressed to this node if (repeaterMode && last != nc.parentNodeId) { // Message is from one of the child nodes. Add it to routing table. addChildRoute(sender, last); } if (command == C_INTERNAL) { if (type == I_FIND_PARENT_RESPONSE && !isGateway) { // We've received a reply to a FIND_PARENT message. Check if the distance is // shorter than we already have. uint8_t distance = msg.getByte(); if (distance<nc.distance-1) { // Found a neighbor closer to GW than previously found nc.distance = distance + 1; nc.parentNodeId = msg.sender; eeprom_write_byte((uint8_t*)EEPROM_PARENT_NODE_ID_ADDRESS, nc.parentNodeId); eeprom_write_byte((uint8_t*)EEPROM_DISTANCE_ADDRESS, nc.distance); debug(PSTR("new parent=%d, d=%d\n"), nc.parentNodeId, nc.distance); } return false; } else if (sender == GATEWAY_ADDRESS) { bool isMetric; if (type == I_REBOOT) { wdt_enable(WDTO_15MS); for (;;); } else if (type == I_ID_RESPONSE) { if (nc.nodeId == AUTO) { nc.nodeId = msg.getByte(); // Write id to EEPROM if (nc.nodeId == AUTO) { // sensor net gateway will return max id if all sensor id are taken debug(PSTR("full\n")); while (1); // Wait here. Nothing else we can do... } else { RF24::openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(nc.nodeId)); eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, nc.nodeId); } debug(PSTR("id=%d\n"), nc.nodeId); } } else if (type == I_CONFIG) { // Pick up configuration from controller (currently only metric/imperial) // and store it in eeprom if changed isMetric = msg.getByte() == 'M' ; if (cc.isMetric != isMetric) { cc.isMetric = isMetric; eeprom_write_byte((uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, isMetric); //eeprom_write_block((const void*)&cc, (uint8_t*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); } } else if (type == I_CHILDREN) { if (repeaterMode && msg.getByte() == 'C') { // Clears child relay data for this node debug(PSTR("rd=clear\n")); for (uint8_t i=0;i< sizeof(childNodeTable); i++) { removeChildRoute(i); } sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_INTERNAL, I_CHILDREN,false).set("")); } } else if (type == I_TIME) { if (timeCallback != NULL) { // Deliver time to callback timeCallback(msg.getULong()); } } return false; } } // Call incoming message callback if available if (msgCallback != NULL) { msgCallback(msg); } // Return true if message was addressed for this node... return true; } else if (repeaterMode && pipe == CURRENT_NODE_PIPE) { // We should try to relay this message to another node uint8_t route = getChildRoute(msg.destination); if (route>0 && route<255) { // This message should be forwarded to a child node. If we send message // to this nodes pipe then all children will receive it because the are // all listening to this nodes pipe. // // +----B // -A // +----C------D // // We're node C, Message comes from A and has destination D // // lookup route in table and send message there sendWrite(route, msg); } else { // A message comes from a child node and we have no // route for it. // // +----B // -A // +----C------D <-- Message comes from D // // We're node C // // Message should be passed to node A (this nodes relay) // This message should be routed back towards sensor net gateway sendWrite(nc.parentNodeId, msg); // Add this child to our "routing table" if it not already exist addChildRoute(sender, last); } } return false; }
bool MyParserSerial::parse(MyMessage &message, char *inputString) { char *str, *p, *value=NULL; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; int i = 0; uint8_t command = 0; uint8_t ack = 0; // Extract command data coming on serial line for (str = strtok_r(inputString, ";", &p); // split using semicolon str && i < 6; // loop while str is not null an max 5 times str = strtok_r(NULL, ";", &p) // get subsequent tokens ) { switch (i) { case 0: // Radioid (destination) message.destination = atoi(str); break; case 1: // Childid message.sensor = atoi(str); break; case 2: // Message type command = atoi(str); mSetCommand(message, command); break; case 3: // Should we request ack from destination? ack = atoi(str); break; case 4: // Data type message.type = atoi(str); break; case 5: // Variable value if (command == C_STREAM) { blen = 0; uint8_t val; while (*str) { val = h2i(*str++) << 4; val += h2i(*str++); bvalue[blen] = val; blen++; } } else { value = str; // Remove ending carriage return character (if it exists) uint8_t lastCharacter = strlen(value)-1; if (value[lastCharacter] == '\r') value[lastCharacter] = 0; } break; } i++; } // Check for invalid input if (i < 5) return false; message.sender = GATEWAY_ADDRESS; message.last = GATEWAY_ADDRESS; mSetRequestAck(message, ack?1:0); mSetAck(message, false); if (command == C_STREAM) message.set(bvalue, blen); else message.set(value); return true; }
void MyGateway::parseAndSend(char *commandBuffer) { boolean ok = false; char *str, *p, *value=NULL; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; int i = 0; uint16_t destination = 0; uint8_t sensor = 0; uint8_t command = 0; uint8_t type = 0; uint8_t ack = 0; // Extract command data coming on serial line for (str = strtok_r(commandBuffer, ";", &p); // split using semicolon str && i < 6; // loop while str is not null an max 5 times str = strtok_r(NULL, ";", &p) // get subsequent tokens ) { switch (i) { case 0: // Radioid (destination) destination = atoi(str); break; case 1: // Childid sensor = atoi(str); break; case 2: // Message type command = atoi(str); break; case 3: // Should we request ack from destination? ack = atoi(str); break; case 4: // Data type type = atoi(str); break; case 5: // Variable value if (command == C_STREAM) { blen = 0; uint8_t val; while (*str) { val = h2i(*str++) << 4; val += h2i(*str++); bvalue[blen] = val; blen++; } } else { value = str; } break; } i++; } if (destination==GATEWAY_ADDRESS && command==C_INTERNAL) { // Handle messages directed to gateway if (type == I_VERSION) { // Request for version serial(PSTR("0;0;%d;0;%d;%s\n"),C_INTERNAL, I_VERSION, LIBRARY_VERSION); } else if (type == I_INCLUSION_MODE) { // Request to change inclusion mode setInclusionMode(atoi(value) == 1); } } else { txBlink(1); msg.sender = GATEWAY_ADDRESS; msg.destination = destination; msg.sensor = sensor; msg.type = type; mSetCommand(msg,command); mSetRequestAck(msg,ack?1:0); mSetAck(msg,false); msg.set(value); if (command == C_STREAM) msg.set(bvalue, blen); else msg.set(value); ok = sendRoute(msg); if (!ok) { errBlink(1); } } }
bool protocolMQTTParse(MyMessage &message, char* topic, uint8_t* payload, unsigned int length) { char *str, *p; uint8_t i = 0; uint8_t bvalue[MAX_PAYLOAD]; uint8_t blen = 0; uint8_t command = 0; if (topic != strstr(topic, MY_MQTT_SUBSCRIBE_TOPIC_PREFIX)) { // Prefix doesn't match incoming topic return false; } for (str = strtok_r(topic + strlen(MY_MQTT_SUBSCRIBE_TOPIC_PREFIX) + 1, "/", &p); str && i <= 5; str = strtok_r(NULL, "/", &p)) { switch (i) { case 0: { // Node id message.destination = atoi(str); break; } case 1: { // Sensor id message.sensor = atoi(str); break; } case 2: { // Command type command = atoi(str); mSetCommand(message, command); break; } case 3: { // Ack flag mSetRequestAck(message, atoi(str)?1:0); break; } case 4: { // Sub type message.type = atoi(str); break; } } i++; } if (i != 5) { return false; } message.sender = GATEWAY_ADDRESS; message.last = GATEWAY_ADDRESS; mSetAck(message, false); // Add payload if (command == C_STREAM) { blen = 0; uint8_t val; while (*payload) { val = protocolH2i(*payload++) << 4; val += protocolH2i(*payload++); bvalue[blen] = val; blen++; } message.set(bvalue, blen); } else { char* ca; ca = (char *) payload; ca += length; *ca = '\0'; message.set((const char*) payload); } return true; }
// main start int main(void) { asm volatile ("clr __zero_reg__"); // reset MCU status register MCUSR = 0; // enable watchdog to avoid deadlock watchdogConfig(WATCHDOG_8S); // initialize SPI SPIinit(); // initialize RF module RFinit(); // Read node config from EEPROM, i.e. nodeId, parent nodeId, distance eeprom_read_block((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(struct NodeConfig)); // Read firmware config from EEPROM, i.e. type, version, CRC, blocks eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); // find nearest node during reboot: invalidate parent node settings, since we have to re-discover them for every single reboot configuredParentID = nc.parentNodeId; // nc.parentNodeId = 0xFF; nc.distance = 0xFF; // prepare for I_FIND_PARENTS outMsg.sender = nc.nodeId; outMsg.last = nc.nodeId; outMsg.sensor = 0xFF; outMsg.destination = BROADCAST_ADDRESS; // set header mSetVersion(outMsg, PROTOCOL_VERSION); mSetLength(outMsg, 0); mSetCommand(outMsg, C_INTERNAL); mSetAck(outMsg,false); mSetPayloadType(outMsg, P_STRING); // set reading & writing pipe address setAddress(nc.nodeId); // network up? get neighbors, else startup if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) { startup(); } // all messages to gateway outMsg.destination = GATEWAY_ADDRESS; // if no node id assigned, request new id if (nc.nodeId == AUTO) { // listen to broadcast openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS)); if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) { // save id to eeprom eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data)); } // we could go on and set everything right here, but rebooting will take care of that - and saves some bytes :) reboot(); } // wuff watchdogReset(); // prepare for FW config request RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)outMsg.data; mSetLength(outMsg, sizeof(RequestFirmwareConfig)); mSetCommand(outMsg, C_STREAM); mSetPayloadType(outMsg,P_CUSTOM); // copy node settings to reqFWConfig memcpy(reqFWConfig,&fc,sizeof(NodeFirmwareConfig)); // add bootloader information reqFWConfig->BLVersion = MYSBOOTLOADER_VERSION; // send node config and request FW config from controller if (!sendAndWait(ST_FIRMWARE_CONFIG_REQUEST, ST_FIRMWARE_CONFIG_RESPONSE)) { startup(); } NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)inMsg.data; // bootloader commands if (firmwareConfigResponse->blocks == 0) { // verify flag if (firmwareConfigResponse->crc == 0xDA7A){ // cmd 0x01 clear eeprom if(firmwareConfigResponse->bl_command == 0x01) { for(uint16_t i = 0; i < EEPROM_SIZE; i++) eeprom_update_byte((uint8_t *)i,0xFF); } else // cmd 0x02 set id if(firmwareConfigResponse->bl_command == 0x02) { eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, (uint8_t)firmwareConfigResponse->bl_data); } } // final step reboot(); } // compare with current node configuration, if equal startup if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { startup(); } // *********** from here on we will fetch new FW // invalidate current CRC fc.crc = 0xFFFF; // write fetched type and version in case OTA fails (BL will reboot and re-request FW with stored settings) eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig)); // copy new FW config memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); RequestFWBlock *firmwareRequest = (RequestFWBlock *)outMsg.data; mSetLength(outMsg, sizeof(RequestFWBlock)); firmwareRequest->type = fc.type; firmwareRequest->version = fc.version; // request FW from controller, load FW counting backwards uint16_t block = fc.blocks; do { firmwareRequest->block = block - 1; // request FW block if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) { reboot(); } ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)inMsg.data; // did we receive requested block? if (!memcmp(firmwareRequest,firmwareResponse,sizeof(RequestFWBlock))) { // calculate page offset uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE; // write to buffer memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); // program if page full if (offset == 0) { programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf); } block--; } } while (block); // wuff watchdogReset(); // all blocks transmitted, calc CRC and write to eeprom if valid if (IsFirmwareValid()) { // if FW is valid, write settings to eeprom eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); } // final step reboot(); }
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; }