From 3a59929b8ed8e1fca9876031264db5d6626876ff Mon Sep 17 00:00:00 2001 From: Henrik Ekblad Date: Mon, 30 Mar 2015 10:28:57 +0200 Subject: [PATCH] Initial untested OTA feature --- libraries/MySensors/MyConfig.h | 10 +- libraries/MySensors/MyHwATMega328.h | 1 - libraries/MySensors/MySensor.cpp | 137 +++++++++++++++++++++++----- libraries/MySensors/MySensor.h | 21 ++++- 4 files changed, 136 insertions(+), 33 deletions(-) diff --git a/libraries/MySensors/MyConfig.h b/libraries/MySensors/MyConfig.h index 4e5b579..4b7b4f1 100644 --- a/libraries/MySensors/MyConfig.h +++ b/libraries/MySensors/MyConfig.h @@ -21,14 +21,8 @@ #define MY_OTA_FIRMWARE_FEATURE // Slave select pin for external flash #define MY_OTA_FLASH_SS 8 -// Number of times to request a fw block before giving up -#define MY_OTA_RETRY 5 -// Number of millisecons before re-request a fw block -#define MY_OTA_RETRY_DELAY 500 -// Bootloader version -#define MY_OTA_BOOTLOADER_MAJOR_VERSION 2 -#define MY_OTA_BOOTLOADER_MINOR_VERSION 0 -#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) +// Flash jdecid +#define MY_OTA_FLASH_JDECID 0x1F65 /********************************** diff --git a/libraries/MySensors/MyHwATMega328.h b/libraries/MySensors/MyHwATMega328.h index b45a5b9..8610f1b 100644 --- a/libraries/MySensors/MyHwATMega328.h +++ b/libraries/MySensors/MyHwATMega328.h @@ -10,7 +10,6 @@ #include #include #include -#include diff --git a/libraries/MySensors/MySensor.cpp b/libraries/MySensors/MySensor.cpp index 4be0330..c05df58 100755 --- a/libraries/MySensors/MySensor.cpp +++ b/libraries/MySensors/MySensor.cpp @@ -49,13 +49,16 @@ MySensor::MySensor(MyTransport &_radio, MyHw &_hw radio(_radio), #ifdef MY_SIGNING_FEATURE signer(_signer), +#endif +#ifdef MY_OTA_FIRMWARE_FEATURE + flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID), #endif hw(_hw) { } -#ifdef MY_OTA_FRIMWARE_FEATURE +#ifdef MY_OTA_FIRMWARE_FEATURE // do a crc16 on the whole received firmware bool MySensor::isValidFirmware() { void* ptr = 0; @@ -63,7 +66,7 @@ bool MySensor::isValidFirmware() { int j; for (uint16_t i = 0; i < fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) { - uint8_t a = pgm_read_byte((uint16_t) ptr + i); + uint8_t a = flash.readByte((uint16_t) ptr + i + FIRMWARE_START_OFFSET); crc ^= a; for (j = 0; j < 8; ++j) { @@ -95,7 +98,7 @@ void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, b hw_readConfigBlock((void*)&nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); // Read latest received controller configuration from EEPROM hw_readConfigBlock((void*)&cc, (void*)EEPROM_CONTROLLER_CONFIG_ADDRESS, sizeof(ControllerConfig)); -#ifdef MY_OTA_FRIMWARE_FEATURE +#ifdef MY_OTA_FIRMWARE_FEATURE // Read firmware config from EEPROM, i.e. type, version, CRC, blocks hw_readConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); #endif @@ -189,7 +192,7 @@ void MySensor::setupNode() { // Wait configuration reply. wait(2000); -#ifdef MY_OTA_FRIMWARE_FEATURE +#ifdef MY_OTA_FIRMWARE_FEATURE RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)msg.data; mSetLength(msg, sizeof(RequestFirmwareConfig)); mSetCommand(msg, C_STREAM); @@ -197,21 +200,9 @@ void MySensor::setupNode() { // 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; - - // compare with current node configuration, if equal startup - if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { - startup(); - } - - + reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; + fwUpdateOngoing = false; + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); #endif @@ -408,7 +399,29 @@ boolean MySensor::process() { hw_watchdogReset(); uint8_t to = 0; if (!radio.available(&to)) + { +#ifdef MY_OTA_FIRMWARE_FEATURE + unsigned long enter = hw_millis(); + if (fwUpdateOngoing && (enter - fwLastRequestTime > MY_OTA_RETRY_DELAY)) { + if (fwRetry == 0) { + debug(PSTR("fw upd fail\n")); + // Give up. We have requested MY_OTA_RETRY times without any packet in return. + fwUpdateOngoing = false; + return false; + } + fwRetry--; + fwLastRequestTime = enter; + // Time to (re-)request firmware block from controller + RequestFWBlock *firmwareRequest = (RequestFWBlock *)msg.data; + mSetLength(msg, sizeof(RequestFWBlock)); + firmwareRequest->type = fc.type; + firmwareRequest->version = fc.version; + firmwareRequest->block = (fwBlock - 1); + sendRoute(build(msg, nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false)); + } +#endif return false; + } #ifdef MY_SIGNING_FEATURE (void)signer.checkTimer(); // Manage signing timeout @@ -567,6 +580,55 @@ boolean MySensor::process() { return false; } } +#ifdef MY_OTA_FIRMWARE_FEATURE + else if (command == C_STREAM) { + if (type == ST_FIRMWARE_CONFIG_RESPONSE) { + NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)msg.data; + // compare with current node configuration, if they differ, start fw fetch process + if (memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) { + debug(PSTR("fw update\n")); + fwUpdateOngoing = true; + fwBlock = fc.blocks; + // copy new FW config + memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); + // Init flash + if (!flash.initialize()) { + debug(PSTR("flash init fail\n")); + } + + } + } else if (type == ST_FIRMWARE_RESPONSE) { + // Save block to eeprom + debug(PSTR("fw block %d\n"), fwBlock); + + ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)msg.data; + // write to flash + flash.writeBytes(((fwBlock-1)*FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); + fwBlock--; + if (fwBlock == 0) { + // We're finished! Do a checksum and reboot. + if (isValidFirmware()) { + debug(PSTR("fw checksum ok\n")); + // All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it) + uint16_t fwsize = FIRMWARE_BLOCK_SIZE*fc.blocks; + flash.writeBytes(0, "FLXIMG:", 7); + flash.writeByte(7, fwsize >> 8); + flash.writeByte(8, fwsize); + flash.writeByte(9, ':'); + // Write the new firmware config to eeprom + hw_writeConfigBlock((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + hw_reboot(); + } else { + debug(PSTR("fw checksum fail\n")); + } + } + } + // Make sure packet request occurs next time process() is called by timing out requestTime. + fwRetry = MY_OTA_RETRY+1; + fwLastRequestTime = 0; + return false; + } +#endif // Call incoming message callback if available if (msgCallback != NULL) { msgCallback(msg); @@ -613,17 +675,46 @@ void MySensor::wait(unsigned long ms) { } void MySensor::sleep(unsigned long ms) { - radio.powerDown(); - hw.sleep(ms); +#ifdef MY_OTA_FIRMWARE_FEATURE + if (fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + } else { +#endif + radio.powerDown(); + hw.sleep(ms); +#ifdef MY_OTA_FIRMWARE_FEATURE + } +#endif } bool MySensor::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) { +#ifdef MY_OTA_FIRMWARE_FEATURE + if (fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + return false; + } else { +#endif radio.powerDown(); return hw.sleep(interrupt, mode, ms) ; +#ifdef MY_OTA_FIRMWARE_FEATURE + } +#endif } int8_t MySensor::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) { - radio.powerDown(); - return hw.sleep(interrupt1, mode1, interrupt2, mode2, ms) ; +#ifdef MY_OTA_FIRMWARE_FEATURE + if (fwUpdateOngoing) { + // Do not sleep node while fw update is ongoing + process(); + return -1; + } else { +#endif + radio.powerDown(); + return hw.sleep(interrupt1, mode1, interrupt2, mode2, ms) ; +#ifdef MY_OTA_FIRMWARE_FEATURE + } +#endif } diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 40e5cd4..7873c22 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -23,6 +23,9 @@ #include "MySigningNone.h" #endif #include "MyMessage.h" +#ifdef MY_OTA_FIRMWARE_FEATURE +#include "utility/SPIFlash.h" +#endif #include #include @@ -78,6 +81,17 @@ struct ControllerConfig { #define FIRMWARE_BLOCK_SIZE 16 // Number of times a firmware block should be requested before giving up #define FIRMWARE_MAX_REQUESTS 5 +// Number of times to request a fw block before giving up +#define MY_OTA_RETRY 5 +// Number of millisecons before re-request a fw block +#define MY_OTA_RETRY_DELAY 500 +// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first) +#define FIRMWARE_START_OFFSET 10 +// Bootloader version +#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3 +#define MY_OTA_BOOTLOADER_MINOR_VERSION 0 +#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) + // FW config structure, stored in eeprom typedef struct { @@ -282,6 +296,11 @@ class MySensor ControllerConfig cc; // Configuration coming from controller #ifdef MY_OTA_FIRMWARE_FEATURE NodeFirmwareConfig fc; + bool fwUpdateOngoing; + unsigned long fwLastRequestTime; + uint16_t fwBlock; + uint8_t fwRetry; + SPIFlash flash; #endif bool repeaterMode; @@ -309,7 +328,7 @@ class MySensor void (*timeCallback)(unsigned long); // Callback for requested time messages void (*msgCallback)(const MyMessage &); // Callback for incoming messages from other nodes and gateway. -#ifdef MY_OTA_FRIMWARE_FEATURE +#ifdef MY_OTA_FIRMWARE_FEATURE // do a crc16 on the whole received firmware bool isValidFirmware(); #endif