|
1 | 1 | # SPDX-License-Identifier: MIT |
2 | 2 | # Copyright (c) 2020 Henrik Blidh |
3 | | -# Copyright (c) 2022 The Pybricks Authors |
| 3 | +# Copyright (c) 2022-2023 The Pybricks Authors |
4 | 4 |
|
5 | 5 | import asyncio |
| 6 | +from contextlib import suppress |
6 | 7 | from bleak import BleakScanner, BleakClient |
7 | 8 |
|
8 | 9 | UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" |
|
14 | 15 | HUB_NAME = "Pybricks Hub" |
15 | 16 |
|
16 | 17 |
|
17 | | -def hub_filter(device, ad): |
18 | | - return device.name and device.name.lower() == HUB_NAME.lower() |
| 18 | +async def main(): |
| 19 | + main_task = asyncio.current_task() |
19 | 20 |
|
| 21 | + def handle_disconnect(_): |
| 22 | + print("Hub was disconnected.") |
20 | 23 |
|
21 | | -def handle_disconnect(_): |
22 | | - print("Hub was disconnected.") |
| 24 | + # If the hub disconnects before this program is done, |
| 25 | + # cancel this program so it doesn't get stuck waiting |
| 26 | + # forever. |
| 27 | + if not main_task.done(): |
| 28 | + main_task.cancel() |
23 | 29 |
|
| 30 | + ready_event = asyncio.Event() |
24 | 31 |
|
25 | | -def handle_rx(_, data: bytearray): |
26 | | - print("Received:", data) |
| 32 | + def handle_rx(_, data: bytearray): |
| 33 | + if data == b"rdy": |
| 34 | + ready_event.set() |
| 35 | + else: |
| 36 | + print("Received:", data) |
27 | 37 |
|
| 38 | + # Do a Bluetooth scan to find the hub. |
| 39 | + device = await BleakScanner.find_device_by_name(HUB_NAME) |
28 | 40 |
|
29 | | -async def main(): |
30 | | - # Find the device and initialize client. |
31 | | - device = await BleakScanner.find_device_by_filter(hub_filter) |
32 | | - client = BleakClient(device, disconnected_callback=handle_disconnect) |
| 41 | + if device is None: |
| 42 | + print(f"could not find hub with name: {HUB_NAME}") |
| 43 | + return |
33 | 44 |
|
34 | | - # Shorthand for sending some data to the hub. |
35 | | - async def send(client, data): |
36 | | - await client.write_gatt_char(rx_char, data) |
| 45 | + # Connect to the hub. |
| 46 | + async with BleakClient(device, handle_disconnect) as client: |
37 | 47 |
|
38 | | - try: |
39 | | - # Connect and get services. |
40 | | - await client.connect() |
| 48 | + # Subscribe to notifications from the hub. |
41 | 49 | await client.start_notify(UART_TX_CHAR_UUID, handle_rx) |
42 | | - nus = client.services.get_service(UART_SERVICE_UUID) |
43 | | - rx_char = nus.get_characteristic(UART_RX_CHAR_UUID) |
| 50 | + |
| 51 | + # Shorthand for sending some data to the hub. |
| 52 | + async def send(data): |
| 53 | + # Wait for hub to say that it is ready to receive data. |
| 54 | + await ready_event.wait() |
| 55 | + # Prepare for the next ready event. |
| 56 | + ready_event.clear() |
| 57 | + # Send the data to the hub. |
| 58 | + await client.write_gatt_char(UART_RX_CHAR_UUID, data) |
44 | 59 |
|
45 | 60 | # Tell user to start program on the hub. |
46 | 61 | print("Start the program on the hub now with the button.") |
47 | 62 |
|
48 | 63 | # Send a few messages to the hub. |
49 | 64 | for i in range(5): |
50 | | - await send(client, b"fwd") |
| 65 | + await send(b"fwd") |
51 | 66 | await asyncio.sleep(1) |
52 | | - await send(client, b"rev") |
| 67 | + await send(b"rev") |
53 | 68 | await asyncio.sleep(1) |
| 69 | + print(".", end="", flush=True) |
54 | 70 |
|
55 | 71 | # Send a message to indicate stop. |
56 | | - await send(client, b"bye") |
| 72 | + await send(b"bye") |
| 73 | + |
| 74 | + print("done.") |
57 | 75 |
|
58 | | - except Exception as e: |
59 | | - # Handle exceptions. |
60 | | - print(e) |
61 | | - finally: |
62 | | - # Disconnect when we are done. |
63 | | - await client.disconnect() |
| 76 | + # Hub disconnects here when async with block exits. |
64 | 77 |
|
65 | 78 |
|
66 | 79 | # Run the main async program. |
67 | | -asyncio.run(main()) |
| 80 | +if __name__ == "__main__": |
| 81 | + with suppress(asyncio.CancelledError): |
| 82 | + asyncio.run(main()) |
0 commit comments