Skip to content

Commit 709cf90

Browse files
committed
connections: check Pybricks protocol version
This adds a check to verify the Pybricks protocol version when connecting to a Bluetooth Low Energy device. Also add some comments and sort imports while we are touching this code.
1 parent ca94b4c commit 709cf90

File tree

4 files changed

+71
-18
lines changed

4 files changed

+71
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

77
## [Unreleased]
8+
### Added
9+
- Check Pybricks protocol version when connecting to Bluetooth Low Energy devices.
810
### Fixed
911
- Fix running programs via Bluetooth Low Energy.
1012

poetry.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pybricksdev/connections.py

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
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
92
import base64
3+
import json
104
import logging
5+
import os
6+
import random
117

8+
import asyncssh
9+
from bleak.backends.device import BLEDevice
1210
from bleak import BleakClient
11+
import semver
12+
13+
from .ble import BLEConnection
14+
from .compile import compile_file
15+
from .usbconnection import USBConnection
1316

1417

1518
class CharacterGlue():
@@ -554,8 +557,21 @@ async def get(self, remote_path, local_path=None):
554557
)
555558

556559

560+
# Pybricks control characteristic UUID
557561
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
558573
NUS_RX_UUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'
574+
# Nordic UART hub Tx, pybricksdev Rx characteristic
559575
NUS_TX_UUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'
560576

561577

@@ -649,23 +665,45 @@ def pybricks_service_handler(self, _: int, data: bytearray):
649665
if not program_running_now:
650666
self.user_program_stopped.set()
651667

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.
654673
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}")
657680
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
663702

664703
async def disconnect(self):
665704
if self.connected:
666705
self.logger.info("Disconnecting...")
667706
await self.client.disconnect()
668-
self.connected = False
669707
else:
670708
self.logger.debug("already disconnected")
671709

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ python = "~3.8"
3131
tqdm = "^4.46.1"
3232
validators = "^0.18.2"
3333
pyusb = "^1.0.2"
34+
semver = "^2.13.0"
3435

3536
[tool.poetry.dev-dependencies]
3637
flake8 = "^3.8.3"

0 commit comments

Comments
 (0)