Skip to content

Commit

Permalink
px_uploader.py: clean up various tidbits
Browse files Browse the repository at this point in the history
Includes:
- Remove some of the outdated Python2 checks and compatibility.
- Try not catch all exceptions but only the expected ones. Otherwise,
  this makes it really hard to debug if anything unexpected actually
  goes wrong.
- Make use of fstrings.
- Make output slightly prettier.

Signed-off-by: Julian Oes <[email protected]>
  • Loading branch information
julianoes committed May 10, 2024
1 parent 03eb3ef commit 13f2ca2
Showing 1 changed file with 40 additions and 46 deletions.
86 changes: 40 additions & 46 deletions Tools/px_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@
# Currently only used for informational purposes.
#

# for python2.7 compatibility
from __future__ import print_function

import sys
import argparse
import binascii
Expand All @@ -70,35 +67,32 @@
try:
import serial
except ImportError as e:
print("Failed to import serial: " + str(e))
print(f"Failed to import serial: {e}")
print("")
print("You may need to install it using:")
print(" pip3 install --user pyserial")
print(" python -m pip install pyserial")
print("")
sys.exit(1)


# Define time to use time.time() by default
def _time():
return time.time()

# Detect python version
if sys.version_info[0] < 3:
runningPython3 = False
else:
runningPython3 = True
if sys.version_info[1] >=3:
# redefine to use monotonic time when available
def _time():
try:
return time.monotonic()
except Exception:
return time.time()
raise RuntimeError("Python 2 is not supported. Please try again using Python 3.")
sys.exit(1)


# Use monotonic time where available
def _time():
try:
return time.monotonic()
except Exception:
return time.time()

class FirmwareNotSuitableException(Exception):
def __init__(self, message):
super(FirmwareNotSuitableException, self).__init__(message)


class firmware(object):
'''Loads a firmware file'''

Expand Down Expand Up @@ -163,13 +157,13 @@ def __crc32(self, bytes, state):

def crc(self, padlen):
state = self.__crc32(self.image, int(0))
for i in range(len(self.image), (padlen - 1), 4):
for _ in range(len(self.image), (padlen - 1), 4):
state = self.__crc32(self.crcpad, state)
return state


class uploader(object):
'''Uploads a firmware file to the PX FMU bootloader'''
class uploader:
'''Uploads a firmware file to the PX4 bootloader'''

# protocol bytes
INSYNC = b'\x12'
Expand Down Expand Up @@ -361,19 +355,22 @@ def __determineInterface(self):
self.port.baudrate = self.baudrate_bootloader * 2.33
except NotImplementedError as e:
# This error can occur because pySerial on Windows does not support odd baudrates
print(str(e) + " -> could not check for FTDI device, assuming USB connection")
print(f"{e} -> could not check for FTDI device, assuming USB connection")
return

self.__send(uploader.GET_SYNC +
uploader.EOC)
try:
self.__getSync(False)
except:
except RuntimeError:
# if it fails we are on a real serial port - only leave this enabled on Windows
if sys.platform.startswith('win'):
self.ackWindowedMode = True
finally:
self.port.baudrate = self.baudrate_bootloader
try:
self.port.baudrate = self.baudrate_bootloader
except Exception:
pass

# send the GET_DEVICE command and wait for an info parameter
def __getInfo(self, param):
Expand Down Expand Up @@ -423,8 +420,7 @@ def __getVersion(self):
except RuntimeError:
# Bootloader doesn't support version call
return "unknown"
pieces = value.split(b".")
return pieces
return value.decode()

def __drawProgressBar(self, label, progress, maxVal):
if maxVal < progress:
Expand All @@ -437,7 +433,7 @@ def __drawProgressBar(self, label, progress, maxVal):

# send the CHIP_ERASE command and wait for the bootloader to become ready
def __erase(self, label):
print("Windowed mode: %s" % self.ackWindowedMode)
print(f"Windowed mode: {self.ackWindowedMode}")
print("\n", end='')

