// 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; }
void incomingMQTT(char* topic, byte* payload, unsigned int length) { debug(PSTR("Message arrived on topic: %s\n"), topic); char *str, *p; uint8_t i = 0; for (str = strtok_r(topic, "/", &p); str && i <= 5; str = strtok_r(NULL, "/", &p)) { switch (i) { case 0: { // Topic prefix if (strcmp_P(str, MY_MQTT_TOPIC_PREFIX) != 0) { // Message not for us or malformed! return; } break; } case 1: { // Node id _mqttMsg.destination = atoi(str); break; } case 2: { // Sensor id _mqttMsg.sensor = atoi(str); break; } case 3: { // Command type mSetCommand(_mqttMsg, atoi(str)); break; } case 4: { // Ack flag mSetRequestAck(_mqttMsg, atoi(str)?1:0); break; } case 5: { // Sub type _mqttMsg.type = atoi(str); // Add payload char* ca; ca = (char *) payload; ca += length; *ca = '\0'; _mqttMsg.set((const char*) payload); _available = true; } } i++; } }
bool MySensor::send(MyMessage &message, bool enableAck) { message.sender = nc.nodeId; mSetCommand(message,C_SET); mSetRequestAck(message,enableAck); return sendRoute(message); }
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(); }