Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ options:
-q, --query Query Bootloader Device IDs
-v, --verbose Enable verbose responses
-r, --request-bootloader
Requests the bootloader and exits (CAN only)
Requests the bootloader and exits (CAN|Virtual serial|Physical serial))
```

### Can Programming
Expand All @@ -157,7 +157,9 @@ The `-f` option defaults to `~/klipper/out/klipper.bin` when omitted.

The `-d` option is required. The `-b` option defaults to `250000` if omitted.

### Request Bootloader (CAN Devices Only)
### Request Bootloader

#### CAN Devices

When the `-r` option is supplied in addition to `-u` (and optionally `-i`)
the script will request that the node enter the bootloader. The script will
Expand All @@ -168,6 +170,17 @@ option allows the user to enter the bootloader without physical access to the
board, then use the appropriate tool (`dfu-util` or `flashtool.py -d`) to
upload the new binary.

#### Virtual Serial and Physical Serial devices

For devices with Klipper installed when the `-r` option is supplied the script
will request that the device enter the bootloader. The script will then immediately
exit, no attempt will be made to upload a binary. After entering the bootloader,
you can query information about the bootloader by specifying the `-q` option.
The methods for entering the bootloader for both types of devices are
described [here](https://github.com/Klipper3d/klipper/blob/master/docs/Bootloader_Entry.md).
After that, you can run the utility again without the `-r` parameter to write
the firmware.

## Katapult Deployer

**WARNING**: Make absolutely sure your Katapult build configuration is
Expand Down
49 changes: 44 additions & 5 deletions scripts/flashtool.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def crc16_ccitt(buf: Union[bytes, bytearray]) -> int:
KLIPPER_SET_NODE_CMD = 0x01
KLIPPER_REBOOT_CMD = 0x02

# Klipper request bootloader command (physical serial)
KLIPPER_BL_REQ_CMD = b'~ \x1c Request Serial Bootloader!! ~\n'

# CAN Admin Defs
CANBUS_ID_ADMIN = 0x3f0
CANBUS_ID_ADMIN_RESP = 0x3f1
Expand Down Expand Up @@ -483,8 +486,7 @@ async def run(
raise FlashError("Unable to bind socket to can0")
self.closed = False
self.cansock.setblocking(False)
self._loop.add_reader(
self.cansock.fileno(), self._handle_can_response)
self._loop.add_reader(self.cansock.fileno(), self._handle_can_response)
self._jump_to_bootloader(uuid)
await asyncio.sleep(.5)
if req_only:
Expand Down Expand Up @@ -534,6 +536,15 @@ def __init__(self, loop: asyncio.AbstractEventLoop):
self.serial: Optional[Serial] = None
self.node = CanNode(0, self)

def _jump_to_bootloader(self):
output_line("Sending bootloader jump command...")
baudrate = self.serial.baudrate
self.serial.baudrate = 1200
self.serial.dtr = True
self.serial.dtr = False
self.serial.baudrate = baudrate
self.send(None, KLIPPER_BL_REQ_CMD)

def _handle_response(self) -> None:
assert self.serial is not None
try:
Expand Down Expand Up @@ -605,7 +616,7 @@ async def validate_device(self, dev_strpath: str) -> None:
)
raise FlashError(f"Serial device {dev_path} in use")

async def run(self, intf: str, baud: int, fw_path: pathlib.Path) -> None:
async def run(self, intf: str, baud: int, fw_path: pathlib.Path, req_only: bool) -> None:
if not fw_path.is_file():
raise FlashError("Invalid firmware path '%s'" % (fw_path))
await self.validate_device(intf)
Expand All @@ -619,6 +630,11 @@ async def run(self, intf: str, baud: int, fw_path: pathlib.Path) -> None:
raise FlashError("Unable to open serial port: %s" % (e,))
self.serial = serial_dev
self._loop.add_reader(self.serial.fileno(), self._handle_response)
await asyncio.sleep(.5)
if req_only:
self._jump_to_bootloader()
output_line("Bootloader request command sent")
return
flasher = CanFlasher(self.node, fw_path)
try:
await flasher.connect_btl()
Expand All @@ -630,6 +646,24 @@ async def run(self, intf: str, baud: int, fw_path: pathlib.Path) -> None:
# unless comms were broken
await flasher.finish()

async def run_query(self, intf: str, baud: int):
try:
serial_dev = Serial( # type: ignore
baudrate=baud, timeout=0, exclusive=True
)
serial_dev.port = intf
serial_dev.open()
except (OSError, IOError, SerialException) as e:
raise FlashError("Unable to open serial port: %s" % (e,))
self.serial = serial_dev
self._loop.add_reader(self.serial.fileno(), self._handle_response)
flasher = CanFlasher(self.node, None)
try:
await flasher.connect_btl()
except Exception:
self.close()
raise FlashError("Unable to connect to bootloader")

def close(self):
if self.serial is None:
return
Expand Down Expand Up @@ -675,7 +709,10 @@ async def main(args: argparse.Namespace) -> int:
)
output_line(f"Flashing Serial Device {args.device}, baud {args.baud}")
sock = SerialSocket(loop)
await sock.run(args.device, args.baud, fpath)
if args.query:
await sock.run_query(args.device, args.baud)
else:
await sock.run(args.device, args.baud, fpath, req_only)
except Exception:
logging.exception("Flash Error")
return -1
Expand All @@ -684,6 +721,8 @@ async def main(args: argparse.Namespace) -> int:
sock.close()
if args.query:
output_line("Query Complete")
elif args.request_bootloader:
output_line("Bootloader request complete")
else:
output_line("Flash Success")
return 0
Expand Down Expand Up @@ -722,7 +761,7 @@ async def main(args: argparse.Namespace) -> int:
)
parser.add_argument(
"-r", "--request-bootloader", action="store_true",
help="Requests the bootloader and exits (CAN only)"
help="Requests the bootloader and exits (CAN|Virtual serial|Physical serial)"
)

args = parser.parse_args()
Expand Down