Guía completa de ESP32 NimBLE para principiantes
La comunicación inalámbrica se ha convertido en un elemento esencial en el desarrollo de dispositivos inteligentes, como rastreadores de fitness y balizas. Una de las tecnologías más adoptadas para este fin es Bluetooth Low Energy (BLE), que permite a los dispositivos conectarse sin agotar rápidamente sus baterías. En este contexto, el chip ESP32 se ha vuelto una opción popular, ya que combina capacidades de Wi-Fi y Bluetooth en un solo hardware compacto.
Históricamente, para manejar Bluetooth en el ESP32, se ha utilizado un paquete de software integrado conocido como Bluedroid. A pesar de su robustez y su capacidad para gestionar tanto Bluetooth Clásico como BLE, Bluedroid es relativamente pesado. Consume una cantidad considerable de memoria de almacenamiento y potencia de procesamiento activa, dado que está diseñado para manejar las complejas máquinas de estado de ambas tecnologías de manera simultánea. Esto se convierte en un inconveniente si tu proyecto solo necesita BLE, haciendo que el uso del sistema Bluedroid resulte innecesario y poco eficiente.
Esta necesidad de optimización dio lugar al desarrollo de NimBLE, un stack de Bluetooth Low Energy completamente open-source y altamente optimizado, que fue desarrollado inicialmente bajo el proyecto del sistema operativo de tiempo real Apache Mynewt. Cambiar del pesado marco de Bluedroid al liviano stack de NimBLE no solo reduce la sobrecarga de memoria, sino que también asegura que los dispositivos ESP32 funcionen con la máxima eficiencia.
En esta guía, exploraremos por qué NimBLE es una opción superior a Bluedroid, cómo configurarlo en tu software de Arduino y te guiaremos a través de la implementación de tu primer servidor Bluetooth básico utilizando NimBLE. ¡Vamos a comenzar!
Comparación entre Bluedroid y NimBLE
Para comprender la utilidad de NimBLE, es fundamental explorar las diferencias estructurales entre el stack de NimBLE y la implementación estándar de ESP32 basada en Bluedroid.
Consumo de Flash y SRAM
El principal inconveniente de Bluedroid radica en que está diseñado para manejar las complejidades del Bluetooth Clásico y BLE al mismo tiempo. Aunque desactives las funciones de Bluetooth clásico en tu código, el código de fondo no optimizado sigue presente, ocupando espacio valioso.
NimBLE, por el contrario, fue concebido desde sus inicios como un stack exclusivo de BLE. Esta simplificación elimina una gran cantidad de lógica de manejo de protocolos innecesaria, resultando en un uso considerablemente menor de espacio de almacenamiento permanente y una utilización significativamente más baja de memoria activa durante su funcionamiento.
Eficiencia y capacidad de respuesta en tiempo de ejecución
Además del ahorro de espacio, NimBLE también es mucho más rápido y sensible. Esto es crucial para proyectos que utilizan el modo de suspensión profunda. En este modo, el ESP32 apaga la mayoría de sus componentes para conservar energía. Cuando se despierta, necesita iniciar Bluetooth, transmitir datos y volver a dormir lo más rápido posible.
Las pruebas indican que NimBLE suele iniciarse mucho más rápido que Bluedroid. Por ejemplo, Bluedroid puede tardar aproximadamente 631 milisegundos en inicializarse, mientras que NimBLE puede hacerlo en unos 209 milisegundos, una mejora significativa.
Aquí hay una simple comparación:
- Bluedroid: 631 ms para inicializar.
- NimBLE: 209 ms para inicializar.
Aunque NimBLE puede tardar un poco más en negociar y establecer la conexión física con un dispositivo, su velocidad de arranque es tan superior que el tiempo total sigue siendo más corto. Esto ayuda a conservar la vida de la batería, ya que el ESP32 permanece menos tiempo despierto.
Densidad de conexiones concurrentes
NimBLE también puede manejar más conexiones BLE simultáneamente. Mientras que el stack de Bluedroid típicamente limita al ESP32 a entre tres y siete conexiones concurrentes, dependiendo de la configuración, NimBLE puede soportar hasta nueve conexiones BLE al mismo tiempo.
Esta capacidad adicional es extremadamente útil si estás construyendo un hub central que necesita recopilar datos de múltiples sensores simultáneamente.
Fundamentos de Bluetooth Low Energy (BLE)
Antes de crear tu servidor NimBLE en el ESP32, es esencial familiarizarse con los conceptos básicos de Bluetooth Low Energy.
En el código que desarrollaremos, configuraremos el ESP32 para actuar como un servidor. Lo configuraremos para transmitir un servicio específico y, dentro de ese servicio, crearemos una característica que contendrá nuestro valor de datos real. Para que otros dispositivos reconozcan exactamente qué datos estamos transmitiendo, asignaremos a todo un número de identificación único llamado UUID. Si alguno de estos términos te suena extraño, no te preocupes. Vamos a repasar rápidamente cómo está estructurada la información en BLE para que el código tenga sentido.
Cuando trabajamos con BLE, todo gira en torno a dos sistemas principales: GAP y GATT.
GAP (Perfil de Acceso Genérico)
GAP controla la fase de conexión y publicidad en Bluetooth. Es lo que hace que tu dispositivo sea visible para el mundo exterior y determina cómo dos dispositivos descubren y establecen una conexión entre sí.
GAP define varios roles para los dispositivos, pero los dos conceptos clave a recordar son dispositivos Centrales y Periféricos.
- Dispositivos Periféricos: Normalmente son dispositivos pequeños, de bajo consumo y limitados en recursos, como monitores de frecuencia cardíaca o etiquetas de proximidad, que pueden conectarse a un dispositivo central mucho más poderoso.
- Dispositivos Centrales: Son dispositivos que cuentan con mucha más potencia de procesamiento y memoria, como teléfonos móviles o tabletas, a los que se conecta el periférico.
Un dispositivo periférico publicita su presencia enviando paquetes de publicidad a intervalos regulares para informar a los dispositivos centrales cercanos. Una vez que se establece la conexión, el proceso de publicidad se detiene y entra en juego GATT.
GATT (Perfil de Atributo Genérico)
GATT especifica cómo debe organizarse la información y cómo deben compartir datos dos dispositivos BLE. A diferencia de GAP, que define las interacciones de bajo nivel con los dispositivos, GATT se ocupa únicamente de los procedimientos y formatos de transferencia de datos.
La información se organiza jerárquicamente en secciones llamadas servicios, que agrupan piezas conceptualmente relacionadas de datos de usuario denominadas características.
Servicios
Un servicio de GATT es una colección de datos conceptualmente relacionados llamados características. Cada servicio puede tener una o más características y tiene su propio identificador numérico único o UUID, que puede ser de 16 bits (para servicios BLE oficialmente adoptados) o de 128 bits (para servicios personalizados). Un ejemplo es el Servicio de Frecuencia Cardíaca, que tiene un UUID de 16 bits de 0x180D y contiene hasta 3 características: Medición de Frecuencia Cardíaca, Ubicación del Sensor Corporal y Punto de Control de Frecuencia Cardíaca. Puedes encontrar más servicios definidos por Bluetooth SIG aquí.
Características
Una característica GATT es un grupo de información llamado Atributos. Los Atributos son la información que se transfiere realmente entre los dispositivos BLE. Una característica típica está compuesta por los siguientes atributos:
- Valor: Es el dato real almacenado en la característica. Puede ser cualquier tipo de dato, como un número, una cadena o un array de bytes.
- Descriptor: Proporciona información adicional o configuraciones para la característica.
Además del valor, cada característica está asociada con las siguientes propiedades:
- Handle: Un número de 16 bits que se utiliza para acceder a la característica en el dispositivo servidor.
- UUID: Un identificador único de 128 bits que indica qué representa la característica.
- Permisos: Define qué operaciones sobre la característica están permitidas, como leer, escribir o notificar.
UUID (Identificador Único Universal)
Los UUIDs aseguran que los servicios y características puedan ser identificados de manera única. Juegan un papel esencial en la definición y acceso a los datos en un dispositivo BLE. Hay dos tipos de UUIDs en BLE:
- UUID de 16 bits: Se utiliza para perfiles, servicios y características BLE oficiales, estandarizados por Bluetooth-SIG. Por ejemplo, el “Servicio de Frecuencia Cardíaca” tiene un UUID de 16 bits estandarizado de 0x180D.
- UUID de 128 bits: Se utiliza para servicios y características personalizados (específicos de un proveedor). Si una empresa desarrolla su propio servicio no cubierto por los servicios BLE oficiales, usaría un UUID de 128 bits único.
Servidor GATT y Cliente GATT
Desde la perspectiva de GATT, cuando dos dispositivos están conectados, cada uno ocupa uno de dos roles:
- Servidor GATT: Es el dispositivo que contiene la base de datos de características.
- Cliente GATT: Es el dispositivo que lee o escribe datos del servidor GATT.
Esta relación se ilustra en una conexión BLE típica, donde el dispositivo periférico (un ESP32) actúa como servidor GATT que contiene los datos, mientras que el dispositivo central (un smartphone) es el cliente GATT que los lee.
Creando un servidor NimBLE simple en ESP32
Ahora que comprendes qué es un servidor GATT, un servicio y una característica, es momento de construir uno.
Para facilitar este proceso, utilizaremos la biblioteca NimBLE-Arduino. Desarrollada por Ryan Powell, esta biblioteca toma el sistema NimBLE, rápido y ligero, y lo hace fácil de compilar y ejecutar en el IDE de Arduino. Su objetivo principal es proporcionar una alternativa más ligera a la biblioteca de BLE basada en Bluedroid original de Neil Kolban, manteniendo casi los mismos comandos de programación. Esto facilita a los desarrolladores actualizar su código BLE antiguo a NimBLE.
Para instalar la biblioteca:
- Abre tu programa IDE de Arduino y haz clic en el icono de Administrador de Bibliotecas en la barra lateral izquierda.
- Escribe “nimble” en el cuadro de búsqueda para filtrar tus resultados.
- Busca la biblioteca NimBLE-Arduino de h2zero.
- Haz clic en el botón Instalar para agregarla a tu IDE de Arduino.
Sketch de Arduino
El ejemplo a continuación configura el ESP32 como un servidor NimBLE. El ESP32 se publicita para que los dispositivos BLE cercanos puedan descubrirlo. También crea un servicio BLE y una característica BLE. Un dispositivo cliente, como un smartphone, puede conectarse al ESP32 y leer el valor almacenado en la característica.
#include
// Verifica la siguiente dirección para generar UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
void setup() {
// Inicializa la interfaz serial principal para la salida de depuración
Serial.begin(115200);
Serial.println("Iniciando configuración del servidor NimBLE...");
// Fase 1: Inicializa el stack BLE con un nombre de dispositivo único
NimBLEDevice::init("MyESP32");
// Fase 2: Instancia el servidor y crea el servicio
NimBLEServer *pServer = NimBLEDevice::createServer();
NimBLEService *pService = pServer->createService(SERVICE_UUID);
// Fase 3: Crea la característica y define sus propiedades
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
);
// Asigna un valor inicial a la característica
pCharacteristic->setValue("Hello World!");
// Fase 4: Inicia el servicio para hacerlo accesible
pService->start();
// Fase 5: Configura e inicia la publicidad
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->setName("MyESP32");
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->enableScanResponse(true);
pAdvertising->start();
Serial.println("El servidor NimBLE está publicitando correctamente.");
}
void loop() {
// El programador FreeRTOS maneja las tareas BLE en segundo plano;
// el loop principal puede permanecer vacío o manejar otra lógica de sensores.
delay(2000);
}Explicación del código
Vamos a revisar el código en detalle. Comienza incluyendo la biblioteca NimBLEDevice para las operaciones BLE en el ESP32.
#include A continuación, definimos los UUIDs (Identificadores Únicos Universales) para nuestro Servicio y Característica. Estas son etiquetas de identificación especiales que ayudan a otros dispositivos a reconocer exactamente qué tipo de datos estamos ofreciendo. Si deseas usar tus propios IDs únicos en lugar de los predeterminados proporcionados aquí, puedes generar nuevos utilizando un generador de UUID en línea.
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"Dentro de la función de configuración, primero abrimos la conexión serial a una velocidad de 115200.
Luego, iniciamos el stack BLE utilizando el método init() y pasamos el nombre "MyESP32". Esta cadena es simplemente el nombre público que tu ESP32 transmitirá al mundo. Así, cuando saques tu teléfono para escanear dispositivos Bluetooth cercanos, sabrás exactamente cuál es el tuyo. Puedes cambiar este nombre a cualquier cosa que desees para que tu dispositivo se destaque en un escaneo Bluetooth abarrotado.
NimBLEDevice::init("MyESP32");Una vez que el stack BLE está inicializado, creamos el servidor que manejará todas las conexiones entrantes.
NimBLEServer *pServer = NimBLEDevice::createServer();Inmediatamente después de crear el servidor, creamos un nuevo servicio dentro de él y adjuntamos el UUID del servicio que definimos anteriormente.
NimBLEService *pService = pServer->createService(SERVICE_UUID);A continuación, creamos una característica dentro de ese servicio. Una característica es donde se almacena el valor real. Al configurarla, pasamos nuestro UUID de característica junto con un conjunto específico de permisos. Asignamos las propiedades READ y WRITE para dictar exactamente lo que los dispositivos conectados pueden hacer. Con READ, el teléfono puede leer el valor del ESP32. Con WRITE, el teléfono puede enviar un nuevo valor al ESP32.
Combinamos estas propiedades utilizando el símbolo |. Este símbolo nos permite unir múltiples opciones. Así que en lugar de dar a la característica solo una habilidad, le damos dos: leer y escribir.
Si alguna vez olvidas conceder estos permisos, tus dispositivos conectados encontrarán errores frustrantes como “GATT WRITE NOT ALLOWED” cuando intenten interactuar con tu ESP32.
NimBLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
NIMBLE_PROPERTY::READ |
NIMBLE_PROPERTY::WRITE
);Ahora que nuestra característica está construida y tiene los permisos correctos, le damos algunos datos iniciales utilizando el método setValue(). Aquí, estamos cargando el simple mensaje de texto "Hello World!". Así que cuando un teléfono se conecta y lee esta característica, recibirá "Hello World!".
Por supuesto, puedes reemplazar esto con algo más útil, como un valor de sensor, un estado de botón, un estado de LED o cualquier otra información que desees enviar a través de BLE.
pCharacteristic->setValue("Hello World!");A continuación, iniciamos el servicio usando pService->start();. Este paso completa la configuración del servicio. Si omites este paso, tu servicio y sus características permanecerán completamente ocultos e inaccesibles para el mundo exterior.
Después de configurar el servicio y la característica, pasamos a la publicidad. La publicidad es cómo el ESP32 se anuncia a los dispositivos BLE cercanos.
Obtenemos el objeto de publicidad utilizando NimBLEDevice::getAdvertising();. Esto nos da acceso a la configuración de publicidad, para que podamos decidir qué información compartirá el ESP32 mientras espera que un dispositivo se conecte.
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();A continuación, establecemos el nombre publicitado utilizando pAdvertising->setName("MyESP32");. Esto ayuda a un escáner BLE a mostrar un nombre amigable en lugar de solo un dispositivo desconocido o una dirección de hardware. Si deseas que tu ESP32 aparezca con un nombre diferente, puedes cambiar MyESP32 a algo más.
pAdvertising->setName("MyESP32");Luego, agregamos el UUID del servicio a los datos de publicidad utilizando pAdvertising->addServiceUUID(SERVICE_UUID);. Esto permite que los dispositivos que están escaneando sepan que nuestro ESP32 está ofreciendo este servicio BLE en particular.
pAdvertising->addServiceUUID(SERVICE_UUID);Después de eso, habilitamos la respuesta de escaneo utilizando pAdvertising->enableScanResponse(true);. Los paquetes de publicidad BLE son pequeños, por lo que a veces el ESP32 no puede incluir toda la información útil en el paquete principal de publicidad. La respuesta de escaneo permite que el ESP32 envíe información adicional cuando un escáner solicita más detalles. Esto puede ayudar a que el nombre del dispositivo y la información del servicio aparezcan de manera más confiable en las aplicaciones de escaneo.
pAdvertising->enableScanResponse(true);Finalmente, comenzamos la publicidad con pAdvertising->start();. En este punto, el ESP32 comienza a transmitirse.
La función loop() está casi vacía en este ejemplo porque NimBLE maneja las tareas BLE en segundo plano. Si deseas expandir este proyecto, puedes utilizar la función loop() para leer sensores, verificar botones, actualizar el valor de la característica o enviar notificaciones a un dispositivo conectado sin interrumpir la conexión Bluetooth.
void loop() {
// El programador FreeRTOS maneja las tareas BLE en segundo plano;
// el loop principal puede permanecer vacío o manejar otra lógica de sensores.
delay(2000);
}Usando nRF Connect para pruebas
Para probar la conexión BLE, necesitarás emparejar el ESP32 con tu smartphone y también necesitarás instalada una aplicación de depuración Bluetooth. Existen varias opciones disponibles; una de nuestras favoritas es nRF Connect de Nordic, que está disponible tanto para dispositivos iOS como Android. Es una herramienta poderosa que te permite escanear y explorar tus dispositivos BLE y comunicarte con ellos.
1. Ve a la Google Play Store o a la Apple App Store y busca "nRF Connect for Mobile". Instala la aplicación y ábrela.
2. Asegúrate de que el Bluetooth de tu teléfono esté activado.
3. En la aplicación, toca el botón “ESCANEAR”. La aplicación comenzará a escanear dispositivos BLE cercanos.
4. Apresionarás una lista de dispositivos disponibles con sus respectivas intensidades de señal y otros detalles. Busca “MyESP32” y haz clic en el botón “Conectar” junto a ese nombre.
5. Serás llevado a la vista de “Servicios”. Allí verás una lista de servicios disponibles. Haz clic en “Servicio Desconocido”; el UUID de la cadena debería coincidir con el SERVICE_UUID en tu código de ejemplo.
6. Toca un servicio para ver sus características asociadas. Al lado de cada característica, verás dos íconos. La flecha hacia abajo permite leer una característica, mientras que la flecha hacia arriba permite escribir en ella.
7. Toca la flecha hacia abajo para leer la característica. Verás el UUID de la característica, las propiedades de LECTURA y ESCRITURA, y el valor “Hello World!”, exactamente como se especificó en nuestro código.
Deja una respuesta

Estos temas te pueden interesar