33# Variable baud rate bootloader for Artemis Apollo3 modules
44
55# Immediately upon reset the Artemis module will search for the timing character
6- # to auto-detect the baud rate. If a valid baud rate is found the Artemis will
6+ # to auto-detect the baud rate. If a valid baud rate is found the Artemis will
77# respond with the bootloader version packet
88# If the computer receives a well-formatted version number packet at the desired
9- # baud rate it will send a command to begin bootloading. The Artemis shall then
10- # respond with the a command asking for the next frame.
11- # The host will then send a frame packet. If the CRC is OK the Artemis will write
9+ # baud rate it will send a command to begin bootloading. The Artemis shall then
10+ # respond with the a command asking for the next frame.
11+ # The host will then send a frame packet. If the CRC is OK the Artemis will write
1212# that to memory and request the next frame. If the CRC fails the Artemis will
1313# discard that data and send a request to re-send the previous frame.
1414# This cycle repeats until the Artemis receives a done command in place of the
1515# requested frame data command.
16- # The initial baud rate determination must occur within some small timeout. Once
17- # baud rate detection has completed all additional communication will have a
16+ # The initial baud rate determination must occur within some small timeout. Once
17+ # baud rate detection has completed all additional communication will have a
1818# universal timeout value. Once the Artemis has begun requesting data it may no
19- # no longer exit the bootloader. If the host detects a timeout at any point it
20- # will stop bootloading.
19+ # no longer exit the bootloader. If the host detects a timeout at any point it
20+ # will stop bootloading.
2121
2222# Notes about PySerial timeout:
23- # The timeout operates on whole functions - that is to say that a call to
24- # ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
23+ # The timeout operates on whole functions - that is to say that a call to
24+ # ser.read(10) will return after ser.timeout, just as will ser.read(1) (assuming
2525# that the necessary bytes were not found)
26- # If there are no incoming bytes (on the line or in the buffer) then two calls to
26+ # If there are no incoming bytes (on the line or in the buffer) then two calls to
2727# ser.read(n) will time out after 2*ser.timeout
2828# Incoming UART data is buffered behind the scenes, probably by the OS.
2929
3939import sys
4040import time
4141import math
42+ import os .path
4243from sys import exit
4344
45+ SCRIPT_VERSION_MAJOR = "1"
46+ SCRIPT_VERSION_MINOR = "7"
47+
4448# ***********************************************************************************
4549#
4650# Commands
4751#
4852# ***********************************************************************************
49- SVL_CMD_VER = 0x01 # version
50- SVL_CMD_BL = 0x02 # enter bootload mode
51- SVL_CMD_NEXT = 0x03 # request next chunk
52- SVL_CMD_FRAME = 0x04 # indicate app data frame
53- SVL_CMD_RETRY = 0x05 # request re-send frame
54- SVL_CMD_DONE = 0x06 # finished - all data sent
53+ SVL_CMD_VER = 0x01 # version
54+ SVL_CMD_BL = 0x02 # enter bootload mode
55+ SVL_CMD_NEXT = 0x03 # request next chunk
56+ SVL_CMD_FRAME = 0x04 # indicate app data frame
57+ SVL_CMD_RETRY = 0x05 # request re-send frame
58+ SVL_CMD_DONE = 0x06 # finished - all data sent
5559
5660barWidthInCharacters = 50 # Width of progress bar, ie [###### % complete
5761
97101
98102def get_crc16 (data ):
99103
100- #Table and code ported from Artemis SVL bootloader
104+ # Table and code ported from Artemis SVL bootloader
101105 crc = 0x0000
102106 data = bytearray (data )
103107 for ch in data :
@@ -108,30 +112,33 @@ def get_crc16(data):
108112 return crc
109113
110114
111-
112115# ***********************************************************************************
113116#
114- # Wait for a packet
117+ # Wait for a packet
115118#
116119# ***********************************************************************************
117120def wait_for_packet (ser ):
118121
119- packet = {'len' :0 , 'cmd' :0 , 'data' :0 , 'crc' :1 , 'timeout' :1 }
122+ packet = {'len' : 0 , 'cmd' : 0 , 'data' : 0 , 'crc' : 1 , 'timeout' : 1 }
120123
121- n = ser .read (2 ) # get the number of bytes
124+ n = ser .read (2 ) # get the number of bytes
122125 if (len (n ) < 2 ):
123126 return packet
124-
125- packet ['len' ] = int .from_bytes (n , byteorder = 'big' , signed = False ) #
127+
128+ packet ['len' ] = int .from_bytes (n , byteorder = 'big' , signed = False ) #
126129 payload = ser .read (packet ['len' ])
127130
128131 if (len (payload ) != packet ['len' ]):
129132 return packet
130-
131- packet ['timeout' ] = 0 # all bytes received, so timeout is not true
132- packet ['cmd' ] = payload [0 ] # cmd is the first byte of the payload
133- packet ['data' ] = payload [1 :packet ['len' ]- 2 ] # the data is the part of the payload that is not cmd or crc
134- packet ['crc' ] = get_crc16 (payload ) # performing the crc on the whole payload should return 0
133+
134+ # all bytes received, so timeout is not true
135+ packet ['timeout' ] = 0
136+ # cmd is the first byte of the payload
137+ packet ['cmd' ] = payload [0 ]
138+ # the data is the part of the payload that is not cmd or crc
139+ packet ['data' ] = payload [1 :packet ['len' ]- 2 ]
140+ # performing the crc on the whole payload should return 0
141+ packet ['crc' ] = get_crc16 (payload )
135142
136143 return packet
137144
@@ -140,22 +147,20 @@ def wait_for_packet(ser):
140147# Send a packet
141148#
142149# ***********************************************************************************
150+
151+
143152def send_packet (ser , cmd , data ):
144153 data = bytearray (data )
145154 num_bytes = 3 + len (data )
146- payload = bytearray (cmd .to_bytes (1 ,'big' ))
155+ payload = bytearray (cmd .to_bytes (1 , 'big' ))
147156 payload .extend (data )
148157 crc = get_crc16 (payload )
149- payload .extend (bytearray (crc .to_bytes (2 ,'big' )))
158+ payload .extend (bytearray (crc .to_bytes (2 , 'big' )))
150159
151- ser .write (num_bytes .to_bytes (2 ,'big' ))
160+ ser .write (num_bytes .to_bytes (2 , 'big' ))
152161 ser .write (bytes (payload ))
153162
154163
155-
156-
157-
158-
159164# ***********************************************************************************
160165#
161166# Setup: signal baud rate, get version, and command BL enter
@@ -165,29 +170,27 @@ def phase_setup(ser):
165170
166171 baud_detect_byte = b'U'
167172
168- verboseprint ('\n phase: \t setup ' )
169-
170- # Handle the serial startup blip
173+ verboseprint ('\n Phase: \t Setup ' )
174+
175+ # Handle the serial startup blip
171176 ser .reset_input_buffer ()
172- verboseprint ('\t cleared startup blip' )
177+ verboseprint ('\t Cleared startup blip' )
173178
174179 ser .write (baud_detect_byte ) # send the baud detection character
175180
176181 packet = wait_for_packet (ser )
177182 if (packet ['timeout' ] or packet ['crc' ]):
178- return 1
179-
180- twopartprint ('\t ' ,'Got SVL Bootloader Version: ' +
183+ return False # failed to enter bootloader
184+
185+ twopartprint ('\t ' , 'Got SVL Bootloader Version: ' +
181186 str (int .from_bytes (packet ['data' ], 'big' )))
182187 verboseprint ('\t Sending \' enter bootloader\' command' )
183188
184189 send_packet (ser , SVL_CMD_BL , b'' )
185190
186- # Now enter the bootload phase
187-
188-
189-
191+ return True
190192
193+ # Now enter the bootload phase
191194
192195
193196# ***********************************************************************************
@@ -203,7 +206,7 @@ def phase_bootload(ser):
203206 resend_max = 4
204207 resend_count = 0
205208
206- verboseprint ('\n phase: \t bootload ' )
209+ verboseprint ('\n Phase: \t Bootload ' )
207210
208211 with open (args .binfile , mode = 'rb' ) as binfile :
209212 application = binfile .read ()
@@ -220,36 +223,38 @@ def phase_bootload(ser):
220223 ' bytes to send in ' + str (total_frames ) + ' frames' )
221224
222225 bl_done = False
223- bl_failed = False
224- while ((not bl_done ) and (not bl_failed )):
225-
226- packet = wait_for_packet (ser ) # wait for indication by Artemis
226+ bl_succeeded = True
227+ while ((bl_done == False ) and (bl_succeeded == True )):
228+
229+ # wait for indication by Artemis
230+ packet = wait_for_packet (ser )
227231 if (packet ['timeout' ] or packet ['crc' ]):
228- print ('\n \t error receiving packet' )
229- print (packet )
230- print ('\n ' )
231- bl_failed = True
232+ verboseprint ('\n \t Error receiving packet' )
233+ verboseprint (packet )
234+ verboseprint ('\n ' )
235+ bl_succeeded = False
232236 bl_done = True
233237
234- if ( packet ['cmd' ] == SVL_CMD_NEXT ):
238+ if (packet ['cmd' ] == SVL_CMD_NEXT ):
235239 # verboseprint('\tgot frame request')
236240 curr_frame += 1
237241 resend_count = 0
238- elif ( packet ['cmd' ] == SVL_CMD_RETRY ):
239- verboseprint ('\t \t retrying ...' )
242+ elif (packet ['cmd' ] == SVL_CMD_RETRY ):
243+ verboseprint ('\t \t Retrying ...' )
240244 resend_count += 1
241- if ( resend_count >= resend_max ):
242- bl_failed = True
245+ if (resend_count >= resend_max ):
246+ bl_succeeded = False
243247 bl_done = True
244248 else :
245- print ('unknown error' )
246- bl_failed = True
249+ print ('Timeout or unknown error' )
250+ bl_succeeded = False
247251 bl_done = True
248252
249- if ( curr_frame <= total_frames ):
250- frame_data = application [((curr_frame - 1 )* frame_size ):((curr_frame - 1 + 1 )* frame_size )]
253+ if (curr_frame <= total_frames ):
254+ frame_data = application [(
255+ (curr_frame - 1 )* frame_size ):((curr_frame - 1 + 1 )* frame_size )]
251256 if (args .verbose ):
252- verboseprint ('\t sending frame #' + str (curr_frame ) +
257+ verboseprint ('\t Sending frame #' + str (curr_frame ) +
253258 ', length: ' + str (len (frame_data )))
254259 else :
255260 percentComplete = curr_frame * 100 / total_frames
@@ -267,19 +272,15 @@ def phase_bootload(ser):
267272 send_packet (ser , SVL_CMD_DONE , b'' )
268273 bl_done = True
269274
270- if ( bl_failed == False ):
275+ if (bl_succeeded == True ):
271276 twopartprint ('\n \t ' , 'Upload complete' )
272277 endTime = time .time ()
273278 bps = total_len / (endTime - startTime )
274279 verboseprint ('\n \t Nominal bootload bps: ' + str (round (bps , 2 )))
275280 else :
276281 twopartprint ('\n \t ' , 'Upload failed' )
277282
278- return bl_failed
279-
280-
281-
282-
283+ return bl_succeeded
283284
284285
285286# ***********************************************************************************
@@ -294,12 +295,12 @@ def phase_serial_port_help():
294295 for dev in devices :
295296 if (dev .device .upper () == args .port .upper ()):
296297 print (dev .device + " is currently open. Please close any other terminal programs that may be using " +
297- dev .device + " and try again." )
298+ dev .device + " and try again." )
298299 exit ()
299300
300301 # otherwise, give user a list of possible com ports
301302 print (args .port .upper () +
302- " not found but we detected the following serial ports:" )
303+ " not found but we detected the following serial ports:" )
303304 for dev in devices :
304305 if 'CH340' in dev .description :
305306 print (
@@ -325,23 +326,46 @@ def main():
325326
326327 print ('\n \n Artemis SVL Bootloader' )
327328
329+ verboseprint ("Script version " + SCRIPT_VERSION_MAJOR +
330+ "." + SCRIPT_VERSION_MINOR )
331+
332+ if not os .path .exists (args .binfile ):
333+ print ("Bin file {} does not exist." .format (args .binfile ))
334+ exit ()
335+
336+ bl_success = False
337+ entered_bootloader = False
338+
328339 for _ in range (num_tries ):
329340
330341 with serial .Serial (args .port , args .baud , timeout = args .timeout ) as ser :
331342
332- t_su = 0.15 # startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
343+ # startup time for Artemis bootloader (experimentally determined - 0.095 sec min delay)
344+ t_su = 0.15
333345
334346 time .sleep (t_su ) # Allow Artemis to come out of reset
335- phase_setup (ser ) # Perform baud rate negotiation
336347
337- bl_failed = phase_bootload (ser ) # Bootload
348+ # Perform baud rate negotiation
349+ entered_bootloader = phase_setup (ser )
350+
351+ if (entered_bootloader == True ):
352+ bl_success = phase_bootload (ser )
353+ if (bl_success == True ): # Bootload
354+ #print("Bootload complete!")
355+ break
356+ else :
357+ verboseprint ("Failed to enter bootload phase" )
338358
339- if ( bl_failed == False ):
359+ if (bl_success == True ):
340360 break
341361
342- except :
362+ if (entered_bootloader == False ):
363+ print (
364+ "Target failed to enter bootload mode. Verify the right COM port is selected and that your board has the SVL bootloader." )
365+
366+ except serial .SerialException :
343367 phase_serial_port_help ()
344-
368+
345369 exit ()
346370
347371
@@ -367,7 +391,7 @@ def main():
367391 action = "store_true" )
368392
369393 parser .add_argument ("-t" , "--timeout" , default = 0.50 , help = "Communication timeout in seconds (default 0.5)" ,
370- type = float )
394+ type = float )
371395
372396 if len (sys .argv ) < 2 :
373397 print ("No port selected. Detected Serial Ports:" )
@@ -390,7 +414,7 @@ def verboseprint(*args):
390414
391415 def twopartprint (verbosestr , printstr ):
392416 if args .verbose :
393- print (verbosestr , end = '' )
417+ print (verbosestr , end = '' )
394418
395419 print (printstr )
396420
0 commit comments