diff --git a/py/tarpn/bbd/firmware_update.py b/py/tarpn/bbd/firmware_update.py index bf70618..4f63a0a 100644 --- a/py/tarpn/bbd/firmware_update.py +++ b/py/tarpn/bbd/firmware_update.py @@ -64,14 +64,17 @@ def start_agent_process(): assert check_agent_process(), "Started agent program, but not showing RUNNING" -def ensure_serial_port_free(): - p = subprocess.run(shlex.split("lsof /dev/ttyS0"), stdout=subprocess.PIPE) +def ensure_serial_port_free(device: str): + if not os.path.exists(device): + assert False, f"No such device {device}" + p = subprocess.run(shlex.split(f"lsof {device}"), stdout=subprocess.PIPE) if p.returncode == 1: + # lsof will return code 1 if nothing is using the device return else: lines = p.stdout.decode("ascii").split("\n") cols = re.split("\s+", lines[1]) - assert False, f"Process {cols[0]} (pid {cols[1]}) is using the serial port" + assert False, f"Process {cols[0]} (pid {cols[1]}) is using the serial port {device}" def reset_atmega(gpio: int) -> True: @@ -89,29 +92,32 @@ def reset_atmega(gpio: int) -> True: GPIO.output(gpio, GPIO.HIGH) -def upload_firmware(): +def upload_firmware(device: str): path = firmware_file() - p = subprocess.run(shlex.split(f"avrdude -v -p atmega328p -c arduino -b 38400 -D -P /dev/ttyS0 -U flash:w:{path}:i")) + p = subprocess.run(shlex.split(f"avrdude -v -p atmega328p -c arduino -b 38400 -D -P {device} -U flash:w:{path}:i")) assert p.returncode == 0, "Firmware upload failed!" def main(): parser = argparse.ArgumentParser(description="Firmware updater for TARPN BBD") - parser.add_argument("--gpio", type=int, default=7, help="Override the default GPIO used for Atmega reset (7).") + parser.add_argument("--gpio", type=int, default=7, + help="The GPIO pin used to reset the ATmega microcontroller. Defaults to pin 7 (GPIO 4).") + parser.add_argument("--device", type=str, default="/dev/ttyS0", + help="The serial device that the BBD is connected to. Defaults to hardware UART /dev/ttyS0") parser.add_argument("--dry-run", action="store_true", help="Perform a dry-run of the firmware update") - parser.add_argument("--reset-only", action="store_true", help="Only reset the atemga, do not update the firmware") + parser.add_argument("--reset-only", action="store_true", help="Only reset the ATmega, do not update the firmware") args = parser.parse_args() if args.reset_only: - check(partial(reset_atmega, args.gpio), "atmega is reset", skip=args.dry_run) + check(partial(reset_atmega, args.gpio), "ATmega is reset", skip=args.dry_run) exit(0) check(ensure_firmware_file, "firmware is present") check(ensure_supervisor, "supervisor is installed") check(stop_agent_process, "agent is not running", allow_fail=True) - check(ensure_serial_port_free, "serial port is unused") - check(partial(reset_atmega, args.gpio), "atmega is reset", skip=args.dry_run) - check(upload_firmware, "firmware is uploaded", skip=args.dry_run) + check(partial(ensure_serial_port_free, args.device), f"serial port {args.device} is unused") + check(partial(reset_atmega, args.gpio), "ATmega is reset", skip=args.dry_run) + check(partial(upload_firmware, args.device), "firmware is uploaded", skip=args.dry_run) check(start_agent_process, "agent is running")