BLE Service
Overview
The BLE (Bluetooth Low Energy) Service provides a complete implementation of Bluetooth communication using the Zephyr Bluetooth stack. It follows the singleton pattern and provides a high-level interface for BLE advertising, GATT services, and data notifications.
Architecture
Class Design
class BleService {
public:
static BleService& getInstance();
int init();
int start();
int stop();
bool isConnected() const;
bool isNotifyEnabled() const;
int notify(const uint8_t* data, uint16_t len);
int notify(const char* message);
void setConnectionCallback(ConnectionCallback cb);
void setDataReceivedCallback(DataReceivedCallback cb);
private:
BleService();
~BleService();
};
Callback Types
The BLE service uses function pointers for asynchronous event notification:
// Connection state callback
using ConnectionCallback = void (*)(bool connected);
// Data received callback
using DataReceivedCallback = void (*)(const uint8_t* data, uint16_t len);
GATT Service Structure
Custom GATT Service
The BLE service implements a custom GATT service with the following characteristics:
Characteristic |
UUID |
Properties |
|---|---|---|
Data TX |
Custom UUID |
Notify, Read |
Data RX |
Custom UUID |
Write, Write Without Response |
Service UUID
The service uses a custom 128-bit UUID for identification.
Advertising
Advertising Configuration
// Advertising parameters
static constexpr uint16_t ADV_INTERVAL_MIN = 0x0020; // 20ms
static constexpr uint16_t ADV_INTERVAL_MAX = 0x0040; // 40ms
// Device name
CONFIG_BT_DEVICE_NAME = "ESP32 Smart Home"
Advertising Data
The advertising packet includes:
Flags: General discoverable, BR/EDR not supported
Device Name: “ESP32 Smart Home”
Service UUID: Custom GATT service UUID
Connection Management
Connection Parameters
// Connection parameters
static constexpr uint16_t CONN_INTERVAL_MIN = 0x0018; // 30ms
static constexpr uint16_t CONN_INTERVAL_MAX = 0x0028; // 50ms
static constexpr uint16_t CONN_LATENCY = 0;
static constexpr uint16_t CONN_TIMEOUT = 400; // 4s
Connection Callbacks
Register a callback to receive connection state changes:
static void connection_callback(bool connected) {
if (connected) {
LOG_INF("BLE device connected");
} else {
LOG_INF("BLE device disconnected");
}
}
BleService::getInstance().setConnectionCallback(connection_callback);
Data Communication
Sending Data (Notifications)
Send data to the connected BLE client:
BleService& ble = BleService::getInstance();
// Send raw bytes
uint8_t data[] = {0x01, 0x02, 0x03};
ble.notify(data, sizeof(data));
// Send string
ble.notify("Hello BLE!");
Receiving Data
Register a callback to receive data from the BLE client:
static void data_received_callback(const uint8_t* data, uint16_t len) {
LOG_INF("Received %d bytes", len);
// Process received data
}
BleService::getInstance().setDataReceivedCallback(data_received_callback);
API Reference
Initialization
-
int BleService::init()
Initialize the BLE service and Bluetooth stack.
- Returns:
0 on success, negative error code on failure
- Return values:
-EALREADY – BLE already initialized
-EIO – Bluetooth enable failed
-
int BleService::start()
Start BLE advertising.
- Returns:
0 on success, negative error code on failure
- Return values:
-EINVAL – Invalid advertising parameters
-EALREADY – Already advertising
-
int BleService::stop()
Stop BLE advertising.
- Returns:
0 on success, negative error code on failure
Status Query
-
bool BleService::isConnected() const
Check if a BLE client is connected.
- Returns:
true if connected, false otherwise
-
bool BleService::isNotifyEnabled() const
Check if notifications are enabled by the client.
- Returns:
true if notifications enabled, false otherwise
Data Transmission
-
int BleService::notify(const uint8_t *data, uint16_t len)
Send data to the connected BLE client via notification.
- Parameters:
data – Pointer to data buffer
len – Length of data in bytes
- Returns:
0 on success, negative error code on failure
- Return values:
-ENOTCONN – No device connected
-EINVAL – Invalid parameters
-
int BleService::notify(const char *message)
Send a text message to the connected BLE client.
- Parameters:
message – Null-terminated string
- Returns:
0 on success, negative error code on failure
Callbacks
-
void BleService::setConnectionCallback(ConnectionCallback cb)
Register a callback for connection state changes.
- Parameters:
cb – Function pointer to callback
-
void BleService::setDataReceivedCallback(DataReceivedCallback cb)
Register a callback for received data.
- Parameters:
cb – Function pointer to callback
Configuration
Kconfig Options
Required Kconfig symbols in prj.conf:
# Bluetooth configuration
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="ESP32 Smart Home"
CONFIG_BT_DEVICE_APPEARANCE=0
CONFIG_BT_MAX_CONN=1
CONFIG_BT_MAX_PAIRED=1
# GATT configuration
CONFIG_BT_GATT_DYNAMIC_DB=y
CONFIG_BT_ATT_PREPARE_COUNT=2
# Stack sizes
CONFIG_MAIN_STACK_SIZE=2048
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
Usage Example
Complete Example
#include "modules/ble/bleservice.hpp"
// Connection callback
static void on_ble_connected(bool connected) {
if (connected) {
LOG_INF("BLE connected");
} else {
LOG_INF("BLE disconnected");
}
}
// Data received callback
static void on_ble_data(const uint8_t* data, uint16_t len) {
LOG_INF("Received %d bytes", len);
// Echo data back
BleService::getInstance().notify(data, len);
}
void ble_task_entry(void *p1, void *p2, void *p3) {
BleService& ble = BleService::getInstance();
// Register callbacks
ble.setConnectionCallback(on_ble_connected);
ble.setDataReceivedCallback(on_ble_data);
// Start advertising
int ret = ble.start();
if (ret < 0) {
LOG_ERR("Failed to start BLE: %d", ret);
return;
}
// Periodic data transmission
while (1) {
k_sleep(K_SECONDS(5));
if (ble.isConnected()) {
char msg[] = "Heartbeat";
ble.notify(msg);
}
}
}
Testing
Mobile App Testing
Install a BLE Scanner App:
Android: nRF Connect, BLE Scanner
iOS: LightBlue, nRF Connect
Scan for Device:
Look for “ESP32 Smart Home”
Connect to the device
Enable Notifications:
Find the TX characteristic
Enable notifications
Send Data:
Find the RX characteristic
Write data to test receiving
Command Line Testing
Using bluetoothctl on Linux:
# Start bluetoothctl
bluetoothctl
# Scan for devices
scan on
# Connect (replace XX:XX:XX:XX:XX:XX with device address)
connect XX:XX:XX:XX:XX:XX
# List services
menu gatt
list-attributes
Troubleshooting
Common Issues
Device Not Advertising
Symptoms: Device not visible in BLE scanner
Solutions:
Check if Bluetooth is enabled:
CONFIG_BT=yVerify advertising started successfully
Check system logs for errors
Ensure no other BLE connections are active
Connection Fails
Symptoms: Connection attempt fails immediately
Solutions:
Check connection parameters
Verify GATT database is properly initialized
Ensure stack sizes are sufficient
Check for memory issues
Notifications Not Working
Symptoms: Data sent but not received by client
Solutions:
Verify client enabled notifications (CCCD)
Check
isNotifyEnabled()returns trueEnsure MTU is sufficient for data size
Verify connection is active
Performance Optimization
Throughput Optimization
To maximize BLE throughput:
Increase MTU: Negotiate larger MTU size
Connection Interval: Use shorter intervals (20-50ms)
Queue Data: Buffer data to reduce overhead
Use Write Without Response: For unidirectional data
Power Optimization
To minimize power consumption:
Increase Advertising Interval: 500ms - 2000ms
Use Longer Connection Intervals: 100ms - 500ms
Enable Connection Latency: Allow peripheral to skip events
Reduce Notification Frequency: Batch data transmissions
Source Code Reference
app/src/modules/ble/bleservice.hpp- BLE service interfaceapp/src/modules/ble/bleservice.cpp- BLE service implementationapp/src/thread/ble_task.cpp- BLE task orchestration