forked from mysensors/MySensors
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request mysensors#456 from tekka007/MyOTAFirmwareUpdate
Refactor OTA update code
- Loading branch information
Showing
7 changed files
with
258 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <henrik.ekblad@mysensors.org> | ||
* 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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <henrik.ekblad@mysensors.org> | ||
* 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.