17 Python BLE: Scanning and Connections
17.1 Start With the Decision
A BLE scanner can see many adverts but still miss the device a service needs. The code must filter first, then connect with a clear timeout.
17.2 Route Overview
This is part 1 of 3. Continue with Python BLE: RSSI Smoothing and Zones.
17.3 Part Objectives
- Filter BLE advertisements by stable device evidence.
- Manage Python scan and connection state with explicit timeouts.
- bleak (Bluetooth Low Energy platform Agnostic Klient): Python async BLE library supporting Windows (WinRT), macOS (CoreBluetooth), and Linux (BlueZ) backends
- BleakClient: bleak class representing a connection to a BLE peripheral; provides methods for service discovery, read, write, start_notify, stop_notify
- BleakScanner: bleak class for BLE device discovery; supports filtering by service UUID, device name, and RSSI threshold
- asyncio.run(): Python coroutine runner; required for bleak operations which are all async; use asyncio.get_event_loop() for integration with existing async frameworks
- UUID String Format: bleak accepts both 16-bit UUIDs as “0000xxxx-0000-1000-8000-00805f9b34fb” (128-bit expanded form) and short “xxxx” strings; use full 128-bit format for custom services
- characteristic.properties: bleak property set of enabled operations: {‘read’, ‘write’, ‘notify’, ‘indicate’, ‘write-without-response’} — check before attempting operations
- client.start_notify(uuid, callback): Registers a Python callback function called when a BLE notification arrives; callback receives (sender_handle, bytearray_data)
- GATT Error Codes in bleak: BleakError wraps ATT error codes; common causes: device not paired (0x05), wrong UUID (0x01), characteristic not found (service discovery needed)
17.4 Minimum Viable Understanding
BLE development in Python centers on the bleak library, which provides cross-platform async APIs for scanning, connecting, and interacting with BLE devices. Production BLE applications need RSSI filtering for reliable device discovery, GATT service exploration for data access, and exponential smoothing for stable proximity detection — zone-based classification (immediate/near/far) is far more reliable than precise distance calculations due to inherent RSSI variability.
17.5 Learning Objectives
By the end of this chapter, you will be able to:
- Implement Production BLE Scanners: Configure device filtering by RSSI threshold and name patterns using the bleak library
- Analyze GATT Services: Connect to BLE devices and enumerate services and characteristics to assess device capabilities
- Distinguish Beacon Protocols: Compare iBeacon and Eddystone advertisement packet structures and justify protocol selection for a given use case
- Design Proximity Detection Systems: Apply RSSI smoothing algorithms and construct zone-based presence detection with exponential moving averages
- Develop Async BLE Applications: Construct event-driven Python programs using asyncio and diagnose disconnection handling for production reliability
- In 60 Seconds
- Key Concepts
- Quick Check: Implementation
- Minimum Viable Understanding
- For Beginners: Python BLE Development
- Python BLE Implementation Pattern
- Prerequisites
- BLE Scanner with Device Filtering
- Try It: BLE RSSI Filter Simulator
- Checkpoint: Scanner Evidence
- BLE GATT Server Explorer
- Try It: GATT Service UUID Lookup
- BLE Beacon Manager
- Try It: Beacon Advertisement Decoder
- Checkpoint: Services and Advertisements
- BLE Proximity Detector
17.6 For Beginners: Python BLE Development
What you’ll learn: Production-ready Python implementations for common BLE tasks using the bleak library.
Prerequisites:
- Basic Python with asyncio understanding
- BLE concepts from Bluetooth Fundamentals
- Code examples from BLE Code Examples
Why Python for BLE? Python’s bleak library provides cross-platform BLE support (Windows, macOS, Linux) with clean async APIs, making it ideal for gateways, data collection, and prototyping.
Use Python BLE code where the device doing the Bluetooth work has enough compute, storage, and operating-system support to run a gateway or test tool.
- Start with scanning filters: service UUID, name pattern, and minimum RSSI.
- Connect only after you have selected a specific target from scan results.
- Discover services before reading or subscribing to characteristics.
- Treat RSSI distance as an estimate; classify broad zones instead of promising exact meters.
- Build reconnection handling from the first prototype because BLE links can drop normally.
In this pattern, the Python host is the BLE Central and GATT client. While scanning it is acting as an Observer; after selection it initiates the connection and reads, writes, or subscribes to the peripheral’s GATT server. If the product requirement says a phone must discover the Python process or read a characteristic served by it, that is a Peripheral/GATT-server requirement and belongs on firmware or a platform-specific stack, not plain Bleak.
17.7 Prerequisites
Before working through these implementations:
- BLE Code Examples and Simulators: Basic Python scanner and GATT concepts
- Python Environment: A currently supported Python version for your chosen
bleakrelease, installed withpip install bleak
17.8 BLE Scanner with Device Filtering
A production scanner with RSSI filtering and statistics:
Expected scanner output should include:
- The scan duration and minimum RSSI threshold, for example
15.0sand-70 dBm. - Each matching device name, address or platform identifier, latest RSSI, and rough distance estimate.
- A final count of devices that passed the filter.
- Per-device statistics such as sample count, RSSI range, mean RSSI, and standard deviation.
The implementation filters devices by minimum RSSI threshold, collects multiple samples per device, and calculates statistics for more reliable readings.
For implementation review, keep a repeatable scanner log: adapter used, scan timeout, target name or service UUID, RSSI threshold, advertisements received, advertisements rejected by filters, and the selected device identifier. The script should keep a short sample window and select the strongest stable match instead of connecting to the first advertisement packet it sees.
Checkpoint: Scanner Evidence
You now know:
- A production scanner log should include the adapter, scan timeout, target filter, RSSI threshold, advertisements accepted, advertisements rejected, and selected device identifier.
- The chapter’s scanner example uses values like
15.0sand-70 dBmto make discovery repeatable instead of anecdotal. - Bleak code should select a specific target after filtering; connecting to the first packet is weaker evidence than a stable sample window.
Scanning answers “which devices are worth investigating.” The next step is to connect to one selected peripheral and prove which services and characteristic operations it actually exposes.
17.9 BLE GATT Server Explorer
Connect to a BLE device and enumerate its services and characteristics:
Expected explorer output should identify the device and list each discovered service with its characteristics:
- Heart Rate Service:
0000180d-0000-1000-8000-00805f9b34fb- Heart Rate Measurement characteristic
00002a37-0000-1000-8000-00805f9b34fb - Common properties: read and notify
- Heart Rate Measurement characteristic
- Battery Service:
0000180f-0000-1000-8000-00805f9b34fb- Battery Level characteristic
00002a19-0000-1000-8000-00805f9b34fb - Common properties: read and notify
- Battery Level characteristic
Standard service UUIDs:
- 0x180D - Heart Rate Service
- 0x180F - Battery Service
- 0x181A - Environmental Sensing
- 0x1816 - Cycling Speed and Cadence
Review evidence should show service discovery before any read, write, or subscription attempt. Check characteristic properties before writing and subscribe only to characteristics that expose notify or indicate. For decoded sensor values, log the raw bytes and the decoded value; for example, a signed 16-bit temperature value of 2234 centi-degrees represents 22.34 C.
17.10 BLE Beacon Manager
Parse and manage iBeacon and Eddystone beacon advertisements:
Expected beacon-manager output should summarize:
- Total decoded beacons.
- Count by beacon type, such as iBeacon and Eddystone-URL.
- iBeacon fields: calibrated TX power, proximity UUID, major value, and minor value.
- Eddystone-URL fields: calibrated TX power and decoded URL.
Beacon Protocol Differences:
- iBeacon: Apple-defined advertisement format using a 128-bit proximity UUID plus
majorandminorfields. It does not carry URLs or telemetry in the standard iBeacon frame. - Eddystone: Google-defined advertisement family with UID, URL, TLM, and EID frame types. UID frames use a namespace plus instance identifier, URL frames broadcast compact web links, and TLM frames carry telemetry.
Checkpoint: Services and Advertisements
You now know:
- Service discovery comes before reads, writes, or subscriptions; properties such as
READ,WRITE,NOTIFY, andINDICATEdecide which operation is valid. - Standard services in this chapter include Heart Rate
0x180D, Battery0x180F, Environmental Sensing0x181A, and Cycling Speed0x1816. - iBeacon uses a proximity UUID plus
majorandminor; Eddystone uses UID, URL, TLM, or EID frames depending on the deployment goal.
Once the advertisement and GATT surface are explicit, the chapter moves from identity to location. RSSI is useful, but only after you treat it as a noisy signal.
17.11 BLE Proximity Detector
Zone-based proximity detection with RSSI smoothing:
A typical approach trace should show the raw RSSI, smoothed RSSI, estimated distance, and current zone at each sample. For example, a device moving closer may start at -75 dBm in the far zone, cross into the near zone around -68 dBm, and only enter the immediate zone after the smoothed RSSI rises above the immediate threshold.
Zone Thresholds:
- Immediate: RSSI above
-55 dBm, usually less than0.5m. - Near: RSSI from about
-55 dBmto-70 dBm, usually0.5mto3m. - Far: RSSI below
-70 dBm, usually more than3m.
RSSI Smoothing Algorithm:
The exponential moving average (EMA) filter reduces RSSI noise:
- Formula:
smoothed_rssi = alpha * new_rssi + (1 - alpha) * prev_smoothed - A higher
alphareacts faster but lets more noise through. - A lower
alphais steadier but takes longer to follow real movement. - Values around
0.2to0.3are common starting points for zone-based proximity.
17.12 Continue to the Next Part
Carry this evidence into Python BLE: RSSI Smoothing and Zones, which begins with Phoebe’s Field Notes: Why RSSI Distance Moves in Multipliers.
