14.14. Wrap up¶
You have walked through Bluetooth Low Energy from the radio up to the Python API used to drive it:
The motivation – BLE is the answer when the camera wants to talk to something close by without any infrastructure between them. A phone in the same room, a wearable on a wrist, a beacon on a wall. Short range, no network to join, almost no power.
The radio – 2.4 GHz, 40 channels: three for advertising, 37 for connection data, hopped on a pseudo-random sequence with adaptive avoidance of noisy channels. Brief packets, mostly-asleep radios.
The link layer – packet framing, addressing, connection scheduling, retransmission, and link-layer encryption. None of it is configured from Python; all of it shows through in the connection parameters and the MTU.
Generic Access Profile (GAP) – discovery and connection management. Four roles: peripheral and broadcaster (advertise), central and observer (scan). Advertising payloads carry the local name, service UUIDs, appearance, and manufacturer-specific data – 31 bytes plus an optional 31-byte scan response. The connection interval, peripheral latency, and supervision timeout govern what an open connection feels like.
Generic Attribute Profile (GATT) – a tree of services, each holding characteristics, each optionally holding descriptors, identified by UUIDs (16-bit for Bluetooth-SIG standards, 128-bit for custom ones). Five operations: read and write (pull, client-initiated), notify and indicate (push, server-initiated, subscribed via the Client Characteristic Configuration Descriptor). Payload size is bounded by the negotiated MTU.
The Python API –
aiobleturns every BLE pattern into an asyncio coroutine. A peripheral isaioble.advertise()looping over connections, withService/Characteristicobjects built once and committed byaioble.register_services(). A central isaioble.scan()to find a peer,connect()to open the link,service()andcharacteristic()to walk the remote GATT tree, thenread()/write()/subscribe()/notified()for the actual data. Disconnects surface asaioble.DeviceDisconnectedErrorinside the coroutine that was waiting.L2CAP channels – the escape hatch for bulk byte streams that do not fit GATT’s key/value model.
aioble.DeviceConnection.l2cap_accept()/l2cap_connect()open a per-application channel on top of the GAP connection, with credit-flow-controlled send / recv and a larger MTU than GATT can carry.Pairing and encryption – BLE links are public by default.
aioble.DeviceConnection.pair()initiates a key exchange that produces an encrypted link;bond=True(the default) persists the keys so subsequent connections skip the handshake. Withoutmitm=Trueand a usable IO capability, encryption protects against passive eavesdroppers but not against an active redirect during the original pairing.
That is enough to write camera applications that publish status as a peripheral, read sensor data as a central, push live values to a phone over BLE, secure the link with a pair-and-bond step, and – for the rare bulk-transfer case – step off GATT into an L2CAP channel.
14.14.1. Troubleshooting¶
BLE failures are mostly mismatches between what the two sides expect, and a phone-side inspector is the fastest way to see whose expectations are off. The standard tool is nRF Connect for Mobile (Nordic Semiconductor, free on Android and iOS): it scans, connects, walks the GATT database, reads and writes characteristics, and subscribes to notifications – so the camera-side behaviour can be tested in isolation, without writing a companion app at all.
The common failure modes:
“My device shows up in the scanner but won’t connect.” Most often the advertising packet has
connectable=False(broadcaster mode), or a previous connection is still open and the cam is already pastaioble.advertise(). Add print statements around the advertise call to confirm.“exchange_mtu(512) ran but my notifications are still capped at 20 bytes.” The negotiated MTU is
min(local, peer)– the phone or central library may not have requested a larger MTU on its side, in which case the connection stays at 23. Inspectmtuafterexchange_mtu()returns. Also note thatexchange_mtu()only works once per connection; call it before the first large operation.“Pairing fails with a generic error.” Two usual culprits: the IO-capability mismatch (asking for
mitm=Trueon a cam declaringio=3/ no input no output – there is no way to confirm the numeric code, so the pairing engine bails), and a wildly wrong wall-clock time on the cam when the peer requires it. Set the clock withntptime.settime()before the first pairing attempt.“Notifications never arrive at the client.” Two things to check, in order: (a) was the characteristic declared with
notify=True? – the property bit must be set on the server side; (b) did the client callsubscribe()? – without writing the Client Characteristic Configuration Descriptor (CCCD), the server is told no client wants notifications and silently drops them.“The advertised name is truncated or missing.” The advertising payload is 31 bytes, and the flags + service-UUID + appearance fields each take bytes off the top. A long
name=plus several service UUIDs overflows. Either shorten the name or use active scanning so the scan response (another 31 bytes) carries the overflow. nRF Connect shows both halves separately, which makes the split obvious.“L2CAP connect raises immediately.” Usually a PSM mismatch – both sides have to agree on the same PSM number out of band. A
L2CAPConnectionErrorcarries the Bluetooth status code as its first argument; status2(“PSM not supported”) is the giveaway.“Bonded connections still trigger a full pairing handshake on every reconnect.”
aioble.security.load_secrets()was not called at startup. Without it, the saved keys are on flash but never loaded into memory, so the peer’s identity is unknown and pairing runs from scratch every time.
When all else fails, the lower-level bluetooth
module exposes an IRQ callback that fires for every
underlying event; subscribing to it briefly and printing
the events is the equivalent of a Wireshark trace for
the cam side.
14.14.2. Using this reference later¶
Treat the Bluetooth chapters as reference material; coming back for the exact layout of a peripheral’s advertising payload or the central scan-and-subscribe flow is the intended use. The aioble — Async BLE and bluetooth — low-level Bluetooth reference pages list every method, flag, and constant in one place when the question is just “what is the exact name of this call”.