%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#2C3E50', 'primaryTextColor': '#fff', 'primaryBorderColor': '#16A085', 'lineColor': '#16A085', 'secondaryColor': '#E67E22', 'tertiaryColor': '#ecf0f1', 'fontSize': '14px'}}}%%
graph TB
DEV[BLE Device<br/>ESP32-Sensor]
SERV1[Service<br/>UUID: 4fafc201...]
CHAR1[Characteristic<br/>UUID: beb5483e...]
DESC1[CCCD Descriptor<br/>Enable/Disable Notifications]
DEV --> SERV1
SERV1 --> CHAR1
CHAR1 --> DESC1
CHAR1 -.->|Properties| PROP[READ + NOTIFY]
CHAR1 -.->|Value| VAL[Temperature: 23.5°C]
style DEV fill:#2C3E50,stroke:#16A085,stroke-width:3px,color:#fff
style SERV1 fill:#16A085,stroke:#2C3E50,stroke-width:2px
style CHAR1 fill:#E67E22,stroke:#2C3E50,stroke-width:2px
style DESC1 fill:#7F8C8D,stroke:#2C3E50,stroke-width:2px
style PROP fill:#ecf0f1,stroke:#2C3E50,stroke-width:1px
style VAL fill:#ecf0f1,stroke:#2C3E50,stroke-width:1px
918 BLE Code Examples and Simulators
918.1 Learning Objectives
By the end of this chapter, you will be able to:
- Write Python BLE Scanners: Use the bleak library to discover nearby BLE devices
- Build ESP32 BLE Beacons: Create Arduino-based GATT servers that advertise sensor data
- Understand GATT Structure: Implement services, characteristics, and descriptors
- Use BLE Notifications: Enable real-time data push from peripheral to central
- Test with Simulators: Experiment with Wokwi-based BLE device simulations
What you’ll learn: This chapter provides working code examples for BLE development - both Python scripts for scanning/connecting and ESP32 Arduino code for creating BLE peripherals.
Prerequisites: - Basic Python or C/Arduino programming - Understanding of BLE concepts (GATT, services, characteristics) - Review Bluetooth Fundamentals first
Key takeaway: BLE programming follows a client-server model where peripherals (servers) advertise services and centrals (clients) discover and connect to read/write data.
918.2 Prerequisites
Before working through these examples:
- Bluetooth Fundamentals and Architecture: Understanding BLE protocol stack, GATT services, characteristics, and the master/slave model
- Basic Programming Skills: Familiarity with Python (asyncio) or Arduino C++ for embedded development
918.3 Python BLE Scanner
A minimal BLE scanner using the bleak library:
import asyncio
from bleak import BleakScanner
async def scan_devices():
"""Scan for BLE devices"""
print("Scanning for BLE devices...")
devices = await BleakScanner.discover(timeout=10.0)
print(f"\nFound {len(devices)} devices:\n")
for device in devices:
print(f"Name: {device.name or 'Unknown'}")
print(f"Address: {device.address}")
print(f"RSSI: {device.rssi} dBm\n")
# Run scanner
asyncio.run(scan_devices())Install: pip install bleak
918.4 Arduino ESP32 BLE Beacon
Create a BLE GATT server on ESP32 that advertises a temperature sensor service:
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHAR_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
class ServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("Device connected");
}
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("Disconnected");
}
};
void setup() {
Serial.begin(115200);
BLEDevice::init("ESP32-Sensor");
BLEServer *pServer = BLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHAR_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY
);
pCharacteristic->addDescriptor(new BLE2902());
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->start();
Serial.println("BLE beacon advertising!");
}
void loop() {
if (deviceConnected) {
float temp = 20.0 + random(0, 100) / 10.0;
char tempStr[8];
dtostrf(temp, 4, 2, tempStr);
pCharacteristic->setValue(tempStr);
pCharacteristic->notify();
Serial.printf("Sent: %s°C\n", tempStr);
}
delay(2000);
}
Try it yourself! See how ESP32 broadcasts BLE advertisements and serves data to connected clients.
What This Simulates: An ESP32 acting as a BLE GATT server, advertising a temperature sensor service and notifying connected clients with readings.
How to Use: 1. Click Start Simulation 2. Watch the Serial Monitor show “BLE beacon advertising!” 3. Observe temperature readings being generated 4. See connection status when clients connect 5. Notice GATT service and characteristic UUIDs
918.5 Interactive Simulator: BLE Scanner (Central/Client)
What This Simulates: ESP32 scanning for nearby BLE devices and reading their advertised data
BLE Scanning Flow:
Step 1: ESP32 starts BLE scan
- Sets scan parameters (interval, window, active/passive)
Step 2: Receives advertisements from devices
- Device Name
- RSSI (signal strength)
- Service UUIDs
- Manufacturer data
Step 3: Filters results
- By name pattern
- By RSSI threshold
- By service UUID
Step 4: Optionally connects to device
- Read characteristics
- Subscribe to notifications
How to Use: 1. Click Start Simulation 2. Watch ESP32 discover nearby BLE devices 3. See device names, addresses, and RSSI values 4. Observe scan results filtering 5. Monitor connection attempts to specific devices
918.6 Video Tutorial
918.7 BLE Development Stack Overview
Understanding the complete development stack helps you choose the right tools and libraries:
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor':'#2C3E50','primaryTextColor':'#fff','primaryBorderColor':'#16A085','lineColor':'#16A085','secondaryColor':'#E67E22','tertiaryColor':'#7F8C8D'}}}%%
graph TB
subgraph App["Application Layer"]
A1[Python bleak]
A2[Mobile SDK<br/>CoreBluetooth/Android BLE]
A3[Web Bluetooth API]
end
subgraph Stack["BLE Protocol Stack"]
S1[GATT<br/>Services & Characteristics]
S2[ATT<br/>Attribute Protocol]
S3[L2CAP<br/>Logical Link Control]
S4[HCI<br/>Host Controller Interface]
end
subgraph Firmware["Firmware SDK"]
F1[ESP-IDF BLE]
F2[Zephyr BLE]
F3[Arduino BLE]
end
subgraph Hardware["Hardware"]
H1[ESP32<br/>Xtensa + BLE 4.2/5.0]
H2[nRF52840<br/>ARM Cortex-M4 + BLE 5.3]
H3[CC2640<br/>ARM Cortex-M3 + BLE 5.1]
end
App --> Stack --> Firmware --> Hardware
style App fill:#E67E22,stroke:#2C3E50,stroke-width:2px
style Stack fill:#16A085,stroke:#2C3E50,stroke-width:2px,color:#fff
style Firmware fill:#2C3E50,stroke:#16A085,stroke-width:2px,color:#fff
style Hardware fill:#7F8C8D,stroke:#2C3E50,stroke-width:2px,color:#fff
918.8 Summary
This chapter provided practical BLE code examples:
- Python Scanner: Using bleak library to discover nearby BLE devices with RSSI readings
- ESP32 GATT Server: Creating a BLE peripheral that advertises services and notifies connected clients
- Interactive Simulators: Wokwi-based ESP32 simulations for hands-on experimentation
- GATT Structure: Understanding services, characteristics, descriptors, and properties
- Development Stack: Overview of tools from hardware to application layer
918.9 What’s Next
Continue to BLE Python Implementations for production-ready Python code including device filtering, GATT server exploration, beacon management, and proximity detection algorithms.