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

  1. Install a BLE Scanner App:

    • Android: nRF Connect, BLE Scanner

    • iOS: LightBlue, nRF Connect

  2. Scan for Device:

    • Look for “ESP32 Smart Home”

    • Connect to the device

  3. Enable Notifications:

    • Find the TX characteristic

    • Enable notifications

  4. 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=y

  • Verify 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 true

  • Ensure MTU is sufficient for data size

  • Verify connection is active

Performance Optimization

Throughput Optimization

To maximize BLE throughput:

  1. Increase MTU: Negotiate larger MTU size

  2. Connection Interval: Use shorter intervals (20-50ms)

  3. Queue Data: Buffer data to reduce overhead

  4. Use Write Without Response: For unidirectional data

Power Optimization

To minimize power consumption:

  1. Increase Advertising Interval: 500ms - 2000ms

  2. Use Longer Connection Intervals: 100ms - 500ms

  3. Enable Connection Latency: Allow peripheral to skip events

  4. Reduce Notification Frequency: Batch data transmissions

Source Code Reference

  • app/src/modules/ble/bleservice.hpp - BLE service interface

  • app/src/modules/ble/bleservice.cpp - BLE service implementation

  • app/src/thread/ble_task.cpp - BLE task orchestration

See Also