4 Commits
dbus ... dbus2

Author SHA1 Message Date
Shin'ichiro Kawasaki
39ca1904b7 ble: implement BLEDBusSession class
Current implementation of BLESession class depends on the library bluepy
to communicate with BLE devices. This implementation has two major
issues. The first issue is no maintenance of bluepy library. It's last
commit was made in May 2021. It is no longer active and it is not likely
that its issues get resolved. The second issue is lack of asyncio
interface. pyscrlink uses websockets which uses asyncio. Dirty glue code
and locks are required to connect websockets and bluepy.

This commit avoids the issue by moving away from bluepy. Instead, use
BlueZ D-Bus API. Bluepy communicates with BlueZ kernel module via bluepy
unique user space program. Instead, pyscrlink communicate with well
maintained Blue-Z user space program, or bluetoothd, through D-Bus
protocol. To handle D-Bus protocol, use python-sdbus library [2]. It has
sub-library python-sdbus-bluez to cover BlueZ D-Bus API [3].

Using these libraries, implement a new class named BLEDBusSession. It
replaces BLESession which uses bluepy. These two classes share same
base class named Session. To allow async I/O, add a new function
async_handle_request to the Session class. Until this new implementation
get proved stable enough, keep the old BLESession. Introduce a new
command line option '-b' of scratch_link. When this option is specified,
the new class is used.

To improve code readability, implement BLEDBusSession in a new file
'ble.py'. To share scan_seconds between scratch_link.py and ble.py,
change scan_seconds from global variable to an argument of Session class
constructor. Also to share logger between scratch_link.py and ble.py,
modify the logging.getLogger() argument from __name__ to scratch_link
module name.

[1] http://www.bluez.org/bluez-5-api-introduction-and-porting-guide/
[2] https://github.com/python-sdbus/python-sdbus
[3] https://github.com/python-sdbus/python-sdbus-bluez

Signed-off-by: Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
2023-09-18 18:34:51 +09:00
Shin'ichiro Kawasaki
63345442ca introduce BTUUID class
As a preparation to move away from bluepy, introduce a new class BTUUID.
Bluepy provides its unique UUID class which handles UUIDs of Bluetooth
devices well. Instead of it, introduce BTUUID which extends python
standard uuid.UUID class and support Bluetooth devices.

Signed-off-by: Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
2023-09-18 18:16:20 +09:00
Shin'ichiro Kawasaki
ed4df282b1 Merge pull request #39 from rcy/master-1
Update README.md to correct micro:bit hex file download link.
2023-08-15 11:28:26 +09:00
Ryan Yeske
00154f2b51 Update README.md
Remove version number

Upstream url changed on https://scratch.mit.edu/microbit
2023-08-05 17:08:21 -07:00
3 changed files with 26 additions and 25 deletions

View File

