Skip to content

Commit edb4a1e

Browse files
committed
pybricks.connections.pybricks: split out download_user_program()
This splits out code from the run() method to make is possible to download a user program without starting it right away. Fixes: pybricks/support#284
1 parent abd6b66 commit edb4a1e

File tree

2 files changed

+65
-43
lines changed

2 files changed

+65
-43
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Added
10+
- Added `PybricksHub.download_user_program()` method ([support#284]).
11+
12+
[support#284]: https://github.com/pybricks/support/issues/284
13+
914
## [1.0.0-alpha.45] - 2023-04-21
1015

1116
### Added
@@ -14,14 +19,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1419
### Fixed
1520
- Fixed endline in `PybricksHub.write_line()`.
1621

17-
[support#1038]: https://github.com/orgs/pybricks/discussions/1038
22+
[support#1038]: https://github.com/pybricks/support/issues/1038
1823

1924
## [1.0.0-alpha.44] - 2023-04-20
2025

2126
### Fixed
2227
- Restored `PybricksHub.output` attribute ([support#1037]).
2328

24-
[support#1037]: https://github.com/orgs/pybricks/discussions/1037
29+
[support#1037]: https://github.com/pybricks/support/issues/1037
2530

2631
## [1.0.0-alpha.43] - 2023-04-19
2732

pybricksdev/connections/pybricks.py

Lines changed: 58 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,63 @@ async def read_line(self) -> str:
428428

429429
return await self.race_disconnect(self._stdout_line_queue.get())
430430

431+
async def download_user_program(self, program: bytes) -> None:
432+
"""
433+
Downloads user program to user RAM on the hub and indicates progress
434+
using tqdm.
435+
436+
This is a somewhat low-level function. It verifies that the size of the
437+
program is not too big but it does not verify that the program data
438+
is valid or can be run on the hub. Also see :meth:`run`.
439+
440+
Requires hub with Pybricks Profile >= v1.2.0.
441+
442+
Args:
443+
program: The raw program data.
444+
445+
Raises:
446+
ValueError: if program is too large to fit on the hub
447+
"""
448+
# the hub tells us the max size of program that is allowed, so we can fail early
449+
if len(program) > self._max_user_program_size:
450+
raise ValueError(
451+
f"program is too big ({len(program)} bytes). Hub has limit of {self._max_user_program_size} bytes."
452+
)
453+
454+
# clear user program meta so hub doesn't try to run invalid program
455+
await self.client.write_gatt_char(
456+
PYBRICKS_COMMAND_EVENT_UUID,
457+
struct.pack("<BI", Command.WRITE_USER_PROGRAM_META, 0),
458+
response=True,
459+
)
460+
461+
# payload is max size minus header size
462+
payload_size = self._max_write_size - 5
463+
464+
# write program data with progress bar
465+
with logging_redirect_tqdm(), tqdm(
466+
total=len(program), unit="B", unit_scale=True
467+
) as pbar:
468+
for i, c in enumerate(chunk(program, payload_size)):
469+
await self.client.write_gatt_char(
470+
PYBRICKS_COMMAND_EVENT_UUID,
471+
struct.pack(
472+
f"<BI{len(c)}s",
473+
Command.COMMAND_WRITE_USER_RAM,
474+
i * payload_size,
475+
c,
476+
),
477+
response=True,
478+
)
479+
pbar.update(len(c))
480+
481+
# set the metadata to notify that writing was successful
482+
await self.client.write_gatt_char(
483+
PYBRICKS_COMMAND_EVENT_UUID,
484+
struct.pack("<BI", Command.WRITE_USER_PROGRAM_META, len(program)),
485+
response=True,
486+
)
487+
431488
async def start_user_program(self) -> None:
432489
"""
433490
Starts the user program that is already in RAM on the hub.
@@ -491,47 +548,7 @@ async def run(
491548

492549
mpy = await compile_multi_file(py_path, 6)
493550

494-
# the hub also tells us the max size of program that is allowed, so we can fail early
495-
if len(mpy) > self._max_user_program_size:
496-
raise ValueError(
497-
f"Compiled program is too big ({len(mpy)} bytes). Hub has limit of {self._max_user_program_size} bytes."
498-
)
499-
500-
# clear user program meta so hub doesn't try to run invalid program
501-
await self.client.write_gatt_char(
502-
PYBRICKS_COMMAND_EVENT_UUID,
503-
struct.pack("<BI", Command.WRITE_USER_PROGRAM_META, 0),
504-
response=True,
505-
)
506-
507-
# payload is max size minus header size
508-
payload_size = self._max_write_size - 5
509-
510-
# write program data with progress bar
511-
with logging_redirect_tqdm(), tqdm(
512-
total=len(mpy), unit="B", unit_scale=True
513-
) as pbar:
514-
for i, c in enumerate(chunk(mpy, payload_size)):
515-
await self.client.write_gatt_char(
516-
PYBRICKS_COMMAND_EVENT_UUID,
517-
struct.pack(
518-
f"<BI{len(c)}s",
519-
Command.COMMAND_WRITE_USER_RAM,
520-
i * payload_size,
521-
c,
522-
),
523-
response=True,
524-
)
525-
pbar.update(len(c))
526-
527-
# set the metadata to notify that writing was successful
528-
await self.client.write_gatt_char(
529-
PYBRICKS_COMMAND_EVENT_UUID,
530-
struct.pack("<BI", Command.WRITE_USER_PROGRAM_META, len(mpy)),
531-
response=True,
532-
)
533-
534-
# now we can run the program
551+
await self.download_user_program(mpy)
535552
await self.start_user_program()
536553

537554
if wait:

0 commit comments

Comments
 (0)