dRonin  adbada4
dRonin firmware
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
px_uploader.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 ############################################################################
3 #
4 # Copyright (C) 2012-2015 PX4 Development Team. All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # 1. Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # 2. Redistributions in binary form must reproduce the above copyright
13 # notice, this list of conditions and the following disclaimer in
14 # the documentation and/or other materials provided with the
15 # distribution.
16 # 3. Neither the name PX4 nor the names of its contributors may be
17 # used to endorse or promote products derived from this software
18 # without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 #
33 ############################################################################
34 
35 #
36 # Serial firmware uploader for the PX4FMU bootloader
37 #
38 # The PX4 firmware file is a JSON-encoded Python object, containing
39 # metadata fields and a zlib-compressed base64-encoded firmware image.
40 #
41 # The uploader uses the following fields from the firmware file:
42 #
43 # image
44 # The firmware that will be uploaded.
45 # image_size
46 # The size of the firmware in bytes.
47 # board_id
48 # The board for which the firmware is intended.
49 # board_revision
50 # Currently only used for informational purposes.
51 #
52 
53 # for python2.7 compatibility
54 from __future__ import print_function
55 
56 import sys
57 import argparse
58 import binascii
59 import serial
60 import struct
61 import json
62 import zlib
63 import base64
64 import time
65 import array
66 import os
67 
68 from sys import platform as _platform
69 
70 
71 class firmware(object):
72  '''Loads a firmware file'''
73 
74  desc = {}
75  image = bytes()
76  crctab = array.array('I', [
77  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
78  0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
79  0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
80  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
81  0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
82  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
83  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
84  0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
85  0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
86  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
87  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
88  0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
89  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
90  0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
91  0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
92  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
93  0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
94  0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
95  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
96  0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
97  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
98  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
99  0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
100  0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
101  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
102  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
103  0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
104  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
105  0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
106  0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
107  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
108  0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d])
109  crcpad = bytearray(b'\xff\xff\xff\xff')
110 
111  def __init__(self, path):
112 
113  # read the file
114  f = open(path, "r")
115  self.desc = json.load(f)
116  f.close()
117 
118  self.image = bytearray(zlib.decompress(base64.b64decode(self.desc['image'])))
119 
120  # pad image to 4-byte length
121  while ((len(self.image) % 4) != 0):
122  self.image.append('\xff')
123 
124  def property(self, propname):
125  return self.desc[propname]
126 
127  def __crc32(self, bytes, state):
128  for byte in bytes:
129  index = (state ^ byte) & 0xff
130  state = self.crctab[index] ^ (state >> 8)
131  return state
132 
133  def crc(self, padlen):
134  state = self.__crc32(self.image, int(0))
135  for i in range(len(self.image), (padlen - 1), 4):
136  state = self.__crc32(self.crcpad, state)
137  return state
138 
139 
140 class uploader(object):
141  '''Uploads a firmware file to the PX FMU bootloader'''
142 
143  # protocol bytes
144  INSYNC = b'\x12'
145  EOC = b'\x20'
146 
147  # reply bytes
148  OK = b'\x10'
149  FAILED = b'\x11'
150  INVALID = b'\x13' # rev3+
151  BAD_SILICON_REV = b'\x14' # rev5+
152 
153  # command bytes
154  NOP = b'\x00' # guaranteed to be discarded by the bootloader
155  GET_SYNC = b'\x21'
156  GET_DEVICE = b'\x22'
157  CHIP_ERASE = b'\x23'
158  CHIP_VERIFY = b'\x24' # rev2 only
159  PROG_MULTI = b'\x27'
160  READ_MULTI = b'\x28' # rev2 only
161  GET_CRC = b'\x29' # rev3+
162  GET_OTP = b'\x2a' # rev4+ , get a word from OTP area
163  GET_SN = b'\x2b' # rev4+ , get a word from SN area
164  GET_CHIP = b'\x2c' # rev5+ , get chip version
165  SET_BOOT_DELAY = b'\x2d' # rev5+ , set boot delay
166  GET_CHIP_DES = b'\x2e' # rev5+ , get chip description in ASCII
167  MAX_DES_LENGTH = 20
168 
169  REBOOT = b'\x30'
170 
171  INFO_BL_REV = b'\x01' # bootloader protocol revision
172  BL_REV_MIN = 2 # minimum supported bootloader protocol
173  BL_REV_MAX = 5 # maximum supported bootloader protocol
174  INFO_BOARD_ID = b'\x02' # board type
175  INFO_BOARD_REV = b'\x03' # board revision
176  INFO_FLASH_SIZE = b'\x04' # max firmware size in bytes
177 
178  PROG_MULTI_MAX = 252 # protocol max is 255, must be multiple of 4
179  READ_MULTI_MAX = 252 # protocol max is 255
180 
181  NSH_INIT = bytearray(b'\x0d\x0d\x0d')
182  NSH_REBOOT_BL = b"reboot -b\n"
183  NSH_REBOOT = b"reboot\n"
184  MAVLINK_REBOOT_ID1 = bytearray(b'\xfe\x21\x72\xff\x00\x4c\x00\x00\x80\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x01\x00\x00\x48\xf0')
185  MAVLINK_REBOOT_ID0 = bytearray(b'\xfe\x21\x45\xff\x00\x4c\x00\x00\x80\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x00\x00\x00\xd7\xac')
186 
187  def __init__(self, portname, baudrate):
188  # open the port, keep the default timeout short so we can poll quickly
189  self.port = serial.Serial(portname, baudrate, timeout=0.5)
190  self.otp = b''
191  self.sn = b''
192 
193  def close(self):
194  if self.port is not None:
195  self.port.close()
196 
197  def __send(self, c):
198 # print("send " + binascii.hexlify(c))
199  self.port.write(c)
200 
201  def __recv(self, count=1):
202  c = self.port.read(count)
203  if len(c) < 1:
204  raise RuntimeError("timeout waiting for data (%u bytes)" % count)
205 # print("recv " + binascii.hexlify(c))
206  return c
207 
208  def __recv_int(self):
209  raw = self.__recv(4)
210  val = struct.unpack("<I", raw)
211  return val[0]
212 
213  def __getSync(self):
214  self.port.flush()
215  c = bytes(self.__recv())
216  if c != self.INSYNC:
217  raise RuntimeError("unexpected %s instead of INSYNC" % c)
218  c = self.__recv()
219  if c == self.INVALID:
220  raise RuntimeError("bootloader reports INVALID OPERATION")
221  if c == self.FAILED:
222  raise RuntimeError("bootloader reports OPERATION FAILED")
223  if c != self.OK:
224  raise RuntimeError("unexpected response 0x%x instead of OK" % ord(c))
225 
226  # attempt to get back into sync with the bootloader
227  def __sync(self):
228  # send a stream of ignored bytes longer than the longest possible conversation
229  # that we might still have in progress
230 # self.__send(uploader.NOP * (uploader.PROG_MULTI_MAX + 2))
231  self.port.flushInput()
232  self.__send(uploader.GET_SYNC
233  + uploader.EOC)
234  self.__getSync()
235 
236  def __trySync(self):
237  try:
238  self.port.flush()
239  if (self.__recv() != self.INSYNC):
240  #print("unexpected 0x%x instead of INSYNC" % ord(c))
241  return False;
242  c = self.__recv()
243  if (c == self.BAD_SILICON_REV):
244  raise NotImplementedError()
245  if (c != self.OK):
246  #print("unexpected 0x%x instead of OK" % ord(c))
247  return False
248  return True
249 
250  except NotImplementedError:
251  raise RuntimeError("Programing not supported for this version of silicon!\n See https://pixhawk.org/help/errata")
252  except RuntimeError:
253  #timeout, no response yet
254  return False
255 
256  # send the GET_DEVICE command and wait for an info parameter
257  def __getInfo(self, param):
258  self.__send(uploader.GET_DEVICE + param + uploader.EOC)
259  value = self.__recv_int()
260  self.__getSync()
261  return value
262 
263  # send the GET_OTP command and wait for an info parameter
264  def __getOTP(self, param):
265  t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
266  self.__send(uploader.GET_OTP + t + uploader.EOC)
267  value = self.__recv(4)
268  self.__getSync()
269  return value
270 
271  # send the GET_SN command and wait for an info parameter
272  def __getSN(self, param):
273  t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
274  self.__send(uploader.GET_SN + t + uploader.EOC)
275  value = self.__recv(4)
276  self.__getSync()
277  return value
278 
279  # send the GET_CHIP command
280  def __getCHIP(self):
281  self.__send(uploader.GET_CHIP + uploader.EOC)
282  value = self.__recv_int()
283  self.__getSync()
284  return value
285  # send the GET_CHIP command
286  def __getCHIPDes(self):
287  self.__send(uploader.GET_CHIP_DES + uploader.EOC)
288  length = self.__recv_int()
289  value = self.__recv(length)
290  self.__getSync()
291  peices = value.split(",")
292  return peices
293 
294  def __drawProgressBar(self, label, progress, maxVal):
295  if maxVal < progress:
296  progress = maxVal
297 
298  percent = (float(progress) / float(maxVal)) * 100.0
299 
300  sys.stdout.write("\r%s: [%-20s] %.1f%%" % (label, '='*int(percent/5.0), percent))
301  sys.stdout.flush()
302 
303 
304  # send the CHIP_ERASE command and wait for the bootloader to become ready
305  def __erase(self, label):
306  print("\n", end='')
307  self.__send(uploader.CHIP_ERASE
308  + uploader.EOC)
309 
310  # erase is very slow, give it 20s
311  deadline = time.time() + 20.0
312  while time.time() < deadline:
313 
314  #Draw progress bar (erase usually takes about 9 seconds to complete)
315  estimatedTimeRemaining = deadline-time.time()
316  if estimatedTimeRemaining >= 9.0:
317  self.__drawProgressBar(label, 20.0-estimatedTimeRemaining, 9.0)
318  else:
319  self.__drawProgressBar(label, 10.0, 10.0)
320  sys.stdout.write(" (timeout: %d seconds) " % int(deadline-time.time()) )
321  sys.stdout.flush()
322 
323  if self.__trySync():
324  self.__drawProgressBar(label, 10.0, 10.0)
325  return;
326 
327  raise RuntimeError("timed out waiting for erase")
328 
329  # send a PROG_MULTI command to write a collection of bytes
330  def __program_multi(self, data):
331 
332  if runningPython3 == True:
333  length = len(data).to_bytes(1, byteorder='big')
334  else:
335  length = chr(len(data))
336 
337  self.__send(uploader.PROG_MULTI)
338  self.__send(length)
339  self.__send(data)
340  self.__send(uploader.EOC)
341  self.__getSync()
342 
343  # verify multiple bytes in flash
344  def __verify_multi(self, data):
345 
346  if runningPython3 == True:
347  length = len(data).to_bytes(1, byteorder='big')
348  else:
349  length = chr(len(data))
350 
351  self.__send(uploader.READ_MULTI)
352  self.__send(length)
353  self.__send(uploader.EOC)
354  self.port.flush()
355  programmed = self.__recv(len(data))
356  if programmed != data:
357  print("got " + binascii.hexlify(programmed))
358  print("expect " + binascii.hexlify(data))
359  return False
360  self.__getSync()
361  return True
362 
363  # send the reboot command
364  def __reboot(self):
365  self.__send(uploader.REBOOT
366  + uploader.EOC)
367  self.port.flush()
368 
369  # v3+ can report failure if the first word flash fails
370  if self.bl_rev >= 3:
371  self.__getSync()
372 
373  # split a sequence into a list of size-constrained pieces
374  def __split_len(self, seq, length):
375  return [seq[i:i+length] for i in range(0, len(seq), length)]
376 
377  # upload code
378  def __program(self, label, fw):
379  print("\n", end='')
380  code = fw.image
381  groups = self.__split_len(code, uploader.PROG_MULTI_MAX)
382 
383  uploadProgress = 0
384  for bytes in groups:
385  self.__program_multi(bytes)
386 
387  #Print upload progress (throttled, so it does not delay upload progress)
388  uploadProgress += 1
389  if uploadProgress % 256 == 0:
390  self.__drawProgressBar(label, uploadProgress, len(groups))
391  self.__drawProgressBar(label, 100, 100)
392 
393  # verify code
394  def __verify_v2(self, label, fw):
395  print("\n", end='')
396  self.__send(uploader.CHIP_VERIFY
397  + uploader.EOC)
398  self.__getSync()
399  code = fw.image
400  groups = self.__split_len(code, uploader.READ_MULTI_MAX)
401  verifyProgress = 0
402  for bytes in groups:
403  verifyProgress += 1
404  if verifyProgress % 256 == 0:
405  self.__drawProgressBar(label, verifyProgress, len(groups))
406  if (not self.__verify_multi(bytes)):
407  raise RuntimeError("Verification failed")
408  self.__drawProgressBar(label, 100, 100)
409 
410  def __verify_v3(self, label, fw):
411  print("\n", end='')
412  self.__drawProgressBar(label, 1, 100)
413  expect_crc = fw.crc(self.fw_maxsize)
414  self.__send(uploader.GET_CRC
415  + uploader.EOC)
416  report_crc = self.__recv_int()
417  self.__getSync()
418  verifyProgress = 0
419  if report_crc != expect_crc:
420  print("Expected 0x%x" % expect_crc)
421  print("Got 0x%x" % report_crc)
422  raise RuntimeError("Program CRC failed")
423  self.__drawProgressBar(label, 100, 100)
424 
425  def __set_boot_delay(self, boot_delay):
426  self.__send(uploader.SET_BOOT_DELAY
427  + struct.pack("b", boot_delay)
428  + uploader.EOC)
429  self.__getSync()
430 
431  # get basic data about the board
432  def identify(self):
433  # make sure we are in sync before starting
434  self.__sync()
435 
436  # get the bootloader protocol ID first
437  self.bl_rev = self.__getInfo(uploader.INFO_BL_REV)
438  if (self.bl_rev < uploader.BL_REV_MIN) or (self.bl_rev > uploader.BL_REV_MAX):
439  print("Unsupported bootloader protocol %d" % uploader.INFO_BL_REV)
440  raise RuntimeError("Bootloader protocol mismatch")
441 
442  self.board_type = self.__getInfo(uploader.INFO_BOARD_ID)
443  self.board_rev = self.__getInfo(uploader.INFO_BOARD_REV)
444  self.fw_maxsize = self.__getInfo(uploader.INFO_FLASH_SIZE)
445 
446  # upload the firmware
447  def upload(self, fw):
448  # Make sure we are doing the right thing
449  if self.board_type != fw.property('board_id'):
450  msg = "Firmware not suitable for this board (board_type=%u board_id=%u)" % (
451  self.board_type, fw.property('board_id'))
452  print("WARNING: %s" % msg)
453  if args.force:
454  print("FORCED WRITE, FLASHING ANYWAY!")
455  else:
456  raise IOError(msg)
457  if self.fw_maxsize < fw.property('image_size'):
458  raise RuntimeError("Firmware image is too large for this board")
459 
460  # OTP added in v4:
461  if self.bl_rev > 3:
462  for byte in range(0,32*6,4):
463  x = self.__getOTP(byte)
464  self.otp = self.otp + x
465  print(binascii.hexlify(x).decode('Latin-1') + ' ', end='')
466  # see src/modules/systemlib/otp.h in px4 code:
467  self.otp_id = self.otp[0:4]
468  self.otp_idtype = self.otp[4:5]
469  self.otp_vid = self.otp[8:4:-1]
470  self.otp_pid = self.otp[12:8:-1]
471  self.otp_coa = self.otp[32:160]
472  # show user:
473  try:
474  print("type: " + self.otp_id.decode('Latin-1'))
475  print("idtype: " + binascii.b2a_qp(self.otp_idtype).decode('Latin-1'))
476  print("vid: " + binascii.hexlify(self.otp_vid).decode('Latin-1'))
477  print("pid: "+ binascii.hexlify(self.otp_pid).decode('Latin-1'))
478  print("coa: "+ binascii.b2a_base64(self.otp_coa).decode('Latin-1'))
479  print("sn: ", end='')
480  for byte in range(0,12,4):
481  x = self.__getSN(byte)
482  x = x[::-1] # reverse the bytes
483  self.sn = self.sn + x
484  print(binascii.hexlify(x).decode('Latin-1'), end='') # show user
485  print('')
486  print("chip: %08x" % self.__getCHIP())
487  if (self.bl_rev >= 5):
488  des = self.__getCHIPDes()
489  if (len(des) == 2):
490  print("family: %s" % des[0])
491  print("revision: %s" % des[1])
492  print("flash %d" % self.fw_maxsize)
493  except Exception:
494  # ignore bad character encodings
495  pass
496 
497  self.__erase("Erase ")
498  self.__program("Program", fw)
499 
500  if self.bl_rev == 2:
501  self.__verify_v2("Verify ", fw)
502  else:
503  self.__verify_v3("Verify ", fw)
504 
505  if args.boot_delay is not None:
506  self.__set_boot_delay(args.boot_delay)
507 
508  print("\nRebooting.\n")
509  self.__reboot()
510  self.port.close()
511 
512  def send_reboot(self):
513  try:
514  # try reboot via NSH first
515  self.__send(uploader.NSH_INIT)
516  self.__send(uploader.NSH_REBOOT_BL)
517  self.__send(uploader.NSH_INIT)
518  self.__send(uploader.NSH_REBOOT)
519  # then try MAVLINK command
520  self.__send(uploader.MAVLINK_REBOOT_ID1)
521  self.__send(uploader.MAVLINK_REBOOT_ID0)
522  except:
523  return
524 
525 
526 # Detect python version
527 if sys.version_info[0] < 3:
528  runningPython3 = False
529 else:
530  runningPython3 = True
531 
532 # Parse commandline arguments
533 parser = argparse.ArgumentParser(description="Firmware uploader for the PX autopilot system.")
534 parser.add_argument('--port', action="store", required=True, help="Serial port(s) to which the FMU may be attached")
535 parser.add_argument('--baud', action="store", type=int, default=115200, help="Baud rate of the serial port (default is 115200), only required for true serial ports.")
536 parser.add_argument('--force', action='store_true', default=False, help='Override board type check and continue loading')
537 parser.add_argument('--boot-delay', type=int, default=None, help='minimum boot delay to store in flash')
538 parser.add_argument('firmware', action="store", help="Firmware file to be uploaded")
539 args = parser.parse_args()
540 
541 # warn people about ModemManager which interferes badly with Pixhawk
542 if os.path.exists("/usr/sbin/ModemManager"):
543  print("==========================================================================================================")
544  print("WARNING: You should uninstall ModemManager as it conflicts with any non-modem serial device (like Pixhawk)")
545  print("==========================================================================================================")
546 
547 # Load the firmware file
548 fw = firmware(args.firmware)
549 print("Loaded firmware for %x,%x, size: %d bytes, waiting for the bootloader..." % (fw.property('board_id'), fw.property('board_revision'), fw.property('image_size')))
550 print("If the board does not respond within 1-2 seconds, unplug and re-plug the USB connector.")
551 
552 # Spin waiting for a device to show up
553 try:
554  while True:
555  portlist = []
556  patterns = args.port.split(",")
557  # on unix-like platforms use glob to support wildcard ports. This allows
558  # the use of /dev/serial/by-id/usb-3D_Robotics on Linux, which prevents the upload from
559  # causing modem hangups etc
560  if "linux" in _platform or "darwin" in _platform:
561  import glob
562  for pattern in patterns:
563  portlist += glob.glob(pattern)
564  else:
565  portlist = patterns
566 
567  for port in portlist:
568 
569  #print("Trying %s" % port)
570 
571  # create an uploader attached to the port
572  try:
573  if "linux" in _platform:
574  # Linux, don't open Mac OS and Win ports
575  if not "COM" in port and not "tty.usb" in port:
576  up = uploader(port, args.baud)
577  elif "darwin" in _platform:
578  # OS X, don't open Windows and Linux ports
579  if not "COM" in port and not "ACM" in port:
580  up = uploader(port, args.baud)
581  elif "win" in _platform:
582  # Windows, don't open POSIX ports
583  if not "/" in port:
584  up = uploader(port, args.baud)
585  except Exception:
586  # open failed, rate-limit our attempts
587  time.sleep(0.05)
588 
589  # and loop to the next port
590  continue
591 
592  # port is open, try talking to it
593  try:
594  # identify the bootloader
595  up.identify()
596  print("Found board %x,%x bootloader rev %x on %s" % (up.board_type, up.board_rev, up.bl_rev, port))
597 
598  except Exception:
599  # most probably a timeout talking to the port, no bootloader, try to reboot the board
600  print("attempting reboot on %s..." % port)
601  print("if the board does not respond, unplug and re-plug the USB connector.")
602  up.send_reboot()
603 
604  # wait for the reboot, without we might run into Serial I/O Error 5
605  time.sleep(0.5)
606 
607  # always close the port
608  up.close()
609  continue
610 
611  try:
612  # ok, we have a bootloader, try flashing it
613  up.upload(fw)
614 
615  except RuntimeError as ex:
616  # print the error
617  print("\nERROR: %s" % ex.args)
618 
619  except IOError as e:
620  up.close();
621  continue
622 
623  finally:
624  # always close the port
625  up.close()
626 
627  # we could loop here if we wanted to wait for more boards...
628  sys.exit(0)
629 
630  # Delay retries to < 20 Hz to prevent spin-lock from hogging the CPU
631  time.sleep(0.05)
632 
633 # CTRL+C aborts the upload/spin-lock by interrupt mechanics
634 except KeyboardInterrupt:
635  print("\n Upload aborted by user.")
636  sys.exit(0)
static int print(char **out, const char *format, va_list args)
uint8_t bytes[2]
Definition: storm32bgc.c:156