1
1
/*
2
2
MYSBootloader: OTA bootloader for Mysensor nodes (www.mysensors.org)
3
- Code based on OTA bootloader by ToSa
3
+ Original OTA bootloader code by ToSa
4
4
Optimized and extended by tekka
5
- Version 1.0 / 20150206
6
- Size: 1834 bytes
5
+ Version 1.1 / 20150314
6
+ Size: 2002 bytes
7
7
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)
9
9
10
- MCU: atmega328
11
- clock: 16Mhz (prescaler: off/1)
10
+ MCU: Atmega328p
12
11
bootsz: 1024W
13
12
14
-
15
13
fuses for ISP:
16
14
EX = 0xFE (use 0x06 for Arduino IDE, boards.txt)
17
15
HI = 0xDA
23
21
24
22
Successfully tested with:
25
23
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
29
38
30
39
This program is free software; you can redistribute it and/or
31
40
modify it under the terms of the GNU General Public License
36
45
37
46
38
47
#define MYSBOOTLOADER_MAJVER 1
39
- #define MYSBOOTLOADER_MINVER 0
48
+ #define MYSBOOTLOADER_MINVER 1
40
49
41
50
#define MYSBOOTLOADER_VERSION (MYSBOOTLOADER_MINVER * 256 + MYSBOOTLOADER_MAJVER)
42
51
43
- //#define USE_PRESCALER
44
52
#define MAX_RESEND 5
45
53
46
54
47
55
// procedures and functions
48
56
49
- int main ( void ) __attribute__ (( OS_main )) __attribute__ (( section ( ".init9" )) );
57
+ static void programPage ( uint16_t page , uint8_t * buf );
50
58
static uint16_t calcCRCrom (const void * ptr , uint16_t len );
51
59
static uint8_t IsFirmwareValid ();
52
60
static void reboot ();
53
61
static void startup ();
54
- static void programPage (uint32_t page , uint8_t * buf );
55
62
static boolean sendWrite (MyMessage message );
56
63
static bool sendAndWait (uint8_t reqType , uint8_t resType );
57
64
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
+
58
82
59
83
static uint16_t calcCRCrom (const void * ptr , uint16_t len ) {
60
84
// init 0xFFFF
61
85
uint16_t crc = ~0 ;
62
- for (uint16_t i = 0 ; i < len ; ++ i ) {
86
+ for (uint16_t i = 0 ; i < len ; i ++ ) {
63
87
crc = _crc16_update (crc , pgm_read_byte ((uint16_t ) ptr + i ));
64
88
}
65
89
return crc ;
66
90
}
67
91
92
+
68
93
static uint8_t IsFirmwareValid () {
69
94
return calcCRCrom (0 , fc .blocks * FIRMWARE_BLOCK_SIZE ) == fc .crc ;
70
95
}
71
96
97
+
98
+
72
99
static void reboot () {
73
- // any pending eeprom activities?
100
+ // wait for pending eeprom activities
74
101
eeprom_busy_wait ();
75
102
// trigger watchdog ASAP
76
103
watchdogConfig (WATCHDOG_16MS );
77
- // wait until WD triggers
104
+ // endless loop
78
105
while (1 );
79
106
}
80
107
81
108
static void startup () {
82
109
if (IsFirmwareValid ()) {
83
110
// WD off
84
111
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
92
113
((void (* )()) 0 )();
93
114
94
115
} else {
95
116
reboot ();
96
117
}
97
118
}
98
119
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
+
113
121
114
122
115
123
static boolean sendWrite (MyMessage message ) {
@@ -121,38 +129,45 @@ static bool sendAndWait(uint8_t reqType, uint8_t resType) {
121
129
// outer loop, retries
122
130
for (uint8_t i = 0 ; i < MAX_RESEND ; i ++ ) {
123
131
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
125
133
for (uint8_t j = 0 ; j < 20 ; j ++ ) {
126
134
// loop 100 times, wait 1ms if no/wrong data => 0.1s
127
135
for (uint8_t k = 0 ; k < 100 ; k ++ ) {
128
136
watchdogReset ();
129
137
// Tx FIFO data available? (we don't care about the pipe here)
130
138
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 ;
149
144
}
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
+ }
150
163
164
+ }
151
165
}
152
- } else {
166
+ } else {
153
167
// wait 1ms if no data available
154
- delaym ( 10 );
168
+ _delay_ms ( 1 );
155
169
}
170
+
156
171
}
157
172
}
158
173
}
@@ -169,13 +184,6 @@ int main(void) {
169
184
// reset MCU status register
170
185
MCUSR = 0 ;
171
186
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
-
179
187
// enable watchdog to avoid deadlock
180
188
watchdogConfig (WATCHDOG_8S );
181
189
@@ -190,18 +198,18 @@ int main(void) {
190
198
// Read firmware config from EEPROM, i.e. type, version, CRC, blocks
191
199
eeprom_read_block ((void * )& fc , (void * )EEPROM_FIRMWARE_TYPE_ADDRESS , sizeof (NodeFirmwareConfig ));
192
200
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;
196
204
nc .distance = 0xFF ;
197
- nc .parentNodeId = 0xFF ;
198
205
199
206
// prepare for I_FIND_PARENTS
200
207
outMsg .sender = nc .nodeId ;
201
208
outMsg .last = nc .nodeId ;
202
209
outMsg .sensor = 0xFF ;
203
210
outMsg .destination = BROADCAST_ADDRESS ;
204
211
212
+ // set header
205
213
mSetVersion (outMsg , PROTOCOL_VERSION );
206
214
mSetLength (outMsg , 0 );
207
215
mSetCommand (outMsg , C_INTERNAL );
@@ -211,7 +219,7 @@ int main(void) {
211
219
// set reading & writing pipe address
212
220
setAddress (nc .nodeId );
213
221
214
- // network up?, get neighbors, else startup
222
+ // network up? get neighbors, else startup
215
223
if (!sendAndWait (I_FIND_PARENT , I_FIND_PARENT_RESPONSE )) {
216
224
startup ();
217
225
}
@@ -225,9 +233,9 @@ int main(void) {
225
233
openReadingPipe (CURRENT_NODE_PIPE , TO_ADDR (BROADCAST_ADDRESS ));
226
234
if (sendAndWait (I_ID_REQUEST , I_ID_RESPONSE )) {
227
235
// 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 ));
229
237
}
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 :)
231
239
reboot ();
232
240
}
233
241
@@ -250,6 +258,23 @@ int main(void) {
250
258
251
259
NodeFirmwareConfig * firmwareConfigResponse = (NodeFirmwareConfig * )inMsg .data ;
252
260
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
+
253
278
// compare with current node configuration, if equal startup
254
279
if (!memcmp (& fc ,firmwareConfigResponse ,sizeof (NodeFirmwareConfig ))) {
255
280
startup ();
@@ -260,7 +285,7 @@ int main(void) {
260
285
// invalidate current CRC
261
286
fc .crc = 0xFFFF ;
262
287
// 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 ));
264
289
265
290
// copy new FW config
266
291
memcpy (& fc ,firmwareConfigResponse ,sizeof (NodeFirmwareConfig ));
@@ -270,33 +295,39 @@ int main(void) {
270
295
firmwareRequest -> type = fc .type ;
271
296
firmwareRequest -> version = fc .version ;
272
297
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
+
276
303
// request FW block
277
304
if (!sendAndWait (ST_FIRMWARE_REQUEST , ST_FIRMWARE_RESPONSE )) {
278
305
reboot ();
279
306
}
280
307
281
308
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
+
292
324
// wuff
293
325
watchdogReset ();
294
326
295
327
// all blocks transmitted, calc CRC and write to eeprom if valid
296
328
if (IsFirmwareValid ()) {
297
329
// 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 ));
300
331
}
301
332
// final step
302
333
reboot ();
0 commit comments