From 3b94f839501c86f55dc098183cb9b96b78d28943 Mon Sep 17 00:00:00 2001 From: tekka007 Date: Tue, 31 May 2016 22:49:08 +0200 Subject: [PATCH] Refactor OTA update code --- libraries/MySensors/MySensor.h | 1 + .../MySensors/core/MyOTAFirmwareUpdate.cpp | 145 ++++++++++++++++++ .../MySensors/core/MyOTAFirmwareUpdate.h | 101 ++++++++++++ libraries/MySensors/core/MySensorCore.cpp | 4 +- libraries/MySensors/core/MySensorCore.h | 4 - libraries/MySensors/core/MyTransport.cpp | 135 ++-------------- libraries/MySensors/core/MyTransport.h | 50 ------ 7 files changed, 258 insertions(+), 182 deletions(-) create mode 100644 libraries/MySensors/core/MyOTAFirmwareUpdate.cpp create mode 100644 libraries/MySensors/core/MyOTAFirmwareUpdate.h diff --git a/libraries/MySensors/MySensor.h b/libraries/MySensors/MySensor.h index 4ff121eaa..29bce201a 100644 --- a/libraries/MySensors/MySensor.h +++ b/libraries/MySensors/MySensor.h @@ -246,6 +246,7 @@ // FLASH #ifdef MY_OTA_FIRMWARE_FEATURE #include "drivers/SPIFlash/SPIFlash.cpp" + #include "core/MyOTAFirmwareUpdate.cpp" #endif #include "core/MyTransport.cpp" #if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485)) diff --git a/libraries/MySensors/core/MyOTAFirmwareUpdate.cpp b/libraries/MySensors/core/MyOTAFirmwareUpdate.cpp new file mode 100644 index 000000000..8c4d72ec9 --- /dev/null +++ b/libraries/MySensors/core/MyOTAFirmwareUpdate.cpp @@ -0,0 +1,145 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2016 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include "MyOTAFirmwareUpdate.h" + +SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); +NodeFirmwareConfig _fc; +bool _fwUpdateOngoing; +unsigned long _fwLastRequestTime; +uint16_t _fwBlock; +uint8_t _fwRetry; + +inline void readFirmwareSettings() { + hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); +} + +inline void firmwareOTAUpdateRequest() { + unsigned long enter = hwMillis(); + if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) { + if (!_fwRetry) { + debug(PSTR("fw upd fail\n")); + // Give up. We have requested MY_OTA_RETRY times without any packet in return. + _fwUpdateOngoing = false; + ledBlinkErr(1); + return; + } + _fwRetry--; + _fwLastRequestTime = enter; + // Time to (re-)request firmware block from controller + RequestFWBlock firmwareRequest; + firmwareRequest.type = _fc.type; + firmwareRequest.version = _fc.version; + firmwareRequest.block = (_fwBlock - 1); + debug(PSTR("req FW: T=%02X, V=%02X, B=%04X\n"),_fc.type,_fc.version,_fwBlock - 1); + _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest,sizeof(RequestFWBlock))); + } +} + +inline bool firmwareOTAUpdateProcess() { + if (_msg.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")); + // copy new FW config + memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); + // Init flash + if (!_flash.initialize()) { + debug(PSTR("flash init fail\n")); + _fwUpdateOngoing = false; + } else { + // erase lower 32K -> max flash size for ATMEGA328 + _flash.blockErase32K(0); + // wait until flash erased + while ( _flash.busy() ); + _fwBlock = _fc.blocks; + _fwUpdateOngoing = true; + // reset flags + _fwRetry = MY_OTA_RETRY+1; + _fwLastRequestTime = 0; + } + return true; + } + debug(PSTR("fw update skipped\n")); + } else if (_msg.type == ST_FIRMWARE_RESPONSE) { + if (_fwUpdateOngoing) { + // Save block to flash + debug(PSTR("fw block %d\n"), _fwBlock); + // extract FW block + ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data; + // write to flash + _flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); + // wait until flash written + while ( _flash.busy() ); + _fwBlock--; + if (!_fwBlock) { + // We're finished! Do a checksum and reboot. + _fwUpdateOngoing = false; + if (transportIsValidFirmware()) { + 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; + uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'}; + _flash.writeBytes(0, OTAbuffer, 10); + // Write the new firmware config to eeprom + hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); + hwReboot(); + } else { + debug(PSTR("fw checksum fail\n")); + } + } + // reset flags + _fwRetry = MY_OTA_RETRY+1; + _fwLastRequestTime = 0; + } else { + debug(PSTR("No fw update ongoing\n")); + } + return true; + } + return false; +} + +inline void presentBootloaderInformation(){ + RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msgTmp.data; + mSetLength(_msgTmp, sizeof(RequestFirmwareConfig)); + mSetCommand(_msgTmp, C_STREAM); + mSetPayloadType(_msgTmp,P_CUSTOM); + // copy node settings to reqFWConfig + memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig)); + // add bootloader information + reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; + _fwUpdateOngoing = false; + _sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); +} +// do a crc16 on the whole received firmware +inline bool transportIsValidFirmware() { + // init crc + uint16_t crc = ~0; + for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) { + crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET); + for (int8_t j = 0; j < 8; ++j) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + } + return crc == _fc.crc; +} diff --git a/libraries/MySensors/core/MyOTAFirmwareUpdate.h b/libraries/MySensors/core/MyOTAFirmwareUpdate.h new file mode 100644 index 000000000..ff6770179 --- /dev/null +++ b/libraries/MySensors/core/MyOTAFirmwareUpdate.h @@ -0,0 +1,101 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2016 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef MyOTAFirmwareUpdate_h +#define MyOTAFirmwareUpdate_h + +#include "MySensorCore.h" + +// Size of each firmware block +#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) + + +/// @brief FW config structure, stored in eeprom +typedef struct { + uint16_t type; //!< Type of config + uint16_t version; //!< Version of config + uint16_t blocks; //!< Number of blocks + uint16_t crc; //!< CRC of block data +} __attribute__((packed)) NodeFirmwareConfig; + +/// @brief FW config request structure +typedef struct { + uint16_t type; //!< Type of config + uint16_t version; //!< Version of config + uint16_t blocks; //!< Number of blocks + uint16_t crc; //!< CRC of block data + uint16_t BLVersion; //!< Bootloader version +} __attribute__((packed)) RequestFirmwareConfig; + +/// @brief FW block request structure +typedef struct { + uint16_t type; //!< Type of config + uint16_t version; //!< Version of config + uint16_t block; //!< Block index +} __attribute__((packed)) RequestFWBlock; + +/// @brief FW block reply structure +typedef struct { + uint16_t type; //!< Type of config + uint16_t version; //!< Version of config + uint16_t block; //!< Block index + uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data +} __attribute__((packed)) ReplyFWBlock; + + +/** + * @brief Read firmware settings from EEPROM + * + * Current firmware settings (type, version, crc, blocks) are read into _fc + */ +void readFirmwareSettings(); +/** + * @brief Handle OTA FW update requests + */ + void firmwareOTAUpdateRequest(); +/** + * @brief Handle OTA FW update responses + * + * This function handles incoming OTA FW packets and stores them to external flash (Sensebender) + */ +bool firmwareOTAUpdateProcess(); +/** + * @brief Validate uploaded FW CRC + * + * This function verifies if uploaded FW CRC is valid + */ +bool transportIsValidFirmware(); +/** + * @brief Present bootloader/FW information upon startup + */ +void presentBootloaderInformation(); + +#endif \ No newline at end of file diff --git a/libraries/MySensors/core/MySensorCore.cpp b/libraries/MySensors/core/MySensorCore.cpp index f175d42ce..a807fdb0c 100644 --- a/libraries/MySensors/core/MySensorCore.cpp +++ b/libraries/MySensors/core/MySensorCore.cpp @@ -128,9 +128,9 @@ void _begin() { #elif defined(MY_RADIO_FEATURE) // Read settings from eeprom hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig)); - #ifdef MY_OTA_FIRMWARE_FEATURE + #if defined(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)); + readFirmwareSettings(); #endif _autoFindParent = MY_PARENT_NODE_ID == AUTO; diff --git a/libraries/MySensors/core/MySensorCore.h b/libraries/MySensors/core/MySensorCore.h index b5444e572..87957cd87 100644 --- a/libraries/MySensors/core/MySensorCore.h +++ b/libraries/MySensors/core/MySensorCore.h @@ -25,10 +25,6 @@ #include "MyConfig.h" #include "MyEepromAddresses.h" #include "MyMessage.h" -#ifdef MY_OTA_FIRMWARE_FEATURE -#include "drivers/SPIFlash/SPIFlash.h" -#endif - #include #include diff --git a/libraries/MySensors/core/MyTransport.cpp b/libraries/MySensors/core/MyTransport.cpp index cd370c78d..45c02ad9a 100644 --- a/libraries/MySensors/core/MyTransport.cpp +++ b/libraries/MySensors/core/MyTransport.cpp @@ -23,16 +23,6 @@ bool _autoFindParent; uint8_t _failedTransmissions; -#ifdef MY_OTA_FIRMWARE_FEATURE - SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); - NodeFirmwareConfig _fc; - bool _fwUpdateOngoing; - unsigned long _fwLastRequestTime; - uint16_t _fwBlock; - uint8_t _fwRetry; -#endif - - static inline bool isValidDistance( const uint8_t distance ) { return distance != DISTANCE_INVALID; } @@ -43,25 +33,7 @@ inline void transportProcess() { if (!transportAvailable(&to)) { #ifdef MY_OTA_FIRMWARE_FEATURE - unsigned long enter = hwMillis(); - if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) { - if (!_fwRetry) { - debug(PSTR("fw upd fail\n")); - // Give up. We have requested MY_OTA_RETRY times without any packet in return. - _fwUpdateOngoing = false; - ledBlinkErr(1); - return; - } - _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)); - } + firmwareOTAUpdateRequest(); #endif return; } @@ -179,72 +151,13 @@ inline void transportProcess() { } return; } - } - #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")); - // copy new FW config - memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig)); - // Init flash - if (!_flash.initialize()) { - debug(PSTR("flash init fail\n")); - _fwUpdateOngoing = false; - } else { - // erase lower 32K -> max flash size for ATMEGA328 - _flash.blockErase32K(0); - // wait until flash erased - while ( _flash.busy() ); - _fwBlock = _fc.blocks; - _fwUpdateOngoing = true; - // reset flags - _fwRetry = MY_OTA_RETRY+1; - _fwLastRequestTime = 0; - } - return ; + } else if (command == C_STREAM) { + #if defined(MY_OTA_FIRMWARE_FEATURE) + if(firmwareOTAUpdateProcess()){ + return; // OTA FW update processing indicated no further action needed } - debug(PSTR("fw update skipped\n")); - } else if (type == ST_FIRMWARE_RESPONSE) { - if (_fwUpdateOngoing) { - // Save block to flash - debug(PSTR("fw block %d\n"), _fwBlock); - // extract FW block - ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data; - // write to flash - _flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE); - // wait until flash written - while ( _flash.busy() ); - _fwBlock--; - if (!_fwBlock) { - // We're finished! Do a checksum and reboot. - _fwUpdateOngoing = false; - if (transportIsValidFirmware()) { - 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; - uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'}; - _flash.writeBytes(0, OTAbuffer, 10); - // Write the new firmware config to eeprom - hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig)); - hwReboot(); - } else { - debug(PSTR("fw checksum fail\n")); - } - } - // reset flags - _fwRetry = MY_OTA_RETRY+1; - _fwLastRequestTime = 0; - } else { - debug(PSTR("No fw update ongoing\n")); - } - return; - } - + #endif } - #endif #if defined(MY_GATEWAY_FEATURE) // Hand over message to controller gatewayTransportSend(_msg); @@ -296,26 +209,6 @@ inline void transportProcess() { #endif } -#ifdef MY_OTA_FIRMWARE_FEATURE -// do a crc16 on the whole received firmware -bool transportIsValidFirmware() { - // init crc - uint16_t crc = ~0; - for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) { - crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET); - for (int8_t j = 0; j < 8; ++j) { - if (crc & 1) - crc = (crc >> 1) ^ 0xA001; - else - crc = (crc >> 1); - } - } - return crc == _fc.crc; -} - -#endif - - boolean transportSendWrite(uint8_t to, MyMessage &message) { mSetVersion(message, PROTOCOL_VERSION); @@ -463,6 +356,9 @@ void transportPresentNode() { #endif #else if (_nc.nodeId != AUTO) { + #ifdef MY_OTA_FIRMWARE_FEATURE + presentBootloaderInformation(); + #endif // Send signing preferences for this node to the GW signerPresentation(_msg, GATEWAY_ADDRESS); @@ -479,19 +375,6 @@ void transportPresentNode() { // Wait configuration reply. wait(2000, C_INTERNAL, I_CONFIG); - - #ifdef MY_OTA_FIRMWARE_FEATURE - RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msg.data; - mSetLength(_msg, sizeof(RequestFirmwareConfig)); - mSetCommand(_msg, C_STREAM); - mSetPayloadType(_msg,P_CUSTOM); - // copy node settings to reqFWConfig - memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig)); - // add bootloader information - 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 } #endif } diff --git a/libraries/MySensors/core/MyTransport.h b/libraries/MySensors/core/MyTransport.h index d0d8bc768..e7531890b 100644 --- a/libraries/MySensors/core/MyTransport.h +++ b/libraries/MySensors/core/MyTransport.h @@ -23,59 +23,9 @@ #include #include "MySensorCore.h" -// Size of each firmware block -#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) - // Search for a new parent node after this many transmission failures #define SEARCH_FAILURES 5 - -/// @brief FW config structure, stored in eeprom -typedef struct { - uint16_t type; //!< Type of config - uint16_t version; //!< Version of config - uint16_t blocks; //!< Number of blocks - uint16_t crc; //!< CRC of block data -} __attribute__((packed)) NodeFirmwareConfig; - -/// @brief FW config request structure -typedef struct { - uint16_t type; //!< Type of config - uint16_t version; //!< Version of config - uint16_t blocks; //!< Number of blocks - uint16_t crc; //!< CRC of block data - uint16_t BLVersion; //!< Bootloader version -} __attribute__((packed)) RequestFirmwareConfig; - -/// @brief FW block request structure -typedef struct { - uint16_t type; //!< Type of config - uint16_t version; //!< Version of config - uint16_t block; //!< Block index -} __attribute__((packed)) RequestFWBlock; - -/// @brief FW block reply structure -typedef struct { - uint16_t type; //!< Type of config - uint16_t version; //!< Version of config - uint16_t block; //!< Block index - uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data -} __attribute__((packed)) ReplyFWBlock; - - - #define AUTO 0xFF // 0-254. Id 255 is reserved for auto initialization of nodeId. #define BROADCAST_ADDRESS ((uint8_t)0xFF)