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
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
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 );
5058static uint16_t calcCRCrom (const void * ptr , uint16_t len );
5159static uint8_t IsFirmwareValid ();
5260static void reboot ();
5361static void startup ();
54- static void programPage (uint32_t page , uint8_t * buf );
5562static boolean sendWrite (MyMessage message );
5663static 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
5983static 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+
6893static uint8_t IsFirmwareValid () {
6994 return calcCRCrom (0 , fc .blocks * FIRMWARE_BLOCK_SIZE ) == fc .crc ;
7095}
7196
97+
98+
7299static 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
81108static 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
115123static 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