Skip to content

Commit 3f0ca1a

Browse files
committed
Merge pull request arduino#89 from tekka007/master
MYSBootloader 1.1
2 parents c83d8d6 + 9b41d2a commit 3f0ca1a

File tree

6 files changed

+1149
-272
lines changed

6 files changed

+1149
-272
lines changed

MYSBootloader/MYSBootloader.c

Lines changed: 120 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
/*
22
MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org)
3-
Code based on OTA bootloader by ToSa
3+
Original OTA bootloader code by ToSa
44
Optimized and extended by tekka
5-
Version 1.0 / 20150206
6-
Size: 1834 bytes
5+
Version 1.1 / 20150314
6+
Size: 2002 bytes
77
8-
Tested with Atmega328P, 16Mhz 3.3V and 5V and MYSController 0.1.2.266 (goo.gl/9DCWNo)
8+
Tested with MYSController 0.1.2.276 (goo.gl/9DCWNo)
99
10-
MCU: atmega328
11-
clock: 16Mhz (prescaler: off/1)
10+
MCU: Atmega328p
1211
bootsz: 1024W
1312
14-
1513
fuses for ISP:
1614
EX = 0xFE (use 0x06 for Arduino IDE, boards.txt)
1715
HI = 0xDA
@@ -23,9 +21,20 @@
2321
2422
Successfully tested with:
2523
26-
16Mhz extXTAL, 3.3V
27-
1Mhz intRC (8Mhz + DIV8), 3.3V
28-
128kHz intRC, 3.3V
24+
16Mhz extXTAL, 3.3V & 5V
25+
8Mhz intRC, 3.3V & 5V
26+
1Mhz intRC, 3.3 & 5V
27+
128kHz intRC, 3.3V & 5V
28+
29+
* Version 1.1
30+
- use eeprom_update instead of eeprom_write to reduce wear out
31+
- bootloader commands: erase eeprom, set node id
32+
- verify incoming FW blocks for type & address
33+
- communicate over static parent (if set and found) else broadcast to find nearest node
34+
- adjusted timings
35+
36+
* Version 1.0
37+
Initial release
2938
3039
This program is free software; you can redistribute it and/or
3140
modify it under the terms of the GNU General Public License
@@ -36,80 +45,79 @@
3645

3746

3847
#define MYSBOOTLOADER_MAJVER 1
39-
#define MYSBOOTLOADER_MINVER 0
48+
#define MYSBOOTLOADER_MINVER 1
4049

4150
#define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER)
4251

43-
//#define USE_PRESCALER
4452
#define MAX_RESEND 5
4553

4654

4755
// procedures and functions
4856

49-
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
57+
static void programPage(uint16_t page, uint8_t *buf);
5058
static uint16_t calcCRCrom (const void* ptr, uint16_t len);
5159
static uint8_t IsFirmwareValid();
5260
static void reboot();
5361
static void startup();
54-
static void programPage(uint32_t page, uint8_t *buf);
5562
static boolean sendWrite(MyMessage message);
5663
static bool sendAndWait(uint8_t reqType, uint8_t resType);
5764

65+
int main(void) __attribute__ ((OS_main)) __attribute__ ((section (".init9")));
66+
67+
68+
static void programPage(uint16_t page, uint8_t *buf) {
69+
// these function calls use "out" commands: save some bytes and cycles :)
70+
__boot_page_erase_short(page);
71+
boot_spm_busy_wait();
72+
for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) {
73+
uint16_t data_word = *buf++;
74+
data_word += (*buf++) << 8;
75+
__boot_page_fill_short(page + i, data_word);
76+
}
77+
__boot_page_write_short(page);
78+
boot_spm_busy_wait();
79+
__boot_rww_enable_short();
80+
}
81+
5882

5983
static uint16_t calcCRCrom (const void* ptr, uint16_t len) {
6084
// init 0xFFFF
6185
uint16_t crc = ~0;
62-
for (uint16_t i = 0; i < len; ++i) {
86+
for (uint16_t i = 0; i < len; i++) {
6387
crc = _crc16_update(crc, pgm_read_byte((uint16_t) ptr + i));
6488
}
6589
return crc;
6690
}
6791

92+
6893
static uint8_t IsFirmwareValid () {
6994
return calcCRCrom(0, fc.blocks * FIRMWARE_BLOCK_SIZE) == fc.crc;
7095
}
7196

97+
98+
7299
static void reboot() {
73-
// any pending eeprom activities?
100+
// wait for pending eeprom activities
74101
eeprom_busy_wait();
75102
// trigger watchdog ASAP
76103
watchdogConfig(WATCHDOG_16MS);
77-
// wait until WD triggers
104+
// endless loop
78105
while (1);
79106
}
80107

81108
static void startup() {
82109
if (IsFirmwareValid()) {
83110
// WD off
84111
watchdogConfig(WATCHDOG_OFF);
85-
86-
#ifdef USE_PRESCALER
87-
// reset prescaler
88-
clock_prescale_set(orgClockDiv);
89-
#endif
90-
91-
// start sketch
112+
// run sketch
92113
((void(*)()) 0)();
93114

94115
} else {
95116
reboot();
96117
}
97118
}
98119

99-
static void programPage(uint32_t page, uint8_t *buf) {
100-
// using out commands, saves some bytes :)
101-
__boot_page_erase_short(page);
102-
boot_spm_busy_wait();
103-
for (uint16_t i = 0; i < SPM_PAGESIZE; i += 2) {
104-
uint16_t w = *buf++;
105-
// generate word
106-
w += (*buf++) << 8;
107-
__boot_page_fill_short(page + i, w);
108-
}
109-
__boot_page_write_short(page);
110-
boot_spm_busy_wait();
111-
__boot_rww_enable_short();
112-
}
120+
113121

114122

115123
static boolean sendWrite(MyMessage message) {
@@ -121,38 +129,45 @@ static bool sendAndWait(uint8_t reqType, uint8_t resType) {
121129
// outer loop, retries
122130
for (uint8_t i = 0; i < MAX_RESEND; i++) {
123131
sendWrite(outMsg);
124-
// loop 20 times, wait time 0.1ms if no/wrong data => 2s
132+
// loop 20 times, wait time 0.1s if no/wrong data => 2s
125133
for (uint8_t j = 0; j < 20; j++) {
126134
// loop 100 times, wait 1ms if no/wrong data => 0.1s
127135
for (uint8_t k = 0; k < 100; k++) {
128136
watchdogReset();
129137
// Tx FIFO data available? (we don't care about the pipe here)
130138
if (available(NULL)) {
131-
// read message from FIFO
132-
readMessage(inMsg.array);
133-
// protocol compatible? if not ignore msg
134-
if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) {
135-
continue;
136-
}
137-
if (inMsg.destination == nc.nodeId) {
138-
// msg for us
139-
if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) {
140-
if (inMsg.bValue < nc.distance - 1) {
141-
// got new routing info, update settings
142-
nc.distance = inMsg.bValue + 1;
143-
nc.parentNodeId = inMsg.sender;
144-
}
145-
}
146-
// did we receive expected reply?
147-
if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) {
148-
return true;
139+
// read message from FIFO, skip if size = 0
140+
if (readMessage(inMsg.array) > 0) {
141+
// protocol compatible? if not ignore msg
142+
if ((mGetVersion(inMsg) != PROTOCOL_VERSION)) {
143+
continue;
149144
}
145+
// msg for us?
146+
if (inMsg.destination == nc.nodeId) {
147+
// internal command: find parent
148+
if ((mGetCommand(inMsg) == C_INTERNAL) && (inMsg.type == I_FIND_PARENT_RESPONSE)) {
149+
// static parent found?
150+
if (configuredParentID == inMsg.sender) {
151+
configuredParentFound = true;
152+
}
153+
if ( ((inMsg.bValue < nc.distance - 1) && ( !configuredParentFound) ) || (configuredParentID == inMsg.sender)) {
154+
// got new routing info, update settings
155+
nc.distance = inMsg.bValue + 1;
156+
nc.parentNodeId = inMsg.sender;
157+
}
158+
}
159+
// did we receive expected reply?
160+
if ((mGetCommand(inMsg) == mGetCommand(outMsg)) && (inMsg.type == resType)) {
161+
return true;
162+
}
150163

164+
}
151165
}
152-
} else {
166+
} else {
153167
// wait 1ms if no data available
154-
delaym(10);
168+
_delay_ms(1);
155169
}
170+
156171
}
157172
}
158173
}
@@ -169,13 +184,6 @@ int main(void) {
169184
// reset MCU status register
170185
MCUSR = 0;
171186

172-
#ifdef USE_PRESCALER
173-
// get current prescale
174-
orgClockDiv = clock_prescale_get();
175-
//switch to 4 MHz on 16Mhz crystal
176-
clock_prescale_set(F_CPU_DIV);
177-
#endif
178-
179187
// enable watchdog to avoid deadlock
180188
watchdogConfig(WATCHDOG_8S);
181189

@@ -190,18 +198,18 @@ int main(void) {
190198
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
191199
eeprom_read_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
192200

193-
// bootloader should find closest node during each reboot
194-
195-
// invalidate parent node settings, since we have to re-discover them for every single reboot
201+
// find nearest node during reboot: invalidate parent node settings, since we have to re-discover them for every single reboot
202+
configuredParentID = nc.parentNodeId;
203+
// nc.parentNodeId = 0xFF;
196204
nc.distance = 0xFF;
197-
nc.parentNodeId = 0xFF;
198205

199206
// prepare for I_FIND_PARENTS
200207
outMsg.sender = nc.nodeId;
201208
outMsg.last = nc.nodeId;
202209
outMsg.sensor = 0xFF;
203210
outMsg.destination = BROADCAST_ADDRESS;
204211

212+
// set header
205213
mSetVersion(outMsg, PROTOCOL_VERSION);
206214
mSetLength(outMsg, 0);
207215
mSetCommand(outMsg, C_INTERNAL);
@@ -211,7 +219,7 @@ int main(void) {
211219
// set reading & writing pipe address
212220
setAddress(nc.nodeId);
213221

214-
// network up?, get neighbors, else startup
222+
// network up? get neighbors, else startup
215223
if (!sendAndWait(I_FIND_PARENT, I_FIND_PARENT_RESPONSE)) {
216224
startup();
217225
}
@@ -225,9 +233,9 @@ int main(void) {
225233
openReadingPipe(CURRENT_NODE_PIPE, TO_ADDR(BROADCAST_ADDRESS));
226234
if (sendAndWait(I_ID_REQUEST, I_ID_RESPONSE)) {
227235
// save id to eeprom
228-
eeprom_write_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data));
236+
eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, atoi(inMsg.data));
229237
}
230-
// we could go on and set everything right here, but rebooting will take care of that - saves bytes :)
238+
// we could go on and set everything right here, but rebooting will take care of that - and saves some bytes :)
231239
reboot();
232240
}
233241

@@ -250,6 +258,23 @@ int main(void) {
250258

251259
NodeFirmwareConfig *firmwareConfigResponse = (NodeFirmwareConfig *)inMsg.data;
252260

261+
// bootloader commands
262+
if (firmwareConfigResponse->blocks == 0) {
263+
// verify flag
264+
if (firmwareConfigResponse->crc == 0xDA7A){
265+
// cmd 0x01 clear eeprom
266+
if(firmwareConfigResponse->bl_command == 0x01) {
267+
for(uint16_t i = 0; i < EEPROM_SIZE; i++) eeprom_update_byte((uint8_t *)i,0xFF);
268+
} else
269+
// cmd 0x02 set id
270+
if(firmwareConfigResponse->bl_command == 0x02) {
271+
eeprom_update_byte((uint8_t*)EEPROM_NODE_ID_ADDRESS, (uint8_t)firmwareConfigResponse->bl_data);
272+
}
273+
}
274+
// final step
275+
reboot();
276+
}
277+
253278
// compare with current node configuration, if equal startup
254279
if (!memcmp(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig))) {
255280
startup();
@@ -260,7 +285,7 @@ int main(void) {
260285
// invalidate current CRC
261286
fc.crc = 0xFFFF;
262287
// write fetched type and version in case OTA fails (BL will reboot and re-request FW with stored settings)
263-
eeprom_write_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig));
288+
eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,sizeof(NodeFirmwareConfig));
264289

265290
// copy new FW config
266291
memcpy(&fc,firmwareConfigResponse,sizeof(NodeFirmwareConfig));
@@ -270,33 +295,39 @@ int main(void) {
270295
firmwareRequest->type = fc.type;
271296
firmwareRequest->version = fc.version;
272297

273-
// request FW from controller
274-
for (uint16_t block = fc.blocks; block > 0; block--) {
275-
firmwareRequest->block = (block - 1);
298+
// request FW from controller, load FW counting backwards
299+
uint16_t block = fc.blocks;
300+
do {
301+
firmwareRequest->block = block - 1;
302+
276303
// request FW block
277304
if (!sendAndWait(ST_FIRMWARE_REQUEST, ST_FIRMWARE_RESPONSE)) {
278305
reboot();
279306
}
280307

281308
ReplyFWBlock *firmwareResponse = (ReplyFWBlock *)inMsg.data;
282-
// calculate page offset
283-
uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE;
284-
// write to buffer
285-
memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
286-
// program if page full
287-
if (offset == 0) {
288-
programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf);
289-
}
290-
}
291-
309+
310+
// did we receive requested block?
311+
if (!memcmp(firmwareRequest,firmwareResponse,sizeof(RequestFWBlock))) {
312+
// calculate page offset
313+
uint8_t offset = ((block - 1) * FIRMWARE_BLOCK_SIZE) % SPM_PAGESIZE;
314+
// write to buffer
315+
memcpy(progBuf + offset, firmwareResponse->data, FIRMWARE_BLOCK_SIZE);
316+
// program if page full
317+
if (offset == 0) {
318+
programPage(((block - 1) * FIRMWARE_BLOCK_SIZE), progBuf);
319+
}
320+
block--;
321+
}
322+
} while (block);
323+
292324
// wuff
293325
watchdogReset();
294326

295327
// all blocks transmitted, calc CRC and write to eeprom if valid
296328
if (IsFirmwareValid()) {
297329
// if FW is valid, write settings to eeprom
298-
eeprom_write_block((void*)&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
299-
// we could restart directly here, but eeprom may still be busy, thus reboot (we check for idle eeprom before rebooting) and save some bytes :)
330+
eeprom_update_block(&fc, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS, sizeof(NodeFirmwareConfig));
300331
}
301332
// final step
302333
reboot();

0 commit comments

Comments
 (0)