ChebbiOS

Connectivity: BLE

Services, Characteristics, and the art of sipping power.

BLE is not "lighter Wifi". It's a completely different paradigm built for asynchronous data exchange and coin-cell batteries. Unlike Classic Bluetooth (audio streaming), BLE is bursty.

Protocol Stack

Understanding the layers is key to debugging. The stack is split into the Controller (Radio focus) and the Host (Software focus).

BLE Protocol Stack: Peripheral vs Central

The GAP & GATT Model

Everything in BLE revolves around the Generic Attribute Profile (GATT). It defines how data is structured and exchanged.

Profile
Heart Rate Profile (The "Standard")
Service
Heart Rate Service (UUID: 0x180D)
Characteristic
Measurement (Value: 72 bpm)
Properties: Notify, Read

Key Concepts

  • Peripheral (Server): The device with the data (e.g., Heart Rate Monitor). Advertises its presence.
  • Central (Client): The device reading the data (e.g., Phone). Scans and initiates connection.
  • Connection Interval: How often the radios wake up to talk (e.g., every 30ms). Lower = faster data but high power. Higher = low power but laggy.

System Design with RTOS

In a real-world firmware, you don't run BLE logic in a `while(1)` super-loop. You mostly likely have an RTOS. Here is the classic architecture:

1. The Stack Task (High Priority)

The Vendor's BLE Stack (SoftDevice / Controller) usually runs as a very high priority task or ISR. It handles timing-critical radio events. Do not put your application code here.

2. The App Task (Medium Priority)

Your logic runs here. It blocks on a Queue, waiting for events from the Stack.

main.c FreeRTOS Integration
// 1. Stack Callback (Runs in ISR / High Priority context)
void on_ble_event(ble_evt_t * p_ble_evt) {
    // DON'T process complex data here.
    // Send message to Application Task
    xQueueSendFromISR(app_queue, &p_ble_evt, NULL);
}

// 2. Application Task (Your Code)
void app_task(void * pvParameters) {
    ble_evt_t event;
    for(;;) {
        // Block until an event arrives (0 CPU usage)
        if (xQueueReceive(app_queue, &event, portMAX_DELAY)) {
            switch (event.id) {
                case BLE_GATTS_EVT_WRITE:
                    toggle_led(); // Safe to do slow things here
                    break;
                case BLE_GAP_EVT_CONNECTED:
                    log_info("Connected!");
                    break;
            }
        }
    }
}

Connect & Discuss

Have questions about systems engineering, or found a bug in the code? Reach out!

Feedback

This blog is a static site, but I'd love to hear your thoughts. You can discuss this post by sending me an email or reaching out on social media.

Send Feedback