micropython-lib/micropython/bluetooth/aioble
Matt Trentini 0c31e0b3d7 micropython/aioble: Prioritise services in advertisement payload.
A number of fields (services, appearance, manufacturer, name) can appear
when a scan is requested.  However there is only so much space in the
header so, if a user has configured multiple fields, some may be 'pushed'
into the active scan response which requires additional communication.

When iOS scans for BLE devices it can filter by services, and so services
must be in the advertising (as opposed to scan response) payload.
2022-03-24 11:25:31 +11:00
..
aioble micropython/aioble: Prioritise services in advertisement payload. 2022-03-24 11:25:31 +11:00
examples micropython/aioble: Add asyncio-based wrapper for ubluetooth. 2021-05-28 18:26:49 +10:00
multitests aioble/multitests: Add multitest for shutdown handlers. 2021-11-12 13:36:18 +11:00
README.md aioble/README.md: Add l2cap example. 2021-11-12 13:35:50 +11:00
manifest.py micropython/aioble: Add asyncio-based wrapper for ubluetooth. 2021-05-28 18:26:49 +10:00

README.md

aioble

This library provides an object-oriented, asyncio-based wrapper for MicroPython's ubluetooth API.

Note: aioble requires MicroPython v1.17 or higher.

Features

Broadcaster (advertiser) role:

  • Generate advertising and scan response payloads for common fields.
  • Automatically split payload over advertising and scan response.
  • Start advertising (indefinitely or for duration).

Peripheral role:

  • Wait for connection from central.
  • Wait for MTU exchange.

Observer (scanner) role:

  • Scan for devices (passive + active).
  • Combine advertising and scan response payloads for the same device.
  • Parse common fields from advertising payloads.

Central role:

  • Connect to peripheral.
  • Initiate MTU exchange.

GATT Client:

  • Discover services, characteristics, and descriptors (optionally by UUID).
  • Read / write / write-with-response characters and descriptors.
  • Subscribe to notifications and indications on characteristics (via the CCCD).
  • Wait for notifications and indications.

GATT Server:

  • Register services, characteristics, and descriptors.
  • Wait for writes on characteristics and descriptors.
  • Intercept read requests.
  • Send notifications and indications (and wait on response).

L2CAP:

  • Accept and connect L2CAP Connection-oriented-channels.
  • Manage channel flow control.

Security:

  • JSON-backed key/secret management.
  • Initiate pairing.
  • Query encryption/authentication state.

All remote operations (connect, disconnect, client read/write, server indicate, l2cap recv/send, pair) are awaitable and support timeouts.

Usage

Scan for nearby devices: (Observer)

async with aioble.scan() as scanner:
    async for result in scanner:
        if result.name():
            print(result, result.name(), result.rssi, result.services())

Connect to a peripheral device: (Central)

# Either from scan result
device = result.device
# Or with known address
device = aioble.Device(aioble.PUBLIC, "aa:bb:cc:dd:ee:ff")

try:
    connection = await device.connect(timeout_ms=2000)
except asyncio.TimeoutError:
    print('Timeout')

Register services and wait for connection: (Peripheral, Server)

_ENV_SENSE_UUID = bluetooth.UUID(0x181A)
_ENV_SENSE_TEMP_UUID = bluetooth.UUID(0x2A6E)
_GENERIC_THERMOMETER = const(768)

_ADV_INTERVAL_MS = const(250000)

temp_service = aioble.Service(_ENV_SENSE_UUID)
temp_char = aioble.Characteristic(temp_service, _ENV_SENSE_TEMP_UUID, read=True, notify=True)

aioble.register_services(temp_service)

while True:
    connection = await aioble.advertise(
            _ADV_INTERVAL_MS,
            name="temp-sense",
            services=[_ENV_SENSE_UUID],
            appearance=_GENERIC_THERMOMETER,
            manufacturer=(0xabcd, b"1234"),
        )
    print("Connection from", device)

Update characteristic value: (Server)

temp_char.write(b'data')

temp_char.notify(b'optional data')

await temp_char.indicate(timeout_ms=2000)

Query the value of a characteristic: (Client)

temp_service = await connection.service(_ENV_SENSE_UUID)
temp_char = await temp_service.characteristic(_ENV_SENSE_TEMP_UUID)

data = await temp_char.read(timeout_ms=1000)

await temp_char.subscribe(notify=True)
while True:
    data = await temp_char.notified()

Open L2CAP channels: (Listener)

channel = await connection.l2cap_accept(_L2CAP_PSN, _L2CAP_MTU)
buf = bytearray(64)
n = channel.recvinto(buf)
channel.send(b'response')

Open L2CAP channels: (Initiator)

channel = await connection.l2cap_connect(_L2CAP_PSN, _L2CAP_MTU)
channel.send(b'request')
buf = bytearray(64)
n = channel.recvinto(buf)

Examples

See the examples directory for some example applications.

  • temp_sensor.py: Temperature sensor peripheral.
  • temp_client.py: Connects to the temp sensor.
  • l2cap_file_server.py: Simple file server peripheral. (WIP)
  • l2cap_file_client.py: Client for the file server. (WIP)

Tests

The multitests directory provides tests that can be run with MicroPython's run-multitests.py script. These are based on the existing multi_bluetooth tests that are in the main repo.