From 8f162ddbf95ea530e3c6b278c989145b0d05eb4c Mon Sep 17 00:00:00 2001 From: Morgan Leborgne Date: Mon, 9 Nov 2020 21:49:42 +0100 Subject: [PATCH] Add a firmware upgrade utility. --- CMakeLists.txt | 1 + README.md | 4 ++ firmwares/CMakeLists.txt | 10 +++ firmwares/FirmwareUpgradeUtility.py | 97 +++++++++++++++++++++++++++++ firmwares/README.md | 16 +---- firmwares/upgrade_firmware.py | 63 +++++++++++++++++++ python/CMakeLists.txt | 2 +- 7 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 firmwares/CMakeLists.txt create mode 100644 firmwares/FirmwareUpgradeUtility.py create mode 100644 firmwares/upgrade_firmware.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 2362d7c..5788c49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ execute_process( message(STATUS "VERSION: ${MAESTRO_VERSION}") add_subdirectory(src) +add_subdirectory(firmwares) if(PYTHON_BINDING) add_subdirectory(python) diff --git a/README.md b/README.md index 2089c4a..b15f75b 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ The supported devices are: - Mini Maestro 18-Channel USB Servo Controller (#1354, #1355) - Mini Maestro 24-Channel USB Servo Controller (#1356, #1357) +## Upgrading Firmware + +See the firmwares subdirectory for the instructions. + ## Prerequisites **On all platforms** diff --git a/firmwares/CMakeLists.txt b/firmwares/CMakeLists.txt new file mode 100644 index 0000000..380c182 --- /dev/null +++ b/firmwares/CMakeLists.txt @@ -0,0 +1,10 @@ + + +install(FILES + README.md + FirmwareUpgradeUtility.py + usc02a_v1.04.pgm + usc03a_v1.03.pgm + usc03b_v1.03.pgm + usc03c_v1.03.pgm + DESTINATION firmwares) diff --git a/firmwares/FirmwareUpgradeUtility.py b/firmwares/FirmwareUpgradeUtility.py new file mode 100644 index 0000000..b557459 --- /dev/null +++ b/firmwares/FirmwareUpgradeUtility.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python + +import sys +import os +import time + +try: + import serial +except ImportError: + print(f'Pyserial is not installed for {sys.executable}.') + raise + +try: + import serial.tools.list_ports as list_ports +except ImportError: + print(f'The installed version ({sys.VERSION}) of pyserial appears to be too old (Python interpreter {sys.executable}).') + raise + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def find_maestro_devices(): + devices = [] + for info in list_ports.comports(include_links=False): + port, desc, hwid = info + if 'Pololu' and 'Bootloader' in desc: + devices.append(port) + return sorted(devices) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def progress(count, total, status=''): + bar_len = 60 + filled_len = int(round(bar_len * count / float(total))) + + percents = round(100.0 * count / float(total), 1) + bar = '=' * filled_len + ' ' * (bar_len - filled_len) + + sys.stdout.write('[%s] %s%s %s\r' % (bar, percents, '%', status)) + sys.stdout.flush() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def flash(firmware, serialPort): + # handshake + text=b'fwbootload' + serialPort.write(text) + answer = serialPort.read(len(text)) + if answer != text.upper(): + sys.exit(f'Expected {text.upper()} got {answer}') + + print('Connected to Maestro bootloader.') + + print('Erasing existing Maestro firmware...') + serialPort.write(b's') + answer = serialPort.read() + + if answer != b'S': + print(f"There was error erasing the old firmware. Expected response 'S' from device but received response {answer}") + raise + + print(f"Uploading new firmware...") + Position=0 + while Position < len(firmware): + serialPort.flush() + serialPort.write(firmware[Position:Position+1000]) + Position+=min(1000, len(firmware) - Position) + progress(Position, len(firmware)) + + time.sleep(0.2) + answer=serialPort.read(serialPort.in_waiting) + + if (len(answer) == 0 or answer[-1] != 124) and serialPort.read() != 124: + print(f"Expected to receive the '|' character, but did not.") + + print(f"\nUpload completed successfully.") + serialPort.write(b'*') + time.sleep(0.2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def main(argv): + print(f'Pololu Maestro firmware upgrade utility\n') + if len(argv) != 2: + sys.exit(f'Usage: {argv[0]} firmware.pgm\n') + + with open(argv[1], mode='rb') as file: + firmware = file.read() + + devices = find_maestro_devices() + + if len(devices) == 0: + sys.exit(f'No Maestro devices in bootloader mode connected.') + if len(devices) > 1: + sys.exit(f'More than one Maestro devices in bootloader mode connected.') + + with serial.Serial(devices[0]) as serialPort: + flash(firmware, serialPort) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +if __name__ == '__main__': + main(sys.argv) diff --git a/firmwares/README.md b/firmwares/README.md index 30dd689..60f058b 100644 --- a/firmwares/README.md +++ b/firmwares/README.md @@ -9,17 +9,7 @@ You can determine the version of your Maestro’s firmware by running the Maestr All of your settings will be reset to default values during the firmware upgrade. 1. Determine which type of Maestro you have either by counting the number of channels or by looking at the name that appears in your Device Manager. There are four types of Maestro: the Micro Maestro 6-Channel USB Servo Controller (usc02a), the Mini Maestro 12-Channel USB Servo Controller (usc03a), the Mini Maestro 18-Channel USB Servo Controller (usc03b), and the Mini Maestro 24-Channel USB Servo Controller (usc03c). -2. Connect your Maestro to a Windows or Linux computer using a USB cable. -3. Run the Maestro Control Center and connect to the Maestro by selecting its serial number in the “Connected to:” drop-down box in the upper left corner. -4. If you were not able to connect to the Maestro using the Maestro Control Center, double-check your USB connection, make sure all other devices are disconnected from the Maestro, and try plugging it into several different USB ports on your computer. If you are still unable to connect to it, see the instructions in Section 4.f.1 for doing a hard bootloader reset. -5. Go to the Device menu and select “Upgrade firmware…”. You will see a message asking you if you are sure you want to proceed: click OK. -6. If you are using Windows XP and see a Found New Hardware Wizard window appear, then you should follow steps 6–8 from Section 3.a to get the bootloader’s driver working. -7. Once the Maestro is in bootloader mode and the bootloader’s drivers are properly installed, the green LED should be blinking in a double heart-beat pattern, and there should be an entry for the bootloader in the “Ports (COM & LPT)” list of your computer’s Device Manager. -8. Go to the window entitled “Firmware Upgrade” that the Maestro Control Center has opened. -9. Click the “Browse…” button and select the firmware file you downloaded. Make sure that the selected file is the right file for your type of Maestro (see steps 2 and 3). -10. Select the COM port corresponding to the bootloader. If you do not know which COM port to select, go to the Device Manager and look in the “Ports (COM & LPT)” section. -11. Click the “Program” button. You will see a message warning you that your device’s firmware is about to be erased and asking you if you are sure you want to proceed: click Yes. -12. It will take a few seconds to erase the Maestro’s existing firmware and load the new firmware. Do not disconnect the Maestro during the upgrade. -13. Once the upgrade is complete, the Firmware Upgrade window will close, the Maestro will disconnect from your computer, and it will reappear. If there is only one Maestro plugged in to your computer, the Maestro Control Center will connect to it. Check the firmware version number and make sure that it now indicates the latest version of the firmware. +2. Start your Maestro in bootloader mode (see Section 4.f.1 from the maestro.pdf). +3. Run the FirmwareUpgradeUtility.py python script -If you have problems during or after the firmware upgrade, then it is possible that you loaded the wrong firmware onto your Maestro or some other problem corrupted the firmware. The solution is to retry the firmware upgrade procedure above. Even if your Maestro is not recognized at all by your computer and you see no sign of life from it, the instructions in step 4 can help you get the Maestro into bootloader mode. \ No newline at end of file +If you have problems during or after the firmware upgrade, then it is possible that you loaded the wrong firmware onto your Maestro or some other problem corrupted the firmware. The solution is to retry the firmware upgrade procedure above. \ No newline at end of file diff --git a/firmwares/upgrade_firmware.py b/firmwares/upgrade_firmware.py new file mode 100644 index 0000000..efe0a9c --- /dev/null +++ b/firmwares/upgrade_firmware.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +import sys +import os +import time +import serial +import maestro + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# chose an implementation, depending on os +#~ if sys.platform == 'cli': +#~ else: +if os.name == 'nt': # sys.platform == 'win32': + from serial.tools.list_ports_windows import comports +elif os.name == 'posix': + from serial.tools.list_ports_posix import comports +#~ elif os.name == 'java': +else: + raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name)) +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +def pololu_devices(): + for info in comports(include_links=False): + port, desc, hwid = info + if 'Pololu' in desc: + yield info +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +devices = maestro.getConnectedDevices() + +if len(devices) == 0: + sys.exit('No Maestro devices connected') + +print("Pololu Maestro firmware upgrade") +for i,device in enumerate(devices): + print(f"{i}: {device.getName()}") +num = input("Enter device number to upgrade: ") + +if not num.isdigit() or not 0 <= int(num) < len(devices): + sys.exit('wrong input') + +print("Restarting bootloader...") +devices[int(num)].startBootloader() +time.sleep(3) + +devices = pololu_devices() +if len(devices) == 0: + sys.exit('No Maestro devices in bootloader mode detected') + +for i,(port, desc, hwid) in enumerate(devices): + print(f"{i}: {port}") + print(f" desc: {desc}") + print(f" hwid: {hwid}") + +num = input("Enter device number to upgrade: ") +if not num.isdigit() or not 0 <= int(num) < len(devices): + sys.exit('wrong input') + +ser = serial.Serial(devices[int(num)].port) +print(ser.name) # check which port was really used +ser.write(b'fwbootload') # write a string +line = ser.readline() +print(line) +ser.close() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 49ebb63..b8b1ca3 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -8,7 +8,7 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(pybind11) -pybind11_add_module(pymaestro maestro.cpp) +pybind11_add_module(pymaestro maestro.cpp example.py) target_link_libraries(pymaestro PRIVATE maestro) set_target_properties(pymaestro PROPERTIES FOLDER "python") set_target_properties(pymaestro PROPERTIES OUTPUT_NAME "maestro")