|
1 | 1 | import asyncio |
2 | | -import asyncssh |
3 | | -import os |
4 | | -from .ble import BLEConnection |
5 | | -from .usbconnection import USBConnection |
6 | | -from .compile import compile_file |
7 | | -import json |
8 | | -import random |
9 | 2 | import base64 |
| 3 | +import json |
10 | 4 | import logging |
| 5 | +import os |
| 6 | +import random |
11 | 7 |
|
| 8 | +import asyncssh |
| 9 | +from bleak.backends.device import BLEDevice |
12 | 10 | from bleak import BleakClient |
| 11 | +import semver |
| 12 | + |
| 13 | +from .ble import BLEConnection |
| 14 | +from .compile import compile_file |
| 15 | +from .usbconnection import USBConnection |
13 | 16 |
|
14 | 17 |
|
15 | 18 | class CharacterGlue(): |
@@ -554,8 +557,21 @@ async def get(self, remote_path, local_path=None): |
554 | 557 | ) |
555 | 558 |
|
556 | 559 |
|
| 560 | +# Pybricks control characteristic UUID |
557 | 561 | PYBRICKS_UUID = 'c5f50002-8280-46da-89f4-6d8051e4aeef' |
| 562 | +# The minimum required Pybricks protocol version |
| 563 | +PYBRICKS_PROTOCOL_VERSION = semver.VersionInfo(1) |
| 564 | + |
| 565 | +# Standard Device Information Service UUID |
| 566 | +DI_SERVICE_UUID = '0000180a-0000-1000-8000-00805f9b34fb' |
| 567 | +# Standard Firmware Revision String characteristic UUID |
| 568 | +FW_REV_UUID = '00002a26-0000-1000-8000-00805f9b34fb' |
| 569 | +# Standard Software Revision String UUID (Pybricks protocol version) |
| 570 | +SW_REV_UUID = '00002a28-0000-1000-8000-00805f9b34fb' |
| 571 | + |
| 572 | +# Nordic UART hub Rx, pybricksdev Tx characteristic |
558 | 573 | NUS_RX_UUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e' |
| 574 | +# Nordic UART hub Tx, pybricksdev Rx characteristic |
559 | 575 | NUS_TX_UUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e' |
560 | 576 |
|
561 | 577 |
|
@@ -649,23 +665,45 @@ def pybricks_service_handler(self, _: int, data: bytearray): |
649 | 665 | if not program_running_now: |
650 | 666 | self.user_program_stopped.set() |
651 | 667 |
|
652 | | - def disconnected_handler(self, client: BleakClient): |
653 | | - self.logger.info("Disconnected!") |
| 668 | + async def connect(self, device: BLEDevice): |
| 669 | + """Connects to a device that was discovered with :meth:`pybricksdev.ble.find_device` |
| 670 | +
|
| 671 | + Args: |
| 672 | + device: The device to connect to. |
654 | 673 |
|
655 | | - async def connect(self, device): |
656 | | - self.logger.info("Connecting to " + device.address) |
| 674 | + Raises: |
| 675 | + BleakError: if connecting failed (or old firmware without Device |
| 676 | + Information Service) |
| 677 | + RuntimeError: if Pybricks Protocol version is not supported |
| 678 | + """ |
| 679 | + self.logger.info(f"Connecting to {device.address}") |
657 | 680 | self.client = BleakClient(device) |
658 | | - await self.client.connect(disconnected_callback=self.disconnected_handler) |
659 | | - await self.client.start_notify(NUS_TX_UUID, self.nus_handler) |
660 | | - await self.client.start_notify(PYBRICKS_UUID, self.pybricks_service_handler) |
661 | | - self.logger.info("Connected successfully!") |
662 | | - self.connected = True |
| 681 | + |
| 682 | + def disconnected_handler(self, _: BleakClient): |
| 683 | + self.logger.info("Disconnected!") |
| 684 | + self.connected = False |
| 685 | + |
| 686 | + await self.client.connect(disconnected_callback=disconnected_handler) |
| 687 | + try: |
| 688 | + self.logger.info("Connected successfully!") |
| 689 | + protocol_version = await self.client.read_gatt_char(SW_REV_UUID) |
| 690 | + protocol_version = semver.VersionInfo.parse(protocol_version.decode()) |
| 691 | + if ( |
| 692 | + protocol_version < PYBRICKS_PROTOCOL_VERSION or |
| 693 | + protocol_version >= PYBRICKS_PROTOCOL_VERSION.bump_major() |
| 694 | + ): |
| 695 | + raise RuntimeError(f"Unsupported Pybricks protocol version: {protocol_version}") |
| 696 | + await self.client.start_notify(NUS_TX_UUID, self.nus_handler) |
| 697 | + await self.client.start_notify(PYBRICKS_UUID, self.pybricks_service_handler) |
| 698 | + self.connected = True |
| 699 | + except BaseException as ex: |
| 700 | + self.disconnect() |
| 701 | + raise ex |
663 | 702 |
|
664 | 703 | async def disconnect(self): |
665 | 704 | if self.connected: |
666 | 705 | self.logger.info("Disconnecting...") |
667 | 706 | await self.client.disconnect() |
668 | | - self.connected = False |
669 | 707 | else: |
670 | 708 | self.logger.debug("already disconnected") |
671 | 709 |
|
|
0 commit comments