917 Bluetooth Implementations and Labs
917.1 Learning Objectives
By the end of this section, you will be able to:
- Develop BLE Applications: Build Python and embedded C applications for BLE scanning and connectivity
- Implement GATT Services: Create custom services and characteristics for IoT sensor data
- Handle BLE Events: Program connection, disconnection, and notification callbacks
- Debug BLE Communication: Use packet sniffers and diagnostic tools for troubleshooting
- Optimize Power Consumption: Apply connection intervals and advertising strategies for battery life
- Build End-to-End Solutions: Create complete BLE sensor-to-cloud data pipelines
What is this section? Practical implementation exercises for Bluetooth Low Energy (BLE) development, organized into focused chapters.
When to use: - After understanding BLE fundamentals and GATT profiles - When ready to write actual BLE code - Before building your own BLE IoT project
Prerequisites: - Basic programming knowledge (C/Python) - Understanding of BLE concepts from fundamentals chapter - Access to BLE-capable hardware or simulator
Recommended Path: 1. Review Bluetooth Fundamentals 2. Work through the chapters below in order 3. Test understanding with Bluetooth Review
917.2 Chapter Overview
This implementation guide is organized into three focused chapters:
917.2.1 BLE Code Examples and Simulators
Estimated time: 45 minutes
Get started with BLE development through working code examples:
- Python BLE scanner using bleak library
- Arduino ESP32 GATT server implementation
- Interactive Wokwi simulators for hands-on practice
- GATT structure (services, characteristics, descriptors)
- BLE development stack overview
917.2.2 BLE Python Implementations
Estimated time: 60 minutes
Production-ready Python code for common BLE tasks:
- Device filtering by RSSI and name patterns
- GATT service exploration and characteristic reading
- iBeacon and Eddystone beacon parsing
- Zone-based proximity detection with RSSI smoothing
- Power optimization decision framework
917.2.3 BLE Hands-On Labs
Estimated time: 2-3 hours
Complete project implementations:
- Lab 1: ESP32 Heart Rate Monitor (standard GATT service)
- Lab 2: Python Environmental Dashboard (real-time monitoring)
- Lab 3: Indoor Positioning System (beacon trilateration)
- Lab 4: Mesh Network Simulation (message flooding)
- Worked examples: MTU optimization and power budget analysis
917.3 Common BLE Implementation Pitfalls
The Mistake: Requesting MTU exchange and then immediately sending large payloads using the requested (but not yet negotiated) MTU size, resulting in data truncation, protocol errors, or silent data loss.
Why It Happens: MTU exchange is an asynchronous operation. The request returns immediately, but the actual negotiated MTU is only valid after the exchange completes (indicated by a callback or event). Developers often assume the requested MTU is granted and start using it before confirmation.
The Fix: Always wait for the MTU exchange completion callback before using the new MTU. Track the effective MTU and fall back gracefully:
// WRONG: Using requested MTU immediately
void on_connected(uint16_t conn_handle) {
ble_gattc_mtu_exchange_request(conn_handle, 247);
// BUG: MTU not yet negotiated!
send_large_payload(conn_handle, data, 244); // May truncate to 20 bytes!
}
// CORRECT: Wait for MTU exchange completion
volatile uint16_t g_effective_mtu = 23; // Start with default
void on_connected(uint16_t conn_handle) {
g_effective_mtu = 23; // Reset to default
ble_gattc_mtu_exchange_request(conn_handle, 247);
// Don't send large data yet
}
void on_mtu_exchanged(uint16_t conn_handle, uint16_t negotiated_mtu) {
g_effective_mtu = negotiated_mtu;
uint16_t max_payload = g_effective_mtu - 3; // ATT header overhead
log_info("MTU negotiated: %d, max payload: %d", g_effective_mtu, max_payload);
// NOW safe to send large payloads
if (pending_large_transfer) {
start_transfer_with_mtu(conn_handle, max_payload);
}
}Note: The negotiated MTU is the minimum of what both sides support. A 247-byte request may result in 185 bytes if the central only supports that.
The Mistake: Running BLE scanning with 100% duty cycle (scan window = scan interval) on a battery-powered gateway or hub device, expecting to catch all advertisements while draining the battery in hours instead of days.
Why It Happens: Developers want to ensure no advertisements are missed, so they maximize scan window. But BLE scanning consumes 5-15mA continuously - comparable to active Wi-Fi. For mains-powered gateways this is fine, but battery-powered devices suffer severely.
The Fix: Calculate the trade-off between scan duty cycle and advertisement detection probability. For most beacons, 10-30% duty cycle catches >95% of advertisements:
Scan parameter trade-offs:
100% duty cycle (continuous):
- Scan interval: 100ms, Window: 100ms
- Current: 12mA continuous
- Detection: ~100% (all advertisements)
- Battery life: 200mAh / 12mA = 17 hours
30% duty cycle (balanced):
- Scan interval: 100ms, Window: 30ms
- Current: ~4mA average
- Detection: >95% (for 100ms+ advertising intervals)
- Battery life: 200mAh / 4mA = 50 hours
10% duty cycle (power-optimized):
- Scan interval: 1000ms, Window: 100ms
- Current: ~1.5mA average
- Detection: >90% (for 1s+ advertising intervals)
- Battery life: 200mAh / 1.5mA = 133 hours
Consider your beacon advertising interval when tuning scan parameters - you don’t need 100% duty cycle if beacons advertise frequently.
The Misconception: Many developers believe RSSI (Received Signal Strength Indicator) values can be directly converted to precise distances using simple formulas, and use raw RSSI readings for positioning without filtering.
Why This Fails (With Data):
1. RSSI Variance at Fixed Distance: - At 2 meters: RSSI can vary +/-8 dBm (range: -58 to -74 dBm) - Translation: Same physical position = 1.4m to 5.0m estimated distance - Error rate: Up to 150% distance error without filtering
2. Environmental Interference: - Human body: -5 to -15 dBm attenuation (doubles estimated distance) - Wall penetration: -10 to -30 dBm loss depending on material - Multipath fading: +/-6 dBm variance from reflections
The Correct Approach:
For Positioning: - Use zone-based proximity: Immediate (<0.5m), Near (0.5-3m), Far (>3m) - Require 3+ beacon measurements: Trilateration needs redundancy - Calibrate per environment: Measure path loss exponent (n) on-site - Apply time averaging: 5-10 samples over 2-5 seconds
Zone thresholds: Use >-60 dBm (immediate), -60 to -75 dBm (near), <-75 dBm (far)
917.5 Summary
This section provides comprehensive BLE implementation guidance through three focused chapters:
- Code Examples: Working Python and ESP32 code with interactive Wokwi simulators
- Python Implementations: Production-ready scanner, GATT explorer, beacon manager, and proximity detector
- Hands-On Labs: Complete projects for heart rate monitoring, dashboards, positioning, and mesh networks
917.6 What’s Next
Start with BLE Code Examples and Simulators to get hands-on experience with BLE development, then progress through the Python implementations and complete labs.