if self.force_erase:
Expand Down Expand Up @@ -672,14 +668,14 @@ def upload(self, fw_list, force=False, boot_delay=None, boot_check=False, force_
self.otp_coa = self.otp[32:160]
# show user:
try:
print("sn: ", end='')
print("Sn: ", end='')
for byte in range(0, 12, 4):
x = self.__getSN(byte)
x = x[::-1] # reverse the bytes
self.sn = self.sn + x
print(binascii.hexlify(x).decode('Latin-1'), end='') # show user
print('')
print("chip: %08x" % self.__getCHIP())
print("Chip: %08x" % self.__getCHIP())

otp_id = self.otp_id.decode('Latin-1')
if ("PX4" in otp_id):
Expand All @@ -689,17 +685,19 @@ def upload(self, fw_list, force=False, boot_delay=None, boot_check=False, force_
print("OTP pid: " + binascii.hexlify(self.otp_pid).decode('Latin-1'))
print("OTP coa: " + binascii.b2a_base64(self.otp_coa).decode('Latin-1'))

except Exception:
except Exception as e:
# ignore bad character encodings
print(f"Exception ignored: {e}")
pass

# Silicon errata check was added in v5
if (self.bl_rev >= 5):
des = self.__getCHIPDes()
if (len(des) == 2):
print("family: %s" % des[0])
print("revision: %s" % des[1])
print("flash: %d bytes" % self.fw_maxsize)
family, revision = des
print(f"Family: {family.decode()}")
print(f"Revision: {revision.decode()}")
print(f"Flash: {self.fw_maxsize} bytes")

# Prevent uploads where the maximum image size of the board config is smaller than the flash
# of the board. This is a hint the user chose the wrong config and will lack features
Expand All @@ -710,8 +708,7 @@ def upload(self, fw_list, force=False, boot_delay=None, boot_check=False, force_
# https://github.com/PX4/Firmware/blob/master/src/drivers/boards/common/stm32/board_mcu_version.c#L125-L144

if self.fw_maxsize > fw.property('image_maxsize') and not force:
raise RuntimeError("Board can accept larger flash images (%u bytes) than board config (%u bytes). Please use the correct board configuration to avoid lacking critical functionality."
% (self.fw_maxsize, fw.property('image_maxsize')))
raise RuntimeError(f"Board can accept larger flash images ({self.fw_maxsize} bytes) than board config ({fw.property('image_maxsize')} bytes). Please use the correct board configuration to avoid lacking critical functionality.")
else:
# If we're still on bootloader v4 on a Pixhawk, we don't know if we
# have the silicon errata and therefore need to flash px4_fmu-v2
Expand Down Expand Up @@ -812,10 +809,6 @@ def send_reboot(self, use_protocol_splitter_format=False):


def main():
# Python2 is EOL
if not runningPython3:
raise RuntimeError("Python 2 is not supported. Please try again using Python 3.")

# Parse commandline arguments
parser = argparse.ArgumentParser(description="Firmware uploader for the PX autopilot system.")
parser.add_argument('--port', action="store", required=True, help="Comma-separated list of serial port(s) to which the FMU may be attached")
Expand Down Expand Up @@ -900,9 +893,10 @@ def main():
# Windows, don't open POSIX ports
if "/" not in port:
up = uploader(port, args.baud_bootloader, baud_flightstack)
except Exception:
except Exception as e:
# open failed, rate-limit our attempts
time.sleep(0.05)
print(f"Exception ignored: {e}")

# and loop to the next port
continue
Expand All @@ -917,10 +911,10 @@ def main():
up.identify()
found_bootloader = True
print()
print("Found board id: %s,%s bootloader version: %s on %s" % (up.board_type, up.board_rev, up.bl_rev, port))
print(f"Found board id: {up.board_type},{up.board_rev} bootloader protocol revision {up.bl_rev} on {port}")
break

except Exception:
except RuntimeError or serial.SerialException:

if not up.send_reboot(args.use_protocol_splitter_format):
break
Expand All @@ -945,9 +939,9 @@ def main():
# if we made this far without raising exceptions, the upload was successful
successful = True

except RuntimeError as ex:
except RuntimeError as e:
# print the error
print("\nERROR: %s" % ex.args)
print(f"\n\nError: {e}")

except FirmwareNotSuitableException:
unsuitable_board = True
Expand Down Expand Up @@ -986,4 +980,4 @@ def main():
if __name__ == '__main__':
main()

# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4

0 comments on commit 13f2ca2

Please sign in to comment.