Skip to content

Commit a74a2d5

Browse files
committed
Merge pull request #456 from tekka007/MyOTAFirmwareUpdate
Refactor OTA update code
2 parents 906d3cb + 3b94f83 commit a74a2d5

File tree

7 files changed

+258
-182
lines changed

7 files changed

+258
-182
lines changed

libraries/MySensors/MySensor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@
246246
// FLASH
247247
#ifdef MY_OTA_FIRMWARE_FEATURE
248248
#include "drivers/SPIFlash/SPIFlash.cpp"
249+
#include "core/MyOTAFirmwareUpdate.cpp"
249250
#endif
250251
#include "core/MyTransport.cpp"
251252
#if (defined(MY_RADIO_NRF24) && defined(MY_RADIO_RFM69)) || (defined(MY_RADIO_NRF24) && defined(MY_RS485)) || (defined(MY_RADIO_RFM69) && defined(MY_RS485))
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* The MySensors Arduino library handles the wireless radio link and protocol
3+
* between your home built sensors/actuators and HA controller of choice.
4+
* The sensors forms a self healing radio network with optional repeaters. Each
5+
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
6+
* network topology allowing messages to be routed to nodes.
7+
*
8+
* Created by Henrik Ekblad <[email protected]>
9+
* Copyright (C) 2013-2016 Sensnology AB
10+
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
11+
*
12+
* Documentation: http://www.mysensors.org
13+
* Support Forum: http://forum.mysensors.org
14+
*
15+
* This program is free software; you can redistribute it and/or
16+
* modify it under the terms of the GNU General Public License
17+
* version 2 as published by the Free Software Foundation.
18+
*/
19+
20+
#include "MyOTAFirmwareUpdate.h"
21+
22+
SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID);
23+
NodeFirmwareConfig _fc;
24+
bool _fwUpdateOngoing;
25+
unsigned long _fwLastRequestTime;
26+
uint16_t _fwBlock;
27+
uint8_t _fwRetry;
28+
29+
inline void readFirmwareSettings() {
30+
hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
31+
}
32+
33+
inline void firmwareOTAUpdateRequest() {
34+
unsigned long enter = hwMillis();
35+
if (_fwUpdateOngoing && (enter - _fwLastRequestTime > MY_OTA_RETRY_DELAY)) {
36+
if (!_fwRetry) {
37+
debug(PSTR("fw upd fail\n"));
38+
// Give up. We have requested MY_OTA_RETRY times without any packet in return.
39+
_fwUpdateOngoing = false;
40+
ledBlinkErr(1);
41+
return;
42+
}
43+
_fwRetry--;
44+
_fwLastRequestTime = enter;
45+
// Time to (re-)request firmware block from controller
46+
RequestFWBlock firmwareRequest;
47+
firmwareRequest.type = _fc.type;
48+
firmwareRequest.version = _fc.version;
49+
firmwareRequest.block = (_fwBlock - 1);
50+
debug(PSTR("req FW: T=%02X, V=%02X, B=%04X\n"),_fc.type,_fc.version,_fwBlock - 1);
51+
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_REQUEST, false).set(&firmwareRequest,sizeof(RequestFWBlock)));
52+
}
53+
}
54+
55+
inline bool firmwareOTAUpdateProcess() {
56+
if (_msg.type == ST_FIRMWARE_CONFIG_RESPONSE) {
57+
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)_msg.data;
58+
// compare with current node configuration, if they differ, start fw fetch process
59+
if (memcmp(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
60+
debug(PSTR("fw update\n"));
61+
// copy new FW config
62+
memcpy(&_fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
63+
// Init flash
64+
if (!_flash.initialize()) {
65+
debug(PSTR("flash init fail\n"));
66+
_fwUpdateOngoing = false;
67+
} else {
68+
// erase lower 32K -> max flash size for ATMEGA328
69+
_flash.blockErase32K(0);
70+
// wait until flash erased
71+
while ( _flash.busy() );
72+
_fwBlock = _fc.blocks;
73+
_fwUpdateOngoing = true;
74+
// reset flags
75+
_fwRetry = MY_OTA_RETRY+1;
76+
_fwLastRequestTime = 0;
77+
}
78+
return true;
79+
}
80+
debug(PSTR("fw update skipped\n"));
81+
} else if (_msg.type == ST_FIRMWARE_RESPONSE) {
82+
if (_fwUpdateOngoing) {
83+
// Save block to flash
84+
debug(PSTR("fw block %d\n"), _fwBlock);
85+
// extract FW block
86+
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)_msg.data;
87+
// write to flash
88+
_flash.writeBytes( ((_fwBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
89+
// wait until flash written
90+
while ( _flash.busy() );
91+
_fwBlock--;
92+
if (!_fwBlock) {
93+
// We're finished! Do a checksum and reboot.
94+
_fwUpdateOngoing = false;
95+
if (transportIsValidFirmware()) {
96+
debug(PSTR("fw checksum ok\n"));
97+
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
98+
uint16_t fwsize = FIRMWARE_BLOCK_SIZE * _fc.blocks;
99+
uint8_t OTAbuffer[10] = {'F','L','X','I','M','G',':',(uint8_t)(fwsize >> 8),(uint8_t)(fwsize & 0xff),':'};
100+
_flash.writeBytes(0, OTAbuffer, 10);
101+
// Write the new firmware config to eeprom
102+
hwWriteConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
103+
hwReboot();
104+
} else {
105+
debug(PSTR("fw checksum fail\n"));
106+
}
107+
}
108+
// reset flags
109+
_fwRetry = MY_OTA_RETRY+1;
110+
_fwLastRequestTime = 0;
111+
} else {
112+
debug(PSTR("No fw update ongoing\n"));
113+
}
114+
return true;
115+
}
116+
return false;
117+
}
118+
119+
inline void presentBootloaderInformation(){
120+
RequestFirmwareConfig *reqFWConfig = (RequestFirmwareConfig *)_msgTmp.data;
121+
mSetLength(_msgTmp, sizeof(RequestFirmwareConfig));
122+
mSetCommand(_msgTmp, C_STREAM);
123+
mSetPayloadType(_msgTmp,P_CUSTOM);
124+
// copy node settings to reqFWConfig
125+
memcpy(reqFWConfig,&_fc,sizeof(NodeFirmwareConfig));
126+
// add bootloader information
127+
reqFWConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION;
128+
_fwUpdateOngoing = false;
129+
_sendRoute(build(_msgTmp, _nc.nodeId, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false));
130+
}
131+
// do a crc16 on the whole received firmware
132+
inline bool transportIsValidFirmware() {
133+
// init crc
134+
uint16_t crc = ~0;
135+
for (uint16_t i = 0; i < _fc.blocks * FIRMWARE_BLOCK_SIZE; ++i) {
136+
crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET);
137+
for (int8_t j = 0; j < 8; ++j) {
138+
if (crc & 1)
139+
crc = (crc >> 1) ^ 0xA001;
140+
else
141+
crc = (crc >> 1);
142+
}
143+
}
144+
return crc == _fc.crc;
145+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* The MySensors Arduino library handles the wireless radio link and protocol
3+
* between your home built sensors/actuators and HA controller of choice.
4+
* The sensors forms a self healing radio network with optional repeaters. Each
5+
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
6+
* network topology allowing messages to be routed to nodes.
7+
*
8+
* Created by Henrik Ekblad <[email protected]>
9+
* Copyright (C) 2013-2016 Sensnology AB
10+
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
11+
*
12+
* Documentation: http://www.mysensors.org
13+
* Support Forum: http://forum.mysensors.org
14+
*
15+
* This program is free software; you can redistribute it and/or
16+
* modify it under the terms of the GNU General Public License
17+
* version 2 as published by the Free Software Foundation.
18+
*/
19+
20+
#ifndef MyOTAFirmwareUpdate_h
21+
#define MyOTAFirmwareUpdate_h
22+
23+
#include "MySensorCore.h"
24+
25+
// Size of each firmware block
26+
#define FIRMWARE_BLOCK_SIZE 16
27+
// Number of times a firmware block should be requested before giving up
28+
#define FIRMWARE_MAX_REQUESTS 5
29+
// Number of times to request a fw block before giving up
30+
#define MY_OTA_RETRY 5
31+
// Number of millisecons before re-request a fw block
32+
#define MY_OTA_RETRY_DELAY 500
33+
// Start offset for firmware in flash (DualOptiboot wants to keeps a signature first)
34+
#define FIRMWARE_START_OFFSET 10
35+
// Bootloader version
36+
#define MY_OTA_BOOTLOADER_MAJOR_VERSION 3
37+
#define MY_OTA_BOOTLOADER_MINOR_VERSION 0
38+
#define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION)
39+
40+
41+
/// @brief FW config structure, stored in eeprom
42+
typedef struct {
43+
uint16_t type; //!< Type of config
44+
uint16_t version; //!< Version of config
45+
uint16_t blocks; //!< Number of blocks
46+
uint16_t crc; //!< CRC of block data
47+
} __attribute__((packed)) NodeFirmwareConfig;
48+
49+
/// @brief FW config request structure
50+
typedef struct {
51+
uint16_t type; //!< Type of config
52+
uint16_t version; //!< Version of config
53+
uint16_t blocks; //!< Number of blocks
54+
uint16_t crc; //!< CRC of block data
55+
uint16_t BLVersion; //!< Bootloader version
56+
} __attribute__((packed)) RequestFirmwareConfig;
57+
58+
/// @brief FW block request structure
59+
typedef struct {
60+
uint16_t type; //!< Type of config
61+
uint16_t version; //!< Version of config
62+
uint16_t block; //!< Block index
63+
} __attribute__((packed)) RequestFWBlock;
64+
65+
/// @brief FW block reply structure
66+
typedef struct {
67+
uint16_t type; //!< Type of config
68+
uint16_t version; //!< Version of config
69+
uint16_t block; //!< Block index
70+
uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data
71+
} __attribute__((packed)) ReplyFWBlock;
72+
73+
74+
/**
75+
* @brief Read firmware settings from EEPROM
76+
*
77+
* Current firmware settings (type, version, crc, blocks) are read into _fc
78+
*/
79+
void readFirmwareSettings();
80+
/**
81+
* @brief Handle OTA FW update requests
82+
*/
83+
void firmwareOTAUpdateRequest();
84+
/**
85+
* @brief Handle OTA FW update responses
86+
*
87+
* This function handles incoming OTA FW packets and stores them to external flash (Sensebender)
88+
*/
89+
bool firmwareOTAUpdateProcess();
90+
/**
91+
* @brief Validate uploaded FW CRC
92+
*
93+
* This function verifies if uploaded FW CRC is valid
94+
*/
95+
bool transportIsValidFirmware();
96+
/**
97+
* @brief Present bootloader/FW information upon startup
98+
*/
99+
void presentBootloaderInformation();
100+
101+
#endif

libraries/MySensors/core/MySensorCore.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ void _begin() {
128128
#elif defined(MY_RADIO_FEATURE)
129129
// Read settings from eeprom
130130
hwReadConfigBlock((void*)&_nc, (void*)EEPROM_NODE_ID_ADDRESS, sizeof(NodeConfig));
131-
#ifdef MY_OTA_FIRMWARE_FEATURE
131+
#if defined(MY_OTA_FIRMWARE_FEATURE)
132132
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
133-
hwReadConfigBlock((void*)&_fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
133+
readFirmwareSettings();
134134
#endif
135135

136136
_autoFindParent = MY_PARENT_NODE_ID == AUTO;

libraries/MySensors/core/MySensorCore.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@
2525
#include "MyConfig.h"
2626
#include "MyEepromAddresses.h"
2727
#include "MyMessage.h"
28-
#ifdef MY_OTA_FIRMWARE_FEATURE
29-
#include "drivers/SPIFlash/SPIFlash.h"
30-
#endif
31-
3228
#include <stddef.h>
3329
#include <stdarg.h>
3430

0 commit comments

Comments
 (0)