zephyr-bluetooth-le-conceptos

Bluetooth Low Energy en Zephyr: conceptos básicos

  • 5 min

Bluetooth Low Energy es un protocolo inalámbrico de corto alcance optimizado para intercambiar datos con bajo consumo.

Zephyr ofrece una pila Bluetooth abierta que incluye el Host y una implementación de Controller. La combinación utilizada depende del SoC y de la configuración, y que el software esté cualificado no certifica automáticamente el producto final.

Bluetooth Low Energy tiene su propio vocabulario y arquitectura. Antes de enviar datos, necesitamos entender sus piezas principales.

Arquitectura de BLE en Zephyr

Para entender cómo funciona el código, primero hay que ver cómo está organizado el sistema por dentro. El stack de BLE se divide en dos grandes partes:

  1. Host: Es la parte lógica (software). Maneja los perfiles, la seguridad y los datos de alto nivel (GAP, GATT). En Zephyr, esto corre como parte del Kernel.
  2. Controller: Es la parte que habla con el hardware de radio. Gestiona los tiempos exactos de transmisión y recepción de paquetes.

En un chip como el nRF52 o el ESP32-C3, el Host y el Controller corren en la misma CPU. En configuraciones más complejas, pueden estar en chips separados comunicándose por UART (HCI). Zephyr soporta ambos modelos de forma transparente.

Diccionario básico: GAP y GATT

Si estás empezando con BLE, conviene entender bien estos dos acrónimos desde el principio.

GAP (Generic Access Profile)

Define quién conecta con quién. Es el “portero” de la discoteca. Define los roles:

  • Central: El que manda y escanea (normalmente el Móvil).
  • Peripheral: El dispositivo que anuncia su presencia y consume poca energía (tu sensor IoT).

También define el proceso de Advertising (Anuncios). Antes de conectarse, el Periférico debe estar gritando periódicamente: “¡Estoy aquí! ¡Me llamo Zephyr! ¡Tengo estos servicios!”.

GATT (Generic Attribute Profile)

Define cómo se intercambian los datos una vez conectados. Es el “camarero” que trae los platos.

  • Server (Servidor): El que tiene los datos (tu sensor).
  • Client (Cliente): El que pide o escribe los datos (el Móvil).

Confusión común: Generalmente, el Periférico (GAP) actúa como Servidor (GATT), y el Central actúa como Cliente. Pero técnicamente podrían intercambiarse los roles GATT.

Configuración del proyecto (prj.conf)

Para activar Bluetooth en Zephyr, necesitamos habilitar el subsistema en la configuración. Es un poco más verboso que en Arduino, pero nos da control total sobre el tamaño del binario.

# 1. Activar el subsistema Bluetooth
CONFIG_BT=y

# 2. Definir el rol (Queremos ser un Periférico)
CONFIG_BT_PERIPHERAL=y

# 3. Nombre del dispositivo (Lo que veremos en el móvil)
CONFIG_BT_DEVICE_NAME="Zephyr Demo"
Copied!

Inicialización del stack

En nuestro código main.c, lo primero es incluir las cabeceras y arrancar el stack. A diferencia de Serial.begin(), en Zephyr la inicialización es asíncrona (aunque podemos esperar a que termine).

#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>

int main(void)
{
    int err;

    /* Inicializamos el stack Bluetooth */
    /* Pasamos NULL para que sea síncrono (bloqueante hasta arrancar)
       o una función callback si queremos que nos avise luego */
    err = bt_enable(NULL);

    if (err) {
        printk("Fallo al iniciar Bluetooth (err %d)\n", err);
        return err;
    }

    printk("Bluetooth inicializado correctamente\n");

    /* ... aquí ya podemos empezar a anunciar ... */
    return 0;
}
Copied!

El concepto de UUID

En BLE, todo tiene un identificador único de 128 bits llamado UUID.

  • Servicios estándar: Tienen UUIDs cortos de 16 bits (ej. 0x180D es Heart Rate).
  • Servicios propios: Debemos generar nuestros propios UUIDs largos (para no chocar con los de Philips o Garmin).

Zephyr tiene macros para definir esto fácilmente, pero lo veremos en profundidad cuando creemos nuestro propio servicio. Por ahora, nos centraremos en que el dispositivo “se vea”.

Hola mundo en BLE: advertising

El “Hola Mundo” en Bluetooth no es enviar un dato, es hacer que tu móvil detecte el dispositivo. Para ello, tenemos que configurar el paquete de Advertising.

En el advertising legacy, cada paquete de datos de anuncio tiene un máximo de 31 bytes. El extended advertising permite cargas mayores cuando el controlador y el observador lo soportan.

  1. Flags: Indican si el dispositivo es “General Discoverable”.
  2. Nombre: El nombre completo o corto.
  3. UUIDs: Lista de servicios que ofrecemos (opcional).
/* Definimos el paquete de anuncio (Advertisement Data) */
static const struct bt_data ad[] = {
    /* Flags: General Discoverable (visible) y BR/EDR Not Supported (solo BLE) */
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),

    /* Nombre completo del dispositivo */
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

int main(void)
{
    int err = bt_enable(NULL);
    if (err) {
        printk("Fallo al iniciar Bluetooth (err %d)\n", err);
        return err;
    }

    /* Arrancamos el anuncio */
    /* BT_LE_ADV_CONN: Conectable (el móvil puede pedir conectarse) */
    err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);

    if (err) {
        printk("Fallo al anunciar (err %d)\n", err);
        return err;
    }

    printk("Advertising iniciado... ¡Búscame en tu móvil!\n");

    /* El hilo main puede dormir, el controlador de radio trabaja solo */
    while (1) {
        k_sleep(K_FOREVER);
    }
}
Copied!

Herramientas de prueba: nRF Connect

Para probar esto, no necesitamos escribir una App de Android/iOS todavía. Descargad la aplicación nRF Connect for Mobile (de Nordic Semiconductor). Es la navaja suiza para depurar BLE.

  1. Compila y flashea tu placa.
  2. Abre nRF Connect en el móvil.
  3. Escanead.
  4. Deberías ver un dispositivo llamado “Zephyr Demo”.