@@ -102,7 +102,7 @@ Installation
5. For micro:bit, install Scratch-link hex on your device. 5. For micro:bit, install Scratch-link hex on your device.
* Download and unzip the [micro:bit Scratch Hex file](https://downloads.scratch.mit.edu/microbit/scratch-microbit-1.1.0.hex.zip). * Download and unzip the [micro:bit Scratch Hex file](https://downloads.scratch.mit.edu/microbit/scratch-microbit.hex.zip).
* Flash the micro:bit over USB with the Scratch Hex File, you will see the * Flash the micro:bit over USB with the Scratch Hex File, you will see the
five character name of the micro:bit scroll across the screen such as five character name of the micro:bit scroll across the screen such as
'zo9ev'. 'zo9ev'.

View File

@@ -34,8 +34,8 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
connected_devices = {} connected_devices = {}
class Device(): class Device():
def __init__(self, interface, path, node_name, name, address): def __init__(self, iface, path, node_name, name, address):
self.interface = interface self.iface = iface
self.path = path self.path = path
self.node_name = node_name self.node_name = node_name
self.name = name self.name = name
@@ -95,7 +95,7 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
logger.debug(f"service to check: {s}") logger.debug(f"service to check: {s}")
given_uuid = BTUUID(s) given_uuid = BTUUID(s)
logger.debug(f"given UUID: {given_uuid} hash={given_uuid.__hash__()}") logger.debug(f"given UUID: {given_uuid} hash={given_uuid.__hash__()}")
dev_uuids = await dev.interface.uuids dev_uuids = await dev.iface.uuids
if not dev_uuids: if not dev_uuids:
logger.debug(f"dev UUID not available") logger.debug(f"dev UUID not available")
continue continue
@@ -122,7 +122,7 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
async def _notify_device(self, device) -> None: async def _notify_device(self, device) -> None:
params = { 'rssi': -80, 'name': 'Unknown' } params = { 'rssi': -80, 'name': 'Unknown' }
try: try:
params['rssi'] = await device.interface.rssi params['rssi'] = await device.iface.rssi
except Exception: except Exception:
None None
if device.name: if device.name:
@@ -149,17 +149,17 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
devpath = self.iface + "/" + node_name devpath = self.iface + "/" + node_name
if BLEDBusSession.connected_devices.get(devpath): if BLEDBusSession.connected_devices.get(devpath):
continue continue
interface = DeviceInterfaceAsync() iface = DeviceInterfaceAsync()
interface._connect('org.bluez', devpath, bus=self.dbus) iface._connect('org.bluez', devpath, bus=self.dbus)
try: try:
devname = await interface.name devname = await iface.name
except Exception as e: except Exception as e:
logger.debug(f"device {node_name} does not have name: {e}") logger.debug(f"device {node_name} does not have name: {e}")
devaddr = await interface.address devaddr = await iface.address
device = self.Device(interface, devpath, node_name, devname, device = self.Device(iface, devpath, node_name, devname,
devaddr) devaddr)
if not await self._matches(device, self.discover_filters): if not await self._matches(device, self.discover_filters):
await interface.disconnect() await iface.disconnect()
continue continue
self.found_devices[node_name] = device self.found_devices[node_name] = device
await self._notify_device(device) await self._notify_device(device)
@@ -309,7 +309,7 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
dev = self.found_devices[params['peripheralId']] dev = self.found_devices[params['peripheralId']]
try: try:
logger.debug(f" {dev}") logger.debug(f" {dev}")
await dev.interface.connect() await dev.iface.connect()
res["result"] = None res["result"] = None
self.device = dev self.device = dev
self.status = self.CONNECTED self.status = self.CONNECTED
@@ -362,7 +362,7 @@ class BLEDBusSession(pyscrlink.scratch_link.Session):
dev = self.device dev = self.device
logger.info(f"Disconnecting from '{dev.name}'@{dev.address}") logger.info(f"Disconnecting from '{dev.name}'@{dev.address}")
self._stop_notifications() self._stop_notifications()
await dev.interface.disconnect() await dev.iface.disconnect()
BLEDBusSession.connected_devices.pop(dev.path) BLEDBusSession.connected_devices.pop(dev.path)
logger.info(f"Disconnected from '{dev.name}'@{dev.address}") logger.info(f"Disconnected from '{dev.name}'@{dev.address}")
self.device = None self.device = None

View File

@@ -33,17 +33,8 @@ from pyscrlink import gencert
from pyscrlink import ble from pyscrlink import ble
logLevel = logging.INFO
# for logging # for logging
logger = logging.getLogger('pyscrlink.scratch_link') logger = logging.getLogger('pyscrlink.scratch_link')
formatter = logging.Formatter(fmt='%(asctime)s %(message)s')
handler = logging.StreamHandler()
handler.setLevel(logLevel)
handler.setFormatter(formatter)
logger.setLevel(logLevel)
logger.addHandler(handler)
logger.propagate = False
HOSTNAME="device-manager.scratch.mit.edu" HOSTNAME="device-manager.scratch.mit.edu"
scan_seconds=10.0 scan_seconds=10.0
@@ -597,14 +588,24 @@ def main():
parser.add_argument('-b', '--dbus', action='store_true', parser.add_argument('-b', '--dbus', action='store_true',
help='use DBus backend for BLE devices') help='use DBus backend for BLE devices')
args = parser.parse_args() args = parser.parse_args()
logLevel = logging.INFO
if args.debug: if args.debug:
print("Print debug messages")
logLevel = logging.DEBUG logLevel = logging.DEBUG
handler.setLevel(logLevel)
logger.setLevel(logLevel) formatter = logging.Formatter(fmt='%(asctime)s %(message)s')
handler = logging.StreamHandler()
handler.setLevel(logLevel)
handler.setFormatter(formatter)
logger.setLevel(logLevel)
logger.addHandler(handler)
logger.propagate = False
scan_seconds = args.scan_seconds scan_seconds = args.scan_seconds
scan_retry = args.scan_retry scan_retry = args.scan_retry
dbus = args.dbus dbus = args.dbus
if args.debug:
logger.debug("Print debug messages")
logger.debug(f"set scan_seconds: {scan_seconds}") logger.debug(f"set scan_seconds: {scan_seconds}")
logger.debug(f"set scan_retry: {scan_retry}") logger.debug(f"set scan_retry: {scan_retry}")