| #!/usr/bin/env python3 | |
| import argparse | |
| import serial | |
| import serial.tools.list_ports | |
| import sys | |
| import os | |
| import os.path | |
| import time | |
| import threading | |
| from tqdm import tqdm | |
| parser = argparse.ArgumentParser(description='32blit binary flasher.') | |
| parser.add_argument("command", help="Command to perform (e.g. 'save', 'dfu', 'reset')", default="save") | |
| parser.add_argument("port", help="Name of the serial port (e.g. 'COM4', '/dev/ttyS4', etc.)") | |
| parser.add_argument("--target", help="Target either 'sd' or 'flash'", default="sd", choices=["sd", "flash"]) | |
| parser.add_argument("--no-read", help="Do not wait to read responses", action="store_true") | |
| parser.add_argument("file", help="Path to the binary file (e.g. 'build.stm32/voxel.bin')", nargs='?') | |
| args = parser.parse_args() | |
| print("32blit binary flasher.") | |
| port = args.port | |
| command = args.command | |
| target = args.target | |
| valid_ports = [port[0] for port in serial.tools.list_ports.comports()] | |
| if port not in valid_ports: | |
| print("Specified port '{0}' not valid".format(port)) | |
| sys.exit(os.EX_OSFILE) | |
| serial_port = None | |
| try: | |
| serial_port = serial.Serial(port, 115200, timeout=1) | |
| serial_port.reset_output_buffer() | |
| except OSError: | |
| print("Could not open port '{0}'".format(port)) | |
| print("The port may not exist or you may not have permission to access it.") | |
| sys.exit(os.EX_OSFILE) | |
| if not args.no_read: | |
| # read thread constantly polls for responses from 32blit and | |
| # outputs them to the screen | |
| def read_serial(serial_port): | |
| while True: | |
| data = serial_port.read_until(b"\0").decode() | |
| if data: | |
| print(data) | |
| read_thread = threading.Thread(target=read_serial, args=(serial_port,)) | |
| read_thread.start() | |
| print(command) | |
| if command == "save": | |
| path = args.file | |
| if not os.path.isfile(path): | |
| print("Specified binary file '{0}' does not exist".format(path)) | |
| sys.exit(os.EX_NOINPUT) | |
| file_size = os.stat(path).st_size | |
| file_name = os.path.basename(path) | |
| # send the save command to 32blit | |
| data = "SAVE\x00{0}\x00{1}\x00".format(file_name, file_size) | |
| serial_port.write(bytes(data, "ascii")) | |
| # transmit the file data to 32blit | |
| sent_byte_count = 0 | |
| chunk_size = 64 | |
| with open(path, "rb") as f: | |
| progress = tqdm(total=file_size, desc="Flashing...", unit_scale=True, unit_divisor=1024, unit="B", ncols=70, dynamic_ncols=True) | |
| while sent_byte_count < file_size: | |
| # last chunk may be smaller than 64 bytes | |
| if file_size - sent_byte_count < chunk_size: | |
| chunk_size = file_size - sent_byte_count | |
| # read chunk from file | |
| data = f.read(chunk_size) | |
| # transmit it to 32blit | |
| serial_port.write(data) | |
| sent_byte_count += chunk_size | |
| # update progress bar | |
| progress.update(chunk_size) | |
| progress.close() | |
| if command == "dfu": | |
| # send the dfu command to 32blit | |
| data = "DFU\x00" | |
| serial_port.write(bytes(data, "ascii")) | |
| if command == "reset": | |
| # send the dfu command to 32blit | |
| data = "RESET\x00" | |
| serial_port.write(bytes(data, "ascii")) | |
| if not args.no_read: | |
| # wait for any responses | |
| print("Reading responses from 32blit. Press Ctrl+C to quit.") | |
| try: | |
| while True: | |
| pass | |
| except KeyboardInterrupt: | |
| read_thread.stop() | |
| time.sleep(1) |