From 47ecf2e43c13f7730118d6bcc9363af33b8dde28 Mon Sep 17 00:00:00 2001 From: Sergio Semedi Date: Sat, 15 Nov 2025 14:22:10 +0100 Subject: [PATCH] upload learn from scratch book, exercises and projects --- learn/BLE_ESP32_Fundamentals.md | 1782 +++++++++++++++++++++++++++ learn/BLE_Projects_roadmap_ideas.md | 516 ++++++++ learn/BLE_example_exercises.md | 986 +++++++++++++++ 3 files changed, 3284 insertions(+) create mode 100644 learn/BLE_ESP32_Fundamentals.md create mode 100644 learn/BLE_Projects_roadmap_ideas.md create mode 100644 learn/BLE_example_exercises.md diff --git a/learn/BLE_ESP32_Fundamentals.md b/learn/BLE_ESP32_Fundamentals.md new file mode 100644 index 0000000..fdbdfa3 --- /dev/null +++ b/learn/BLE_ESP32_Fundamentals.md @@ -0,0 +1,1782 @@ +# BLE + ESP32: Masterclass Teórico-Práctico + +## De Cero a Experto en Bluetooth Low Energy y Desarrollo Embedded + +--- + +## TABLA DE CONTENIDOS + +1. [Módulo 0: Fundamentos de BLE](#módulo-0-fundamentos-de-ble) +2. [Módulo 1: Advertising & Discovery](#módulo-1-advertising--discovery) +3. [Módulo 2: Connections & GATT](#módulo-2-connections--gatt) +4. [Módulo 3: Security & Advanced Topics](#módulo-3-security--advanced-topics) +5. [Debugging Masterclass](#debugging-masterclass) +6. [Ejercicios Prácticos Progresivos](#ejercicios-prácticos-progresivos) +7. [Referencias & Recursos](#referencias--recursos) + +--- + +# MÓDULO 0: FUNDAMENTOS DE BLE + +## 0.1 ¿Qué es Bluetooth Low Energy (BLE)? + +BLE es una tecnología inalámbrica diseñada para **baja potencia, corto alcance y bajo consumo de energía**. Se diferencia del **Classic Bluetooth** (BR/EDR) en: + +| Característica | Classic BT | BLE | +|---|---|---| +| **Potencia** | ~100 mW | ~10 mW promedio | +| **Alcance** | ~100 m | ~100 m (BLE 5.x) | +| **Latencia** | 100 ms | <10 ms | +| **Ancho de banda** | 720 kbps | 1-2 Mbps | +| **Caso de uso** | Audio, streaming | Sensores, IoT, wearables | +| **Stack** | Complejo (Bluedroid+BR/EDR) | Simplificado (NimBLE BLE-only) | + +**¿Por qué BLE importa en IoT/Edge AI?** +- Actualizaciones de modelos ML en dispositivos wearables sin descargar batería. +- Comunicación entre edge devices (sensores + nodos) con bajo overhead. +- Integración con blockchain: coordinación de múltiples IoT nodes. + +--- + +## 0.2 Arquitectura del Stack BLE + +El stack BLE se organiza en **capas**, de forma similar a OSI: + +``` +┌─────────────────────────────────────┐ +│ APLICACIÓN (Application) │ ← Tu código +├─────────────────────────────────────┤ +│ GATT (Generic Attribute Profile) │ ← Servicios y características +├─────────────────────────────────────┤ +│ GAP (Generic Access Profile) │ ← Advertising, conexiones +├─────────────────────────────────────┤ +│ ATT (Attribute Protocol) │ ← Lectura/escritura de atributos +├─────────────────────────────────────┤ +│ L2CAP (Logical Link Control) │ ← Multiplexación de canales +├─────────────────────────────────────┤ +│ Link Layer (Enlace de datos) │ ← Control de conexión, hop freq +├─────────────────────────────────────┤ +│ PHY (Física) │ ← Radio 2.4 GHz, modulación +└─────────────────────────────────────┘ + │ + ├─── CONTROLLER ───────────────────── (Firmware en el chip) + │ (HCI: Host Controller Interface) + │ + └─── HOST ─────────────────────────── (Software en tu aplicación) + (Bluedroid o NimBLE en ESP32) +``` + +### Conceptos Clave: + +**1. Controller vs Host** +- **Controller**: Firmware bajo nivel en el chip (RF, Link Layer). No lo tocas en ESP32; está pre-compilado. +- **Host**: Software que interpreta comandos del aplicación y genera HCI commands para el controller. +- **HCI (Host Controller Interface)**: Protocolo de comunicación entre host y controller. En ESP32 usa transporte RAM (no UART). + +**2. Roles BLE** +- **Peripheral (Esclavo)**: Anuncia datos, acepta conexiones. Ej: reloj inteligente. +- **Central (Maestro)**: Escanea, inicia conexiones. Ej: smartphone. + +**3. Estados GAP** +- **Standby**: Inactivo. +- **Advertising**: Transmite paquetes de publicidad. +- **Scanning**: Escucha paquetes de publicidad. +- **Connected**: Vinculado con otra dispositivo. + +--- + +## 0.3 Physical Layer (PHY): La Radio + +### Banda de Frecuencia + +BLE opera en la banda ISM (Industrial, Scientific, Medical) de **2.4 GHz**, compartida con WiFi, Zigbee, etc. + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 2.4 GHz ISM Band (2400 - 2483.5 MHz) │ +├─────────────────────────────────────────────────────────────┤ +│ BLE: 37 canales de datos + 3 canales de advertising │ +│ Espaciados cada 2 MHz │ +│ │ +│ Ch 37: 2402 MHz (Adv) │ +│ Ch 38: 2426 MHz (Adv) │ +│ Ch 39: 2480 MHz (Adv) │ +│ │ +│ Ch 0-36: 2404 - 2480 MHz (Datos + algunas adv ext) │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Modulación y Velocidades (PHYs) + +| PHY | Velocidad | Modulación | Uso | +|---|---|---|---| +| **LE 1M** (default) | 1 Mbps | GFSK | Compatibilidad, bajo costo | +| **LE 2M** (BLE 5.0+) | 2 Mbps | GFSK | Mayor throughput, menos batería en tx | +| **LE Coded** (BLE 5.0+) | 125/500 kbps | GFSK + FEC | Largo alcance, menor SNR | + +**Nota**: Todos los chips ESP32 modernos soportan LE 1M. LE 2M y LE Coded dependen de la variante (C3, S3, etc.). + +### Formato de Paquete BLE (Uncoded PHY) + +``` +┌───────┬──────────────┬───────────┬─────┐ +│Preamble│Access Address│ PDU │ CRC │ +│ 1 byte │ 4 bytes │ 2-257B │3 byte│ +└───────┴──────────────┴───────────┴─────┘ + └────────────────────────────────┘ + Link Layer Packet (LL PDU) +``` + +- **Preamble** (0xAA/0x55): Sincronización de frecuencia. +- **Access Address** (4B): ID del dispositivo o conexión. +- **PDU**: Datos; puede ser advertising PDU o data channel PDU. +- **CRC** (3B): Cyclic Redundancy Check para detectar errores. + +--- + +## 0.4 Link Layer: La Inteligencia + +### Frequency Hopping (AFH) + +BLE evita colisiones e interferencias usando **Adaptive Frequency Hopping (AFH)**: + +1. **Secuencia pseudo-aleatoria**: Los dispositivos emparejados comparten un `Access Address` y un `hop increment` (derivado de direcciones MAC). +2. **Salto por conexión**: Salta entre los 37 canales de datos cada `connection interval` (7.5 ms - 4 s). +3. **Channel map**: Si ciertos canales tienen mucho ruido, se excluyen dinámicamente. + +**Fórmula**: Nuevo canal = (Anterior + hop_increment) % 37 + +```python +# Pseudocódigo del hop +def next_channel(current_ch, hop_inc, channel_map): + """Calcula el próximo canal en el hop.""" + unmapped_channels = channel_map # Máscara de 37 bits + + next_unmapped = (current_ch + hop_inc) % 37 + # Si está excluido, reintenta con el siguiente + while not test_bit(unmapped_channels, next_unmapped): + next_unmapped = (next_unmapped + 1) % 37 + + return next_unmapped +``` + +### Paquetes Advertising PDU + +Formato de un paquete de publicidad: + +``` +┌──────────────────┬─────────────────────────┐ +│ Advertising Hdr │ Advertising Payload │ +├──────────────────┼─────────────────────────┤ +│ Tipo | Tx/Rx Adr │ MAC + Datos Payload │ +│ 1B │ 1B │ ≤ 255 bytes │ +└──────────────────┴─────────────────────────┘ +``` + +**Tipos de Advertising PDU:** +- `ADV_IND` (0x00): Connectable undirected (periférico abierto). +- `ADV_DIRECT_IND`: Connectable directed (a un central específico). +- `ADV_NONCONN_IND`: Non-connectable (beacon). +- `ADV_SCAN_IND`: Scannable undirected (periférico + scan response). +- `SCAN_RSP`: Respuesta a scan request. + +--- + +## 0.5 ATT & GATT: Estructura de Datos + +### ATT (Attribute Protocol) + +ATT es el protocolo **crudo** de lectura/escritura de **atributos** (bloques de datos con UUID). + +**Operaciones ATT:** +``` +Cliente (Central) Servidor (Peripheral) + │ │ + ├─ Read Request ────────────────> │ + │ Read Response │ + │ <────────────────────────────────┤ + │ │ + ├─ Write Request ───────────────> │ + │ Write Response │ + │ <────────────────────────────────┤ + │ │ + ├─ Notify (unsolicited) <───────── │ + │ (sin confirmación) │ + │ │ + ├─ Indicate (unsolicited) <────── │ + │ (con confirmación) │ + │ ─── Confirmation ───────────> │ +``` + +### GATT (Generic Attribute Profile) + +GATT es una **capa de semántica** sobre ATT que organiza atributos en: + +1. **Servicios**: Colecciones de funcionalidad. Ej: Heart Rate Service (HRS). +2. **Características**: Propiedades dentro de un servicio. Ej: Heart Rate Measurement. +3. **Descriptores**: Metadatos de una característica. Ej: CCCD (Client Characteristic Configuration). + +**Estructura GATT:** +``` +Service (UUID 0x180D = Heart Rate Service) + ├─ Characteristic 1 (UUID 0x2A37 = Heart Rate Measurement) + │ ├─ Value: [BPM value] + │ ├─ Properties: Read, Notify + │ └─ Descriptor (CCCD): [0x01, 0x00] = Notificaciones enabled + │ + └─ Characteristic 2 (UUID 0x2A38 = Body Sensor Location) + ├─ Value: [0x01 = Chest] + └─ Properties: Read +``` + +**CCCD (Client Characteristic Configuration Descriptor)**: +- Bitmask que el cliente escribe para activar/desactivar notificaciones. +- `0x0001`: Notificaciones. +- `0x0002`: Indicaciones. + +--- + +## 0.6 Bluedroid vs NimBLE: Comparativa Técnica + +### ESP-Bluedroid + +**Arquitectura:** +``` +┌─────────────────┐ +│ esp_* APIs │ ← Tu código +├─────────────────┤ +│ BTC (Bluetooth │ ← Profile manager, HCI handlers +│ Transport Ctrl) │ +├─────────────────┤ +│ BTU (Bluetooth │ ← L2CAP, GATT, SMP, ATT +│ Upper Layer) │ +├─────────────────┤ +│ HCI Interface │ ← Comandos al controller +└─────────────────┘ +``` + +**Características:** +- Soporta Classic BT (BR/EDR) + BLE. +- Memora RAM: **~200 KB heap** típicamente. +- Velocidad init: ~631 ms (ver benchmark). +- Mayor footprint de código. + +**Cuándo usar Bluedroid:** +- Necesitas Classic BT (audio Bluetooth clásico). +- Compatibilidad máxima con código Android. + +### Apache NimBLE + +**Arquitectura:** +``` +┌─────────────────┐ +│ ble_* APIs │ ← Tu código +├─────────────────┤ +│ NimBLE Host │ ← Task separado en FreeRTOS +│ (event-driven) │ +├─────────────────┤ +│ HCI Interface │ ← Comandos al controller +└─────────────────┘ +``` + +**Características:** +- **BLE-only** (no Classic BT). +- RAM: **~70-80 KB heap**, mucho más eficiente. +- Velocidad init: **~209 ms** (3x más rápido). +- Open-source Apache license. +- Mejor para deep sleep / battery-powered devices. + +**Cuándo usar NimBLE:** +- Solo necesitas BLE (99% de IoT moderno). +- Quieres optimizar potencia y memoria. +- Prefieres open-source. + +### Recomendación + +**🎯 Para tu roadmap (Edge AI + IoT + Blockchain):** + +Usa **NimBLE** porque: +1. Menor footprint → más espacio para ML models. +2. Mejor para federated learning (múltiples nodos IoT). +3. Comunidad open-source muy activa (Apache). +4. Por defecto moderno en nuevos proyectos ESP32. + +--- + +## 0.7 Seguridad BLE: Introducción Conceptual + +### Legacy Pairing (BLE 4.1 y anteriores) + +1. **Intercambio de claves Temporary Link Key (TK)** basado en IO capabilities. +2. **Generación STK** (Short Term Key) via algoritmo simple (vulnerable). +3. **Encriptación** de la conexión con STK. + +**Vulnerabilidades conocidas:** +- KNOB attack (Key Negotiation Of Bluetooth). +- Downgrade attacks. + +### LE Secure Connections (LESC, BLE 4.2+) + +1. **Generación de pares ECDH** (Elliptic Curve Diffie-Hellman) en cada dispositivo. +2. **Intercambio de claves públicas** (seguro matemáticamente). +3. **Generación LTK** (Long Term Key) determinista y fuerte. +4. **Variantes de autenticación:** + - **Numeric Comparison**: Ambos muestran 6 dígitos; usuario confirma si coinciden. + - **Passkey Entry**: Uno muestra código, otro lo digita. + - **Just Works**: Sin confirmación (sin MITM protection). + - **Out Of Band (OOB)**: Intercambio de claves por canal alternativo (NFC, etc.). + +**Matemática simplificada:** +``` +Dispositivo A: d_a = random(); Q_a = d_a * G (ECDH público) +Dispositivo B: d_b = random(); Q_b = d_b * G + +Intercambio: Q_a <-> Q_b + +Shared secret: DHKey = d_a * Q_b = d_b * Q_a = (d_a * d_b) * G + +LTK = PRES(DHKey, other_params) [derivado del shared secret] +``` + +**En ESP32:** +- LESC es el default en NimBLE. +- Generación ECDH es **blocking ~160 ms** (limitación ROM). +- Requiere supervision_timeout >= 165 ms durante pairing. + +--- + +## 0.8 Consumo de Potencia y Parámetros Críticos + +### Ecuación General + +``` +Consumo promedio = (T_on * I_active + T_sleep * I_sleep) / T_total +``` + +### Parámetros de Conexión + +| Parámetro | Rango | Impacto | +|---|---|---| +| **Connection Interval** | 7.5 ms - 4 s | Menor = mayor latencia pero más potencia | +| **Peripheral Latency** | 0 - 499 | Nodo puede saltear N intervalos (reduce despertares) | +| **Supervision Timeout** | 100 ms - 32 s | Si no recibe dato en este tiempo, desconecta | +| **MTU (Max Transfer Unit)** | 23 - 251 bytes | Mayor MTU = menos fragmentación, menos paquetes | + +**Ejemplo optimizado para batería:** +```c +// Connection interval 1 segundo, permite saltar 199 de cada 200 eventos +ble_gap_upd_params conn_params = { + .itvl_min = 800, // 1000 ms + .itvl_max = 800, + .latency = 199, // Puede saltear hasta 199 intervalos + .supervision_timeout = 320, // 3.2 segundos + .min_ce_len = 0x00, + .max_ce_len = 0x00 +}; +``` + +--- + +# MÓDULO 1: ADVERTISING & DISCOVERY + +## 1.1 Ciclo de Vida del Advertising + +### Estados del Periférico + +``` + ┌─────────────┐ + │ STANDBY │ + │ (inactivo) │ + └──────┬──────┘ + │ esp_ble_gap_start_advertising() + ▼ + ┌─────────────┐ + │ ADVERTISING │◄──────────────────────┐ + │ (cada ~100 │ │ + │ eventos, │ │ + │ hopping │ │ + │ canales) │ │ + └──────┬──────┘ │ + │ (remoto) Connection request │ + ▼ │ + ┌─────────────┐ │ + │ CONNECTED │────► Disconnect ──────┘ + │ │ + └─────────────┘ +``` + +### Advertising Event + +**Cada N milisegundos:** + +1. ESP32 transmite paquete de publicidad en **Ch 37**. +2. Espera ~120 μs por **SCAN_REQ** (si scannable). +3. Si recibe SCAN_REQ, transmite **SCAN_RSP** en Ch 37. +4. Salta a **Ch 38**, repite. +5. Salta a **Ch 39**, repite. +6. Espera hasta el próximo `advertising interval` (ej: 100 ms). + +--- + +## 1.2 Estructura de Advertising Data + +Los datos de publicidad se empaquetan en **TLV (Type-Length-Value)** blocks: + +``` +┌──────┬────────┬─────────────────┐ +│ Type │ Length │ Value │ +│ 1B │ 1B │ Length bytes │ +└──────┴────────┴─────────────────┘ + ^ ^ ^ + │ │ └─ Contenido real (flags, UUID, etc.) + │ └─ Tamaño en bytes + └─ Tipo de dato (0x01 = Flags, 0x08 = Name, etc.) +``` + +### Tipos Comunes (AD Types) + +| AD Type | Valor | Significado | +|---------|-------|-----------| +| `0x01` | Flags | Propiedades del dispositivo | +| `0x08` | Shortened Local Name | Nombre corto | +| `0x09` | Complete Local Name | Nombre completo | +| `0x0A` | TX Power Level | Potencia de transmisión | +| `0x16` | Service Data (16-bit UUID) | Datos asociados a servicio | +| `0x20` | Service Data (32-bit UUID) | Idem pero UUID 32-bit | +| `0x21` | Service Data (128-bit UUID) | Idem pero UUID 128-bit | + +### Flags Comunes + +```c +#define ESP_BLE_ADV_FLAG_LIMIT_DISC 0x01 // Limited Discoverable Mode +#define ESP_BLE_ADV_FLAG_GEN_DISC 0x02 // General Discoverable Mode +#define ESP_BLE_ADV_FLAG_BREDR_NOT_SPT 0x04 // BR/EDR Not Supported +#define ESP_BLE_ADV_FLAG_DMR_CONTROLLER 0x08 // Dual Mode Controller +#define ESP_BLE_ADV_FLAG_DMR_HOST 0x10 // Dual Mode Host +``` + +**Ejemplo de payload de advertising completo:** + +``` +01 01 06 → Flags: 0x06 (General Discoverable, BR/EDR Not Supported) +09 09 45 53 50 33 32 5F 42 4C 45 → Name: "ESP32_BLE" (9 bytes) +02 0A F4 → TX Power: -12 dBm (0xF4 = -12 en two's complement) + +Total: 15 bytes de 31 disponibles +``` + +--- + +## 1.3 Beacons: Tipos y Formatos + +Los **beacons** son dispositivos que no necesitan conexión; solo transmiten datos periódicamente. + +### iBeacon (Apple) + +**Formato:** +``` +Company ID (Apple 0x004C) + Type + Major + Minor + TX Power + 2B + 1B + 2B + 2B + 1B +``` + +**Ejemplo en Service Data:** +```c +uint8_t adv_data[] = { + 0x02, 0x01, 0x06, // Flags + 0x1A, 0xFF, // Service Data type + 0x4C, 0x00, // Company ID: 0x004C (Apple) + 0x02, 0x15, // iBeacon type (0x02) + version (0x15) + // UUID: 12345678-1234-5678-1234-56789ABCDEF0 + 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, + 0x12, 0x34, // Major: 0x1234 + 0x56, 0x78, // Minor: 0x5678 + 0xC3 // TX Power: -61 dBm +}; +``` + +### Eddystone (Google/open) + +Más flexible que iBeacon. Formatos: + +- **Eddystone-URL**: URL corta (prefijo comprimido). +- **Eddystone-UID**: UUID 16B + instancia (sensores). +- **Eddystone-TLM**: Telemetría (batería, temp). +- **Eddystone-EID**: Ephemeral ID (privacidad). + +--- + +## 1.4 API: Bluedroid Beacon + +### Código Ejemplo (Conceptual) + +```c +#include "esp_gatt_defs.h" +#include "esp_gap_ble_api.h" +#include "esp_bt.h" + +// Advertising data: Name + TX Power +static uint8_t adv_data[] = { + 0x02, 0x01, 0x06, // Flags + 0x09, 0x09, 'B', 'L', 'U', 'E', 'D', 'R', 'O', 'I', 'D', // Name + 0x02, 0x0A, 0xF4 // TX Power: -12 dBm +}; +static uint8_t scan_rsp_data[] = { + 0x02, 0x0A, 0xF4, // TX Power + 0x0B, 0x09, 'h', 't', 't', 'p', 's', ':', '/', '/', 'e', 's', 'p' +}; + +// GAP Callback +static void esp_gap_cb(esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t *param) { + switch (event) { + case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: + ESP_LOGI("GAP", "Advertising data set"); + esp_ble_gap_start_advertising(&adv_params); + break; + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + if (param->adv_start_cmpl.status == ESP_BT_STATUS_SUCCESS) { + ESP_LOGI("GAP", "Advertising started"); + } + break; + // ... más eventos + } +} + +void app_main(void) { + // Inicializar NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + nvs_flash_erase(); + nvs_flash_init(); + } + + // Liberar memoria de Classic BT + esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); + + // Init Bluetooth + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + + // Init Bluedroid + esp_bluedroid_init(); + esp_bluedroid_enable(); + + // Registrar callback GAP + esp_ble_gap_register_callback(esp_gap_cb); + + // Configurar parámetros de advertising + esp_ble_adv_params_t adv_params = { + .adv_int_min = 0x0020, // 20 ms + .adv_int_max = 0x0040, // 40 ms + .adv_type = ADV_TYPE_NONCONN_IND, // Non-connectable + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, + }; + + // Establecer datos de advertising + esp_ble_gap_config_adv_data_raw(adv_data, sizeof(adv_data)); + esp_ble_gap_config_scan_rsp_data_raw(scan_rsp_data, sizeof(scan_rsp_data)); +} +``` + +--- + +## 1.5 API: NimBLE Beacon + +### Código Ejemplo (Conceptual) + +```c +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "nimble/nimble_port.h" + +static uint8_t own_addr_type; + +// Inicializar GAP +static int adv_init(void) { + struct ble_svc_gap_params gap_params; + struct ble_hs_cfg cfg; + struct ble_gap_adv_params adv_params; + struct ble_gap_adv_set_fields adv_fields; + + // Asegurar dirección válida + ble_hs_util_ensure_addr(0); + ble_hs_id_infer_auto(0, &own_addr_type); + + // Formato de datos de advertising + memset(&adv_fields, 0, sizeof(adv_fields)); + + // Flags + adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + + // Nombre + adv_fields.name = (uint8_t *)"NimBLE_Beacon"; + adv_fields.name_len = strlen("NimBLE_Beacon"); + adv_fields.name_is_complete = 1; + + // TX Power + adv_fields.tx_pwr_lvl_is_present = 1; + adv_fields.tx_pwr_lvl = -12; // dBm + + rc = ble_gap_adv_set_fields(&adv_fields); + if (rc != 0) { + ESP_LOGE("ADV", "ble_gap_adv_set_fields failed: %d", rc); + return rc; + } + + // Configurar parámetros de advertising + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; // Non-connectable + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; // General discoverable + adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(20); // 20 ms + adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(40); // 40 ms + + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, + NULL, NULL); + if (rc != 0) { + ESP_LOGE("ADV", "ble_gap_adv_start failed: %d", rc); + return rc; + } + + return 0; +} + +// Sync callback (cuando NimBLE está listo) +static void ble_app_on_sync(void) { + int rc = adv_init(); + assert(rc == 0); +} + +void app_main(void) { + // Inicializar NVS + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + nvs_flash_erase(); + nvs_flash_init(); + } + + // Init Bluetooth + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + + // Init NimBLE + nimble_port_init(); + + // Registrar callback sync + ble_hs_cfg.sync_cb = ble_app_on_sync; + + // Iniciar stack NimBLE + nimble_port_freertos_init(BLE_HS_TASK_PRIORITY); +} +``` + +--- + +## 1.6 Ejercicio Práctico 1: Beacon Custom + +**Objetivo**: Crear un beacon que transmita datos custom (temperatura, estado sensor). + +**Requisitos:** +1. Usa NimBLE. +2. Implementa Service Data con UUID 16-bit custom. +3. Transmite 2 bytes (temperatura + humedad mocked). +4. Advertising interval: 100 ms. +5. TX Power: -10 dBm. + +**Pasos:** +1. Crea un proyecto nuevo en `examples/bluetooth/ble_beacon_custom/`. +2. Incluye lectura ADC simulada (temperatura mocked). +3. Actualiza datos cada 500 ms (sin reiniciar advertising). +4. Verifica en nRF Connect que los datos cambian. + +--- + +# MÓDULO 2: CONNECTIONS & GATT + +## 2.1 Ciclo de Vida de Conexión + +### Central (Smartphone, Tuya App, etc.) + +``` +SCAN ──> DISCOVER ──> CONNECT ──> GATT ──> DISCONNECT + │ │ │ │ + │ Busca │ Envía │ Espera │ Lee/ + │ beacons │ LE_CONN │ CONNECT │ Escribe/ + │ que se │ _REQ en │ _IND │ Notif + │ anuncian│ Ch 37/38/ │ │ + │ │ 39 │ │ +``` + +### Periférico (ESP32) + +``` +ADVERTISING ──> CONNECT_IND ──> CONNECTED ──> [GATT] ──> DISCONNECT + │ │ │ + │ Transmite │ Central envia │ Intercambia + │ en 37/38/39 │ LE_CONN_REQ en │ atributos y + │ │ cualquier canal │ datos + │ │ (parámetros) │ +``` + +--- + +## 2.2 Connection Request: Parámetros Clave + +Cuando un central inicia conexión, negocia: + +```c +struct ble_gap_conn_params { + uint16_t scan_itvl; // Scan interval (central) + uint16_t scan_window; // Scan window (central) + uint16_t itvl_min; // Min connection interval (periférico) + uint16_t itvl_max; // Max connection interval + uint16_t latency; // Peripheral latency + uint16_t supervision_timeout; // Supervision timeout + uint16_t min_ce_len; // Min connection event length + uint16_t max_ce_len; // Max connection event length +}; +``` + +**Connection Interval**: Tiempo entre dos eventos de conexión. +- Rango: 7.5 ms - 4 s (en múltiplos de 1.25 ms). +- **Valores típicos:** + - Low latency: 7.5 - 30 ms (gaming, streaming). + - Normal: 30 - 100 ms (sensores, actuadores). + - Battery: 1 - 4 s (wearables, medidores). + +**Peripheral Latency**: Nodo puede ignorar N connection events. +- Ej: latency=199 en 1s interval = nodo responde cada 200s (5 minutos). +- **Trade-off**: Menor consumo vs. menor responsividad. + +**Supervision Timeout**: Máximo tiempo sin recibir datos antes de desconexión. +- Debe ser > (1 + latency) * interval_max * 2. +- Ej: interval=100ms, latency=199 → timeout >= 40 segundos. + +--- + +## 2.3 MTU Negotiation + +**MTU (Maximum Transmission Unit)**: Máximo tamaño de datos útil en ATT. + +``` +┌──────────────┬────────────┬─────────────────┐ +│ Link Layer │ L2CAP HDR │ ATT Data │ +│ (4 bytes) │ (4 bytes) │ (= MTU - 3) │ +└──────────────┴────────────┴─────────────────┘ + │ + Total ≤ 251 bytes (BLE packet limit) +``` + +- **Default MTU**: 23 bytes → 20 bytes de datos útil. +- **Max MTU**: 251 bytes → 248 bytes de datos útil. + +**Negociación:** +``` +Client → Server: MTU Exchange Request (MTU_desired = 128) +Server → Client: MTU Exchange Response (MTU_server = 251) +Negociado: MTU = min(128, 251) = 128 +``` + +**En NimBLE:** +```c +// Configurar MTU esperado durante init +ble_hs_cfg.att_max_mtu = 251; + +// En runtime, solicitar MTU desde central +ble_gattc_exchange_mtu(conn_handle); +``` + +--- + +## 2.4 GATT Services y Characteristics + +### Estructura Jerárquica + +``` +┌────────────────────────────┐ +│ SERVICE (HRS 0x180D) │ +│ ├─ Start Handle: 1 │ +│ └─ End Handle: 10 │ +├────────────────────────────┤ +│ CHARACTERISTIC 1 (0x2A37) │ +│ ├─ Handle: 2 │ +│ ├─ Value Handle: 3 │ +│ ├─ Properties: R,N │ +│ │ │ +│ └─ DESCRIPTOR CCCD │ +│ ├─ Handle: 4 │ +│ └─ Value: [0x00, 0x00] │ +├────────────────────────────┤ +│ CHARACTERISTIC 2 (0x2A38) │ +│ ├─ Handle: 5 │ +│ ├─ Value Handle: 6 │ +│ └─ Properties: R │ +└────────────────────────────┘ +``` + +### Declaración de Servicio (Bluedroid) + +```c +// Tabla de atributos +static const esp_gatts_attr_db_t gatt_db[GATTS_CHAR_IDX_NB] = { + // Declaración de servicio + [0] = { + {ESP_GATT_AUTO_RSP}, + { + ESP_UUID_LEN_16, + (uint8_t *)&primary_service_uuid, // 0x2800 + ESP_GATT_PERM_READ, + 2, // 16-bit UUID de servicio + (uint8_t *)&service_uuid // 0x180D (HRS) + } + }, + // Declaración de característica + [1] = { + {ESP_GATT_AUTO_RSP}, + { + ESP_UUID_LEN_16, + (uint8_t *)&character_declaration_uuid, // 0x2803 + ESP_GATT_PERM_READ, + 1, // Properties + (uint8_t *)&char_prop_read_notify // 0x12 + } + }, + // Valor de característica + [2] = { + {ESP_GATT_AUTO_RSP}, + { + ESP_UUID_LEN_16, + (uint8_t *)&heart_rate_char_uuid, // 0x2A37 + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + 8, + (uint8_t *)&heart_rate_value // [BPM] + } + }, + // CCCD Descriptor + [3] = { + {ESP_GATT_AUTO_RSP}, + { + ESP_UUID_LEN_16, + (uint8_t *)&character_client_config_uuid, // 0x2902 + ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, + 2, + (uint8_t *)&cccd_value // [0x00, 0x00] + } + } +}; +``` + +### Declaración de Servicio (NimBLE) + +```c +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(0x180D), // Heart Rate Service + .characteristics = (struct ble_gatt_chr_def[]) { + { + .uuid = BLE_UUID16_DECLARE(0x2A37), // HRM + .access_cb = gatt_svr_chr_access, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, + .descriptors = (struct ble_gatt_dsc_def[]) { + { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16), + .access_cb = gatt_svr_dsc_access, + .flags = BLE_GATT_DSC_F_WRITE, + }, + {0} + }, + }, + {0} + }, + }, + {0} +}; +``` + +--- + +## 2.5 Notificaciones vs Indicaciones + +| Propiedad | Notificación | Indicación | +|-----------|--------------|-----------| +| **Confirmación** | No | Sí | +| **Velocidad** | Rápida | Más lenta | +| **Fiabilidad** | Sin garantía | Garantizada | +| **Caso de uso** | Streaming datos | Eventos críticos | +| **Evento NimBLE** | `BLE_GAP_EVENT_NOTIFY_TX` | `BLE_GAP_EVENT_CONN_UPDATE` | + +**Flujo de Indicación:** +``` +Peripheral → INDICATE → Central + │ + ▼ Procesa dato + │ + ▼ +Peripheral ← CONFIRM ← Central + ↓ +Evento: BLE_GAP_EVENT_INDICATE_TX +``` + +**Flujo de Notificación:** +``` +Peripheral → NOTIFY → Central + (sin confirmación) +``` + +--- + +## 2.6 Ejercicio Práctico 2: GATT Server + LED Control + +**Objetivo**: Crear un periférico que expone dos características: +1. **LED Control** (write): Escribe 0x00 = OFF, 0x01 = ON. +2. **Button Status** (read + notify): Lee estado de botón cada 1 segundo. + +**Requisitos:** +1. Usa NimBLE. +2. Servicios: + - Automation IO service (custom UUID 128-bit). + - LED write characteristic. + - Button read characteristic (con CCCD). +3. Connection parameters: interval 30 ms, latency 0, timeout 3 s. +4. MTU: 128 bytes. + +**Pasos:** +1. Configura GPIO (LED en Pin 2, Button en Pin 0). +2. Implementa `gatt_svr_chr_access` para manejar reads/writes. +3. Crea task que envíe notificaciones cada 1 segundo. +4. Verifica en nRF Connect. + +--- + +# MÓDULO 3: SECURITY & ADVANCED TOPICS + +## 3.1 Flujo de Pairing (LESC) + +### Fase 1: Exchange Public Keys + +``` +Central (Smartphone) Peripheral (ESP32) + │ │ + ├─ Pairing Request ────────> │ + │ (IO cap, auth req, etc.) │ + │ │ + │ <──── Pairing Response ────┤ + │ (IO cap, bonding flag) │ + │ │ + ├─ Public Key (ECDH) ──────> │ [~160 ms blocking] + │ │ + │ <── Public Key (ECDH) ─────┤ +``` + +### Fase 2: Authentication + +Depende de IO capabilities: + +``` +Numeric Comparison: + Ambos generan random: 0-999999 + Ambos muestran 6 dígitos + User confirma: "¿Coinciden?" → Yes/No + +Passkey Entry: + Central muestra: 123456 + Periférico: Entrada en UART → 123456 + +Just Works: + Sin confirmación (sin MITM) +``` + +### Fase 3: Key Generation + +``` +Shared Secret (DHKey) = d_central * Public_Key_periférico + = d_periférico * Public_Key_central + +LTK (Long Term Key): + = PRES(DHKey, 256-bit salt, identifiers) + [Derivación de 128 bits] + +CSRK (Connection Signature Resolving Key): + = Generado para firmar datos [opcionalidad] + +IRK (Identity Resolving Key): + = Permite resolver direcciones privadas +``` + +--- + +## 3.2 NVS: Almacenamiento de Bonding + +**NVS (Non-Volatile Storage)**: Partición en flash donde persisten pares. + +**Estructura en ESP32:** +``` +Flash +├─ Partition: nvs +│ ├─ Namespace: peer_0 +│ │ ├─ ltk (16 bytes) +│ │ ├─ ediv (2 bytes) +│ │ ├─ rand (8 bytes) +│ │ ├─ csrk (16 bytes) +│ │ ├─ irk (16 bytes) +│ │ └─ identity_address (6 bytes) +│ │ +│ ├─ Namespace: peer_1 +│ └─ ... +``` + +**API NimBLE:** +```c +// Almacenar automáticamente al parear +ble_hs_cfg.store_status_cb = ble_store_util_status_rr; + +// Restaurar bonded devices al reconnectar +static void ble_app_on_sync(void) { + // NimBLE automáticamente busca pares en NVS + // Si encontrada identidad, hace "reconnect with encryption" +} + +// Limpiar bonding +ble_store_util_delete_all(); + +// Listar pares +ble_store_util_status_rr(); // Imprime en logs +``` + +--- + +## 3.3 LE Secure Connections vs Legacy + +### Legacy Pairing (BLE 4.0-4.1) + +``` +┌─────────────────────────────────────────┐ +│ Vulnerabilidad: TK débil, algoritmo simple│ +│ → KNOB attack: downgrade a 3-DES débil │ +└─────────────────────────────────────────┘ +``` + +### LE Secure Connections (BLE 4.2+) + +``` +┌──────────────────────────────────────────────┐ +│ ✓ ECDH robusta (P-256 curve) │ +│ ✓ Immune a KNOB (key length siempre 128 bit)│ +│ ✓ Numeric Comparison sin vulnerabilidades │ +│ ✗ Bloquea ~160 ms durante pairing (ROM) │ +└──────────────────────────────────────────────┘ +``` + +**En ESP32, LESC es default. No la deshabilites a menos que requieras legacy.** + +--- + +## 3.4 Privacy: Dirección Privada Resolvible (RPA) + +### Problema + +Un central puede **trackear** un periférico por su MAC fijo. + +### Solución: RPA (Resolvable Private Address) + +Dirección de 48 bits generada dinámicamente: +``` +┌─────────────┬──────────────────────┐ +│ Hash (16 b) │ Random (32 b) │ +│ 2 MSB = 01 │ (lower 2 bits = 01) │ +└─────────────┴──────────────────────┘ +``` + +**Hash = AES(IRK, Random_part)[0:15]** + +- Cada conexión genera RPA nueva. +- Central con IRK bonded puede resolver dirección. +- Sin IRK, imposible trackear. + +**En NimBLE:** +```c +// Forzar RPA (privacidad máxima) +ble_hs_cfg.rpa_timeout = 60; // Regenerar cada 60 s +ble_hs_cfg.sm_use_rpa = 1; // Use RPA en lugar de dirección fija + +// En periférico +ble_gap_set_addr_type(BLE_ADDR_TYPE_RPA_RANDOM_DEFAULT); +``` + +--- + +## 3.5 Herramientas de Debugging de Seguridad + +### Extraer LTK de iOS (post-pairing) + +1. **Xcode Bluetooth Packet Logger** (Mac): + ```bash + # Busca: "LE Start Encryption" + # En la expansión, observa: LTK = [16 bytes] + ``` + +2. **Android HCI Snoop** (developer options): + ```bash + adb pull /sdcard/btsnoop_hci.log + # Abre en Wireshark, filtra por "Start Encryption" + ``` + +### Monitoreo en Tiempo Real (ESP32) + +```c +// Log pairing state +static void gatt_svr_subscribe_cb(struct ble_gap_event *event, void *arg) { + if (event->type == BLE_GAP_EVENT_SUBSCRIBE) { + ESP_LOGI("SECURITY", "Subscribe flags: 0x%04x", + event->subscribe.cur_notify); + } +} + +// Log encryption state +case BLE_GAP_EVENT_ENC_CHANGE: + ESP_LOGI("SECURITY", "Encrypted: %d", + event->enc_change.encrypted); + break; +``` + +--- + +## 3.6 Ejercicio Práctico 3: Periférico Seguro con Bonding + +**Objetivo**: Crear un dispositivo que requiere LESC + Passkey, almacena bonding y re-encripta automáticamente. + +**Requisitos:** +1. NimBLE + LESC activado (default). +2. Passkey Entry (periférico muestra código). +3. Almacenamiento bonding en NVS. +4. Característica protegida: requiere encriptación. +5. Desconectar si intenta acceso sin encripción. + +**Pasos:** +1. Configura Security Manager: + ```c + ble_hs_cfg.sm_mitm = 1; + ble_hs_cfg.sm_bonding = 1; + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; + ``` + +2. Implementa callback `BLE_GAP_EVENT_PASSKEY_ACTION`. + +3. En `gatt_svr_chr_access`, valida `event->access.op` y rechaza si no cifrado. + +4. Test: Parear, desconectar, reconectar sin re-introducir código. + +--- + +## 3.7 Coexistencia WiFi-BLE y PSRAM + +### El Problema + +ESP32 tiene un **controller compartido** para WiFi y BLE. Ambos usan la banda 2.4 GHz. + +**Collision mitigation:** +- Algoritmo de frequency hopping separa canales. +- BLE tiene prioridad sobre WiFi en ciertos eventos críticos. +- Aumenta latencia WiFi durante transferencias BLE. + +### Estrategia de Optimización + +```c +// Config: Asignar más tiempo a BLE si es crítico +esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); +bt_cfg.ble_ll_resolv_list_size = 0; // Liberar RAM si no usas privacidad +esp_bt_controller_init(&bt_cfg); + +// O usar PSRAM para buffers del stack +ble_hs_cfg.ble_l2cap_mtu = 251; // Reduce MTU default si hay presión RAM +``` + +### PSRAM (Pseudo SRAM) + +ESP32 puede tener hasta 4 MB de PSRAM externo. **Acceso es lento (2-5x que IRAM)**, pero útil para: +- Buffers grandes. +- Modelos ML pequeños. + +**En contexto de BLE:** +- No recomendable para buffers de paquetes (latencia). +- Sí para almacenamiento de bonding LUTs si muchos pares. + +--- + +## 3.8 ESP32 Memory Deep Dive + +### Layout de Memoria + +``` +IRAM (Instruction RAM): 192 KB total +├─ 64 KB: PRO/APP cache MMU +├─ 128 KB: Ejecutable + stack tasks + heap crítico +│ ├─ NimBLE task: ~8 KB stack +│ ├─ Application task: ~4 KB +│ └─ Heap: Resto + +DRAM (Data RAM): 320 KB total +├─ 160 KB: Estáticos (.data + .bss) +├─ 160 KB: Heap runtime (rest) + +FLASH (External): 4 MB típico +├─ Partition: app (1.2 MB código) +├─ Partition: nvs (64 KB bonding) +└─ Partition: rest (available) +``` + +### Impacto en BLE + +| Stack | IRAM | DRAM Estático | Heap Init | +|-------|------|--------------|-----------| +| **Bluedroid** | ~60 KB | ~40 KB | ~200 KB | +| **NimBLE** | ~40 KB | ~20 KB | ~70 KB | + +**Conclusión**: NimBLE deja 130 KB más para ML models. Crítico para Edge AI. + +--- + +# DEBUGGING MASTERCLASS + +## Debugging 1: Logging Básico (Siempre Disponible) + +### ESP-IDF Monitor + +```bash +idf.py -p /dev/ttyUSB0 monitor +``` + +**Dentro del monitor:** +``` +# Cambiar nivel de log +Ctrl+T Ctrl+L → Choose log level → ble (default DEBUG) + +# Reset +Ctrl+T Ctrl+R + +# Salir +Ctrl+] +``` + +### Filtrar Logs por Módulo + +```c +// En código +esp_log_level_set("BLE_GATT", ESP_LOG_DEBUG); // Solo GATT debug +esp_log_level_set("*", ESP_LOG_INFO); // Todo a INFO + +// En menuconfig +idf.py menuconfig +→ Component config → Log output → Default log level → DEBUG +→ Bluetooth → Log level → Debug +``` + +### Ejemplo de Logging Custom + +```c +#include "esp_log.h" + +static const char *TAG = "MY_BLE_APP"; + +void my_function() { + ESP_LOGD(TAG, "Debug: valor=%d", 42); + ESP_LOGI(TAG, "Info: evento=%s", "connected"); + ESP_LOGW(TAG, "Warning: mtu bajo"); + ESP_LOGE(TAG, "Error: code=%d", error_code); +} +``` + +--- + +## Debugging 2: GDB + OpenOCD (JTAG Hardware Required) + +### Hardware Setup + +**Requerimientos:** +- **JTAG Adapter**: ESP-PROG, Jlink, ST-Link, o Raspberry Pi. +- **5 Cables**: TDI, TDO, TCK, TMS, GND (+ SRST opcional). + +**ESP32 JTAG Pins:** +``` +GPIO12 → TDI +GPIO13 → TCK +GPIO14 → TDO +GPIO15 → TMS +GND → GND +``` + +### Instalación OpenOCD + +```bash +# Linux (Debian/Ubuntu) +sudo apt-get install openocd + +# macOS +brew install openocd + +# Verify +openocd --version +``` + +### Iniciar OpenOCD + +```bash +# Para ESP-PROG en Linux +openocd -f interface/ftdi/esp32_devkitj_v1.cfg \ + -f target/esp32.cfg + +# Para ST-Link en Windows +openocd -f interface/stlink.cfg \ + -f target/esp32.cfg + +# Verás algo como: +# Info : Listening on port 3333 for gdb connections +# Info : Listening on port 6666 for telnet connections +``` + +### Conectar GDB + +**Terminal 1 (OpenOCD ejecutándose):** +```bash +openocd -f interface/esp32_prog.cfg -f target/esp32.cfg +``` + +**Terminal 2 (GDB):** +```bash +cd your_project_dir + +# Opción 1: Automático (desde ESP-IDF) +idf.py gdb + +# Opción 2: Manual +xtensa-esp32-elf-gdb build/your_app.elf + +# En GDB console: +(gdb) target remote localhost:3333 +(gdb) mon reset halt # Halt on reset +(gdb) thb app_main # Breakpoint temporal en app_main +(gdb) c # Continue +``` + +### Comandos GDB Útiles + +```bash +# Breakpoints +(gdb) b function_name +(gdb) b file.c:123 +(gdb) info breakpoints +(gdb) delete 1 + +# Stepping +(gdb) n # Next (step over) +(gdb) s # Step into function +(gdb) c # Continue +(gdb) finish # Run until return + +# Inspection +(gdb) p variable # Print value +(gdb) p /x 0x1234 # Hex format +(gdb) x/32x 0x3ffb0000 # Examine memory (32 words hex) +(gdb) info registers # All registers +(gdb) bt # Backtrace (stack) + +# FreeRTOS-aware (custom GDB macros) +(gdb) info tasks # List FreeRTOS tasks +(gdb) task 2 # Switch to task 2 +``` + +### Debugging BLE Specific Issues + +**Breakpoints en callbacks:** +```bash +(gdb) b esp_gap_cb # Breakpoint GAP callback +(gdb) b gatt_svr_chr_access # Breakpoint GATT access + +# Cuando se para, inspecciona evento +(gdb) p event->type # Tipo de evento +(gdb) p event->connect.conn_handle # Handle conexión +``` + +--- + +## Debugging 3: Análisis de Paquetes (Wireshark + HCI Sniffing) + +### Opción A: Wireshark en Linux (sin hardware sniffer) + +**Limitación**: Captura solo HCI local (advertiser), no conexiones activas. + +```bash +# Instalar Wireshark + plugin Bluetooth +sudo apt-get install wireshark wireshark-plugin-btsnoop + +# Ejecutar como root +sudo wireshark + +# Capturar: +# 1. Capture → Interfaces +# 2. Selecciona "Bluetooth" +# 3. Start capturing +``` + +**Filtros útiles:** +``` +btatt # Filter: Attribute Protocol +btle # Filter: BLE link layer +btatt.handle == 0x0003 # Characteristic handle +btatt.opcode == 0x0b # Write command +``` + +### Opción B: HCI Sniffing en Android + +```bash +# Activar developer options +# → Bluetooth HCI snoop log: ON + +# Ejecutar comando +adb pull /sdcard/btsnoop_hci.log + +# Abrir en Wireshark +wireshark btsnoop_hci.log +``` + +### Opción C: Captura de Logs de Sincronización (ESP32 Built-in) + +```bash +# Compilar con debug habilitado +idf.py menuconfig +# → Component config → Bluetooth → Enable debug logs +# → Log Level → Debug + +# En monitor, buscar: +# [HCI] LE Create Connection Complete +# [GATT] Write characteristic +``` + +--- + +## Debugging 4: Profiling y Análisis de Performance + +### Task CPU Usage + +```c +#include "freertos/task.h" + +void print_task_stats(void) { + char* buffer = malloc(1024); + vTaskList(buffer); + printf("%s\n", buffer); + free(buffer); +} + +// Output: +// Task Name State Priority Stack Num +// nimble_host_task X 100 4096 123 +// IDLE R 0 1024 0 +``` + +### Heap Usage + +```c +#include "esp_heap_caps.h" + +void print_heap_stats(void) { + printf("Internal DRAM: %d bytes free\n", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); + + printf("Internal IRAM: %d bytes free\n", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT)); +} +``` + +### Timing Analysis + +```c +#include "esp_timer.h" + +void measure_operation(void) { + int64_t start = esp_timer_get_time(); // microseconds + + // [Operation] + + int64_t elapsed_us = esp_timer_get_time() - start; + ESP_LOGI(TAG, "Operation took %.2f ms", elapsed_us / 1000.0); +} +``` + +--- + +## Debugging 5: Clang + AddressSanitizer (ASAN) + +### Enable AddressSanitizer + +```bash +idf.py menuconfig +# → Component config → Compiler options → Enable AddressSanitizer +``` + +### Build & Run + +```bash +idf.py build flash monitor +``` + +**Output de memory error:** +``` +================================================================= +==12345==ERROR: AddressSanitizer: heap-buffer-overflow on unknown address + Read of size 4 at 0x... Thread T1 + #0 in some_function ... +``` + +### Instrumentación Manual + +```c +#include "sanitizer/asan_interface.h" + +void unsafe_access(void) { + uint8_t buffer[10]; + + // Marcar rango válido + ASAN_POISON_MEMORY_REGION(buffer + 10, 1); + + // Esto triggea ASAN error si accede + buffer[15] = 0xFF; // Out of bounds! +} +``` + +--- + +# EJERCICIOS PRÁCTICOS PROGRESIVOS + +## Ejercicio 1: Beacon Personalizado (Nivel: Básico) + +**Objetivo**: Dominar advertising y formatos de datos. + +**Requisitos:** +- Transmitir temperatura simulada cada 100 ms. +- Usar Service Data con UUID 16-bit personalizado (0xAABB). +- Formato: `[temp_byte] [humidity_byte]`. +- Verificable en nRF Connect. + +**Template:** +```c +static uint8_t service_data[] = { + 0x02, 0x0A, 0xF4, // TX Power + 0x07, 0x16, // Service Data 16-bit + 0xBB, 0xAA, // UUID 0xAABB + 25, // Temperature + 60 // Humidity +}; + +// Update cada 100ms en timer +void update_adv_data(void) { + service_data[4] = read_temp(); + service_data[5] = read_humidity(); + ble_gap_adv_set_fields(&adv_fields); +} +``` + +**Verificación:** +1. Conecta a nRF Connect. +2. Abre Raw Data en advertising. +3. Confirma que hex cambia cada 100 ms. + +--- + +## Ejercicio 2: GATT Server Interactivo (Nivel: Intermedio) + +**Objetivo**: Implementar lectura/escritura de características + notificaciones. + +**Requisitos:** +- Servicio custom (UUID 128-bit). +- 3 características: + 1. **LED Control** (write): 0x00=OFF, 0x01=ON. + 2. **Button State** (read+notify): 0x00/0x01. + 3. **BPM Mock** (read+notify): Valor 60-100 BPM. +- BPM actualiza cada 2 segundos. +- Button emula presión cada 5 segundos. + +**Pasos:** +1. Define UUID 128-bit de servicio: + ```c + #define CUSTOM_SERVICE_UUID \ + {0x12, 0x34, 0x56, 0x78, ...} // 16 bytes + ``` + +2. Implementa task que actualiza BPM y Button. + +3. Maneja `GATT_WRITE_EVT` para LED control. + +4. En nRF Connect, verifica: + - Write LED → GPIO on/off. + - Read BPM → valor entre 60-100. + - Suscribirse a BPM → recibe notificaciones cada 2s. + +--- + +## Ejercicio 3: Bonding + Security (Nivel: Avanzado) + +**Objetivo**: Dominar seguridad LESC con persistencia. + +**Requisitos:** +- LESC + Numeric Comparison. +- Almacenamiento bonding en NVS. +- Reinicio sin re-pairing. +- Rechazo de acceso no encriptado. + +**Pasos:** +1. Configura SM: + ```c + ble_hs_cfg.sm_mitm = 1; + ble_hs_cfg.sm_bonding = 1; + ble_hs_cfg.sm_sc = 1; // Secure Connections + ``` + +2. Implementa passkey callback: + ```c + case BLE_GAP_EVENT_PASSKEY_ACTION: + ESP_LOGI("SEC", "Passkey: %06d", + event->passkey.params.action_type); + break; + ``` + +3. Valida encriptación en GATT access: + ```c + if (!ble_gap_conn_find(conn_handle, &desc)) { + if (!desc.sec_state.encrypted) { + return BLE_ATT_ERR_INSUFFICIENT_AUTHEN; + } + } + ``` + +4. Test: + - Parear, confirmar passkey. + - Reboot, reconectar → sin passkey. + - Borrar bonding en ESP32 (`ble_store_util_delete_all()`). + - Reconectar → pide passkey nuevamente. + +--- + +## Ejercicio 4: IoT Sensor Sync (Nivel: Avanzado+) + +**Objetivo**: Integrar múltiples sensores + WiFi + BLE. + +**Requisitos:** +- 1x Beacon BLE (temperatura). +- 1x GATT Server (BLE) con: + - Lectura ADC (temperatura real). + - Lectura RSSI de WiFi. + - Reset button. +- WiFi conectado a broker MQTT. +- Publicar datos por MQTT cada 10s. +- Recibir comandos por GATT + aplicar. + +**Pasos:** +1. Setup ADC + WiFi + MQTT. +2. Crea beacon con temp actualizada. +3. Crea GATT server con: + - Service: Environmental Sensing (0x181A). + - Characteristic: Temperature (0x2A6E). + - Characteristic: Humidity (mock, 0x2A6F). + +4. Escribe datos en NVS bajo etiqueta `iot_config`. + +5. Implementa: Write characteristic para actualizar MQTT topic. + +6. Test: + - BLE beacon muestra temp en tiempo real. + - GATT server lee sensores. + - MQTT publica cada 10s. + - Comando BLE actualiza MQTT topic. + +--- + +# REFERENCIAS & RECURSOS + +## Especificaciones Oficiales + +- **Bluetooth Core Specification Vol 4 (BLE)** + - Part A: Architecture + - Part B: Link Layer + - Part D: ATT/GATT + - Part E: HCI + - Part H: Security + +- **ESP32 Technical Reference Manual** + - Wireless, Memory, Timers + +## Documentación + +- **ESP-IDF Official Docs**: https://docs.espressif.com + - Bluetooth Low Energy API + - NimBLE Host + - JTAG Debugging + +- **Apache NimBLE Project**: https://mynewt.apache.org + - API Reference + - Architecture Guide + +## Herramientas de Desarrollo + +- **nRF Connect** (mobile app): Escanear, conectar, debug BLE +- **Wireshark**: Análisis de paquetes BLE/HCI +- **OpenOCD**: JTAG debugger +- **GDB**: GNU Debugger +- **Ubertooth One**: Hardware BLE sniffer (avanzado) + +## Repositorios Open-Source + +- **esp-idf/examples/bluetooth**: Ejemplos oficiales +- **apache/mynewt-nimble**: Stack NimBLE +- **espressif/esp-idf**: Full ESP-IDF +- **h2zero/NimBLE-Arduino**: NimBLE para Arduino IDE + +## Artículos Técnicos Recomendados + +1. **"The Bluetooth Low Energy Protocol Stack: Understanding the Fundamentals"** - NoveBits +2. **"A Practical Guide to Debugging BLE Communication on iOS"** - NoveBits +3. **"Understanding the Architecture of the Bluetooth Low Energy Protocol"** - Analog Devices +4. **"BLE Frequency Hopping Algorithm Analysis"** - arxiv.org (si te interesa sniffing avanzado) + +## Comunidades + +- **ESP32 Community Forum**: esp32.com +- **Apache NimBLE Mailing List**: dev@mynewt.apache.org +- **Bluetooth SIG**: bluetooth.com (especificaciones) + +--- + +## ROADMAP: Próximos Pasos para Edge AI + IoT + Blockchain + +Con BLE + ESP32 dominados, el siguiente paso en tu roadmap: + +1. **Quantización de Modelos ML** + - TensorFlow Lite Micro (< 1 MB, ~10 ms inferencia). + - Desplegar en ESP32 con BLE para actualizaciones federadas. + +2. **Federated Learning sobre BLE** + - Múltiples ESP32 (sensores) entrenando modelo compartido. + - Coordinación por NimBLE + Bluetooth Mesh. + +3. **Blockchain + BLE** + - Verificación de datos IoT en cadena (Substrate). + - Provenance de sensor data. + +4. **Edge Inference Optimization** + - ONNX Runtime en ESP32. + - Comparación: nanoGPT vs DistilBERT en hardware limitado. + +5. **Rust + CUDA para kernels críticos** + - Compilar NimBLE components en Rust (safety). + - CUDA kernels para training federated en nodos GPU (coordinadores). + +--- + +**¡Felicidades por completar esta masterclass! Ahora eres experto en BLE + ESP32.** 🎓 + +Para profundizar, revisa los repos sugeridos e implementa los mini-proyectos. Cada iteración refuerza internalizacion de conceptos. diff --git a/learn/BLE_Projects_roadmap_ideas.md b/learn/BLE_Projects_roadmap_ideas.md new file mode 100644 index 0000000..09203c0 --- /dev/null +++ b/learn/BLE_Projects_roadmap_ideas.md @@ -0,0 +1,516 @@ +# BLE + ESP32: Síntesis & Roadmap para Edge AI + +## Resumen Ejecutivo + +Has completado una **masterclass exhaustiva** que cubre: + +✅ **Módulo 0**: Fundamentos BLE (PHY, Link Layer, GATT, Seguridad) +✅ **Módulo 1**: Advertising & Discovery (Beacons, Data types) +✅ **Módulo 2**: Connections & GATT (Server, Notificaciones) +✅ **Módulo 3**: Security & Advanced (LESC, Bonding, NVS) +✅ **Debugging**: Logging, GDB+OpenOCD, ASAN, HCI Sniffing +✅ **Ejercicios**: 4 niveles progresivos con código completo + +--- + +## Checklist de Dominio + +Antes de avanzar a Edge AI + Blockchain, valida que **entiendes profundamente**: + +### Teoría BLE (✓ si respondes sin dudar) + +- [ ] Diferencia entre PHY 1M, 2M, Coded. ¿Cuándo usarías cada uno? +- [ ] ¿Por qué BLE usa frequency hopping? ¿Cuál es el parámetro `hop_increment`? +- [ ] ¿Qué es CCCD y por qué es crítico para notificaciones? +- [ ] ¿Cuál es la diferencia entre Notificar e Indicar? +- [ ] ECDH vs Legacy Pairing: ¿Cuáles son los puntos vulnerables? +- [ ] ¿Cómo funciona RPA (Resolvable Private Address)? + +### Práctica ESP32 (✓ si compilas sin errors) + +- [ ] ¿Puedes implementar un beacon custom en 10 minutos? +- [ ] ¿Puedes crear un GATT server con 3+ características? +- [ ] ¿Puedes hacer pairing LESC + bonding? +- [ ] ¿Puedes debuggear un crash con GDB + stack trace? +- [ ] ¿Puedes optimizar connection parameters para batería? + +### NimBLE Internals (✓ si entiendes el flujo) + +- [ ] ¿Cómo fluye un evento BLE en NimBLE? (HCI → Host task → app callback) +- [ ] ¿Dónde se almacenan bonded devices? (NVS, qué namespace) +- [ ] ¿Cuál es la diferencia entre `ble_gap_adv_set_fields` y `ble_gap_adv_start`? +- [ ] ¿Por qué `nimble_host_task` es una task FreeRTOS separada? + +--- + +## Integración: Edge AI + BLE + Blockchain + +### 1️⃣ Scenario: Federated Learning en Wearables + +``` +┌──────────────────────┐ +│ Smartphone Central │ (Orquestador) +│ - Modelo ML global │ +│ - Verifica signaturas│ +├──────────────────────┤ +│ BLE (NimBLE) │ +└──────────────────────┘ + │ ▲ + │ │ (gradientes cuantizados) + ▼ │ + ┌─────────────┐ + │ ESP32 #1 │ Reloj (acelerómetro) + │ - TFLite │ → Localdetect caída + │ - Entrena │ → Sube gradientes + └─────────────┘ +``` + +**Implementación BLE:** +- Central (smartphone) se conecta a múltiples periféricos (ESP32). +- ESP32 notifica cambios de modelo cada 10 minutos. +- Central agrega gradientes con CUDA. +- Re-envía modelo actualizado vía BLE. + +**Código (conceptual):** +```c +// ESP32: Detecta caída via ML, prepara gradientes +void ml_inference_task(void) { + float *gradients = tflite_get_gradients(); // 4 KB comprimido + + // Notificar al central + ble_gatts_notify_custom(conn_handle, grad_handle, gradients, 4096); +} + +// Central: Agrega y re-distribuye +void federated_aggregation(std::vector all_grads) { + aggregate_on_gpu(all_grads); + broadcast_via_ble(updated_model); +} +``` + +--- + +### 2️⃣ Scenario: Verificación de Datos en Blockchain + +``` +┌─────────────────┐ +│ ESP32 Sensor │ +│ - ADC temp │ +│ - Sign with SK │ +│ - BLE send │ +└─────────────────┘ + │ + ▼ +┌─────────────────┐ +│ Smartphone │ +│ - Recibe firma │ +│ - Verifica PK │ +│ - Envía a Dapp│ +└─────────────────┘ + │ + ▼ +┌─────────────────┐ +│ Blockchain │ +│ (Substrate) │ +│ - Verifica data │ +│ - Almacena hash │ +│ - Emite evento │ +└─────────────────┘ +``` + +**Criptografía BLE:** +- ESP32 usa SK (private key) para firmar lecturas. +- Central verifica con PK (public key). +- Hash se envía a blockchain para verificación. + +--- + +### 3️⃣ Scenario: IoT Mesh con Coordinación + +``` + Central (Coordinator) + │ + ┌──────┼──────┐ + │ │ │ + Node1 Node2 Node3 (ESP32s con sensores) + │ │ │ + └──────┼──────┘ + │ + Blockchain + (Consensus) +``` + +**Protocolo:** +1. Cada nodo es periférico (BLE). +2. Coordinator es central (conecta a todos). +3. Cada 30s, nodos reportan sensores. +4. Coordinator agrega y firma. +5. Envía a blockchain via API. + +--- + +## Próximos Pasos: Roadmap Personalizado + +### Fase 1: Validar BLE Mastery (1-2 semanas) + +``` +[ ] Ejercicio 4 completo: IoT Sensor Sync +[ ] Implementar custom beacon con 128-bit UUID +[ ] Crear GATT server con encriptación LESC +[ ] Debuggear con GDB: set 3+ breakpoints +[ ] Medir consumo de potencia en 3 connection params +``` + +**Criterio de éxito:** Debuggeas un crash en GATT sin ayuda y optimizas latencia. + +--- + +### Fase 2: TensorFlow Lite Micro + BLE (2-3 semanas) + +**Objetivo:** Ejecutar modelo ML en ESP32, actualizarlo via BLE. + +**Pasos:** +1. Entrenar modelo pequeño (< 100 KB). + - MobileNetV2 cuantizado. + - DistilBERT para procesamiento de lenguaje. + +2. Compilar para ESP32 con TFLite Micro. + ```bash + python3 -m tensorflow.lite.python.convert \ + --inference_input_type=QUANTIZED_UINT8 \ + --inference_output_type=QUANTIZED_UINT8 \ + model.h5 model.tflite + xxd -i model.tflite > model.h + ``` + +3. Integrar con BLE para actualizaciones OTA. + ```c + void ota_update_model_task(void) { + // Recibe chunks via BLE notification + // Escribe a flash partition + // Reinicia con nuevo modelo + } + ``` + +**Recurso:** TensorFlow Lite for Microcontrollers guide. + +--- + +### Fase 3: Rust + NimBLE Safety (1-2 semanas) + +**Objetivo:** Dominar Rust para **seguridad memory-safe**. + +**Pasos:** +1. Setups Rust ESP32 toolchain. + ```bash + rustup target add xtensa-esp32-espidf + cargo install cargo-espflash + ``` + +2. Reescribir componentes críticos (GAP, GATT). + ```rust + pub struct BleDevice { + stack: NimbleStack, + services: Vec, + } + + impl BleDevice { + pub fn new() -> Result { + // Safe initialization + } + } + ``` + +3. Integrar con C via FFI. + +**Recurso:** The Embedded Rust Book + esp-idf-hal. + +--- + +### Fase 4: Blockchain Coordination (2-3 semanas) + +**Objetivo:** Verificar datos IoT en blockchain (Substrate/Tendermint). + +**Pasos:** +1. Setups cadena local. + ```bash + substrate-contracts-node --dev + ``` + +2. Escribir pallet Substrate. + ```rust + #[pallet::call_index(0)] + pub fn record_iot_reading( + origin: OriginFor, + device_id: u64, + reading: u32, + proof: BoundedVec>, + ) -> DispatchResult { + ensure_signed(origin)?; + // Verificar firma BLE + // Almacenar lectura + Ok(()) + } + ``` + +3. Conectar ESP32 → Pallet via HTTP/WebSocket. + +**Recurso:** Substrate Developer Hub + Ink! (smart contracts). + +--- + +### Fase 5: Federated Learning (3-4 semanas) + +**Objetivo:** Entrenar modelo distribuido entre múltiples ESP32s. + +**Arquitectura:** +``` +Central (Coordinator) +├─ Aggregador ML (PyTorch) +├─ BLE Central +└─ API REST + +ESP32 Periféricos +├─ TFLite Micro (inference) +├─ Gradientes locales (4-16 KB) +└─ BLE Periférico +``` + +**Pasos:** +1. Implementar local SGD en ESP32. + ```c + void sgd_step(float *weights, float *gradients, int size) { + for (int i = 0; i < size; i++) { + weights[i] -= LEARNING_RATE * gradients[i]; + } + } + ``` + +2. Agregación en Central (PyTorch/Rust). + ```python + def aggregate_gradients(esp32_grads): + return torch.stack(esp32_grads).mean(dim=0) + ``` + +3. Broadcasting del modelo nuevo. + +**Recurso:** Federated Learning with PyTorch + TensorFlow Federated. + +--- + +## Matriz de Decisión: ¿Qué Aprender Primero? + +| Necesidad | Prioridad | Tiempo | Roadmap | +|-----------|-----------|--------|---------| +| **Validar BLE** | 🔴 Ahora | 1w | Fase 1 | +| **ML en Edge** | 🟠 Pronto | 2w | Fase 2 | +| **Memory Safety** | 🟠 Pronto | 2w | Fase 3 | +| **Blockchain** | 🟡 Importante | 3w | Fase 4 | +| **Federated Learning** | 🟢 Después | 4w | Fase 5 | + +**Recomendación:** Fases 1 → 2 → 4 (en paralelo 3 si tiempo). + +--- + +## Herramientas Esenciales + +### Debugging & Testing + +```bash +# Terminal 1: OpenOCD +openocd -f interface/esp32_prog.cfg -f target/esp32.cfg + +# Terminal 2: GDB +xtensa-esp32-elf-gdb build/app.elf +(gdb) target remote localhost:3333 + +# Terminal 3: Monitor +idf.py -p /dev/ttyUSB0 monitor + +# Análisis de logs +idf.py monitor | tee session.log +grep "BLE_GATT\|ERROR" session.log | tail -20 +``` + +### Mobile Testing + +- **nRF Connect** (Android/iOS): Scan, connect, notif, indicaciones. +- **LightBlue** (iOS): RSSI, timing. + +### Hardware Sniffing (Avanzado) + +- **Wireshark + Ubertooth One**: Captura OTA de conexiones. +- **Raspberry Pi + OpenOCD**: Debuggeo JTAG local. + +--- + +## Referencias Finales + +### Especificaciones (Obligatorios) + +1. **Bluetooth Core Spec Vol 4 Part A-H**: https://bluetooth.com +2. **ESP32 Technical Reference Manual**: Espressif docs +3. **Apache NimBLE Architecture**: mynewt.apache.org + +### Documentación Técnica + +- **ESP-IDF Programming Guide**: docs.espressif.com +- **TensorFlow Lite Micro**: tensorflow.org/lite/microcontrollers +- **Substrate Developer Hub**: substrate.dev + +### Repositorios Clave + +```bash +# ESP-IDF ejemplos +git clone https://github.com/espressif/esp-idf +cd esp-idf/examples/bluetooth/ble_get_started + +# NimBLE +git clone https://github.com/apache/mynewt-nimble + +# Raspberry Pi Pico + NimBLE +git clone https://github.com/h2zero/NimBLE-Arduino + +# TensorFlow Lite Micro ESP32 +git clone https://github.com/tensorflow/tflite-micro + +# Substrate template +git clone https://github.com/substrate-developer-hub/substrate-node-template +``` + +### Papers & Blogs Recomendados + +1. **"BLE Security Analysis"** - NoveBits (2024) +2. **"Frequency Hopping in BLE"** - arxiv.org/pdf/... (2019) +3. **"Federated Learning for IoT"** - Google Research (2022) +4. **"Edge AI Inference Optimization"** - TensorFlow Blog (2023) + +--- + +## Ejercicio Final Integrador: Mini-Proyecto Capstone + +**Propósito:** Combina BLE + ML + Blockchain. + +### Especificación + +**Sistema:** "Smart Home Energy Monitor" + +``` +┌──────────────────┐ +│ Smart Meter │ +│ (ESP32 + Sensor)│ +│ - Mide potencia │ +│ - Detecta anomalías (TFLite) +│ - BLE periférico│ +└──────────────────┘ + │ BLE + ▼ +┌──────────────────┐ +│ Hub (Central) │ +│ - Recibe datos │ +│ - Agrega stats │ +│ - Firma + blockchain +└──────────────────┘ + │ HTTP + ▼ + Blockchain + (Substrate) +``` + +### Requisitos + +**Hardware:** +- 1x ESP32 con sensor ADC (simular consumo). +- 1x Smartphone o PC como central. + +**Software:** +- **BLE Periférico (ESP32):** + - Beacon: consumo actual (W). + - GATT Service: historiales (últimas 10 lecturas). + - Notificaciones: cada lectura nueva. + - LESC + Bonding. + +- **BLE Central (Smartphone/PC):** + - Conectar múltiples medidores. + - Agregar consumo total. + - Detectar anomalías (> 2σ). + +- **Blockchain (Local Substrate):** + - Pallet `EnergyReading` con readings. + - Verificación de firma (public key ESP32). + - Query: consumo por hora/día. + +### Entregables + +1. Código BLE (periférico + central). +2. Pallet Substrate + tests. +3. Documentación: arquitectura, security, resultados de pruebas. +4. Análisis: latencia BLE, consumo energético, throughput. + +### Criterios de Éxito + +✅ Periférico con LESC + bonding persistente. +✅ Central agrega datos de 2+ periféricos. +✅ Detección de anomalías (algoritmo simple OK). +✅ Blockchain verifica firma y registra. +✅ Demo: De sensor a blockchain en < 5s. + +--- + +## Último Consejo: Tu Diferenciador Competitivo + +Combine **estas 3 áreas** rara vez juntas: + +1. **BLE Low-Level** (PHY, Link Layer, Seguridad). +2. **Embedded Rust** (Memory-safety en IoT). +3. **Blockchain Smart Contracts** (Verificación descentralizada). + +Esto te posiciona como **experto en Edge AI + Distributed Trust**, muy demandado en: +- **Autonomous vehicles**: Coordinación de sensores. +- **Smart cities**: Redes IoT verificadas. +- **Healthcare wearables**: Datos verificables en cadena. +- **Industria 4.0**: IoT industrial con blockchain. + +--- + +**¡Felicidades! Ya tienes todo el conocimiento. Ahora, a construir.** 🚀 + +--- + +## Quick Reference: Comandos Útiles + +```bash +# Build & Flash +idf.py set-target esp32 +idf.py build +idf.py -p /dev/ttyUSB0 flash monitor + +# Menuconfig +idf.py menuconfig +# → Component config → Bluetooth → NimBLE vs Bluedroid +# → Log level → DEBUG + +# GDB + OpenOCD +openocd -f interface/ftdi/esp32_devkitj_v1.cfg -f target/esp32.cfg & +xtensa-esp32-elf-gdb build/app.elf +(gdb) target remote localhost:3333 +(gdb) thb app_main +(gdb) c + +# Monitoring +idf.py monitor +# Dentro del monitor: +# Ctrl+T Ctrl+L → elegir nivel log +# Ctrl+T Ctrl+R → reset +# Ctrl+] → salir + +# NVS (bonding) +idf.py erase-flash # Limpia bonding + +# Memory profiling +idf.py size-components build/app.elf +``` + +--- + +**Documento finalizado. ¡Adelante con el aprendizaje!** 📚 diff --git a/learn/BLE_example_exercises.md b/learn/BLE_example_exercises.md new file mode 100644 index 0000000..ee19c4a --- /dev/null +++ b/learn/BLE_example_exercises.md @@ -0,0 +1,986 @@ +# BLE + ESP32: Ejercicios Avanzados & Debugging en Detalle + +## Parte 1: Ejercicios Codificados (Estructura Completa) + +--- + +## EJERCICIO 1: Beacon Personalizado (Básico) + +### Proyecto: `ble_sensor_beacon` + +```c +// main/main.c +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "esp_err.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "esp_bt.h" + +static const char *TAG = "BLE_BEACON"; + +// Variables globales +static uint8_t own_addr_type; +static uint8_t adv_data[] = { + 0x02, 0x01, 0x06, // Flags + 0x02, 0x0A, 0xF4, // TX Power: -12 dBm + 0x07, 0x16, // Service Data 16-bit + 0xBB, 0xAA, // UUID 0xAABB (custom) + 25, // Temperature: 25°C + 60 // Humidity: 60% +}; + +// Task para actualizar advertising data +void update_beacon_task(void *pvParameters) { + uint8_t temp = 20; + uint8_t hum = 50; + + while (1) { + // Simular sensor + temp = 20 + (esp_random() % 10); // 20-30°C + hum = 50 + (esp_random() % 20); // 50-70% + + // Actualizar payload + adv_data[8] = temp; + adv_data[9] = hum; + + ESP_LOGI(TAG, "Updated: Temp=%d°C, Humidity=%d%%", temp, hum); + + // Re-set advertising data (sin reiniciar advertising) + struct ble_gap_adv_set_fields adv_fields = {0}; + adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + adv_fields.mfg_data = adv_data; + adv_fields.mfg_data_len = sizeof(adv_data); + + ble_gap_adv_set_fields(&adv_fields); + + vTaskDelay(100 / portTICK_PERIOD_MS); // 100 ms + } +} + +// Inicializar advertising +static int adv_init(void) { + struct ble_gap_adv_params adv_params = {0}; + struct ble_gap_adv_set_fields adv_fields = {0}; + + // Configurar campos + adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + adv_fields.mfg_data = adv_data; + adv_fields.mfg_data_len = sizeof(adv_data); + + int rc = ble_gap_adv_set_fields(&adv_fields); + if (rc != 0) { + ESP_LOGE(TAG, "ble_gap_adv_set_fields failed: %d", rc); + return rc; + } + + // Parámetros advertising: 100 ms interval, non-connectable + adv_params.conn_mode = BLE_GAP_CONN_MODE_NON; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(100); + adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(100); + + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params, + NULL, NULL); + if (rc != 0) { + ESP_LOGE(TAG, "ble_gap_adv_start failed: %d", rc); + return rc; + } + + ESP_LOGI(TAG, "Advertising started"); + return 0; +} + +// Callback sync de NimBLE +static void ble_app_on_sync(void) { + // Asegurar dirección válida + ble_hs_util_ensure_addr(0); + ble_hs_id_infer_auto(0, &own_addr_type); + + if (adv_init() != 0) { + ESP_LOGE(TAG, "Failed to initialize advertising"); + } + + // Iniciar task de actualización + xTaskCreate(update_beacon_task, "update_beacon", 2048, NULL, 5, NULL); +} + +void app_main(void) { + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + nvs_flash_erase(); + nvs_flash_init(); + } + + // Init Bluetooth controller + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + + // Init NimBLE + nimble_port_init(); + ble_hs_cfg.sync_cb = ble_app_on_sync; + + // Iniciar NimBLE stack + nimble_port_freertos_init(BLE_HS_TASK_PRIORITY); +} +``` + +### Verificación en nRF Connect + +1. **Scan**: Busca dispositivo `NimBLE_Beacon` o MAC. +2. **Raw Data**: Observa advertising packet: + ``` + 02 01 06 (Flags) + 02 0A F4 (TX Power) + 07 16 BB AA 19 3C ... (Service Data: 25°C, 60% humidity) + ``` +3. **Repetición**: Datos actualizan cada 100 ms. + +--- + +## EJERCICIO 2: GATT Server Interactivo (Intermedio) + +### Proyecto: `ble_gatt_interactive` + +```c +// main/include/gatt_services.h +#ifndef GATT_SERVICES_H +#define GATT_SERVICES_H + +#include "host/ble_hs.h" + +// UUIDs del servicio custom (128-bit) +#define CUSTOM_SERVICE_UUID \ + {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, \ + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0} + +// Características +extern uint8_t led_state; // Write +extern uint8_t button_state; // Read + Notify +extern uint8_t heart_rate; // Read + Notify + +// Declaración de servicios GATT +extern const struct ble_gatt_svc_def gatt_svr_svcs[]; + +#endif // GATT_SERVICES_H +``` + +```c +// main/src/gatt_services.c +#include "gatt_services.h" +#include "esp_log.h" + +static const char *TAG = "GATT_SERVICES"; + +// Estado de características +uint8_t led_state = 0; +uint8_t button_state = 0; +uint8_t heart_rate = 70; + +// Callback de acceso a características +static int gatt_svr_chr_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) { + uint16_t uuid16 = ble_uuid_u16(ctxt->chr->uuid); + + switch (uuid16) { + case 0x0001: // LED Control (write) + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + if (ctxt->om->om_len > 0) { + led_state = ctxt->om->om_data[0]; + ESP_LOGI(TAG, "LED state: %d", led_state); + if (led_state) { + // Encender LED (GPIO) + } else { + // Apagar LED + } + } + } + break; + + case 0x0002: // Button State (read + notify) + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + os_mbuf_append(ctxt->om, &button_state, sizeof(button_state)); + } + break; + + case 0x0003: // Heart Rate (read + notify) + if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) { + os_mbuf_append(ctxt->om, &heart_rate, sizeof(heart_rate)); + } + break; + } + + return 0; +} + +// Callback para CCCD (notificaciones) +static int gatt_svr_dsc_access(uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) { + uint16_t uuid16 = ble_uuid_u16(ctxt->dsc->uuid); + + if (uuid16 == BLE_GATT_DSC_CLT_CFG_UUID16) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_DSC) { + uint16_t notify_flags = os_mbuf_read_u16(ctxt->om, 0); + ESP_LOGI(TAG, "Notify flags for %s: 0x%04x", + (char *)arg, notify_flags); + } + } + + return 0; +} + +// Tabla de servicios GATT +const struct ble_gatt_svc_def gatt_svr_svcs[] = { + { + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID128_DECLARE(CUSTOM_SERVICE_UUID), + + .characteristics = (struct ble_gatt_chr_def[]) { + // LED Control + { + .uuid = BLE_UUID16_DECLARE(0x0001), + .access_cb = gatt_svr_chr_access, + .flags = BLE_GATT_CHR_F_WRITE, + }, + // Button State + { + .uuid = BLE_UUID16_DECLARE(0x0002), + .access_cb = gatt_svr_chr_access, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, + .descriptors = (struct ble_gatt_dsc_def[]) { + { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16), + .access_cb = gatt_svr_dsc_access, + .flags = BLE_GATT_DSC_F_WRITE, + .arg = "Button" + }, + {0} + }, + }, + // Heart Rate + { + .uuid = BLE_UUID16_DECLARE(0x0003), + .access_cb = gatt_svr_chr_access, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY, + .descriptors = (struct ble_gatt_dsc_def[]) { + { + .uuid = BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16), + .access_cb = gatt_svr_dsc_access, + .flags = BLE_GATT_DSC_F_WRITE, + .arg = "HeartRate" + }, + {0} + }, + }, + {0} + }, + }, + {0} +}; +``` + +```c +// main/main.c +#include "host/ble_hs.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "gatt_services.h" + +static const char *TAG = "BLE_GATT_SERVER"; +static uint8_t own_addr_type; + +// GAP event handler +static int gap_event_handler(struct ble_gap_event *event, void *arg) { + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + ESP_LOGI(TAG, "Connected: conn_handle=%d", event->connect.conn_handle); + break; + + case BLE_GAP_EVENT_DISCONNECT: + ESP_LOGI(TAG, "Disconnected: conn_handle=%d, reason=%d", + event->disconnect.conn.conn_handle, + event->disconnect.reason); + adv_init(); // Re-advertise + break; + + case BLE_GAP_EVENT_CONN_UPDATE_REQ: + ESP_LOGI(TAG, "Connection update request"); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + ESP_LOGD(TAG, "Advertising complete"); + break; + } + return 0; +} + +static int adv_init(void) { + struct ble_gap_adv_params adv_params = {0}; + struct ble_gap_adv_set_fields adv_fields = {0}; + + adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + adv_fields.name = (uint8_t *)"BLE_GATT_TEST"; + adv_fields.name_len = strlen("BLE_GATT_TEST"); + adv_fields.name_is_complete = 1; + adv_fields.tx_pwr_lvl_is_present = 1; + adv_fields.tx_pwr_lvl = -10; + + ble_gap_adv_set_fields(&adv_fields); + + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; // Connectable + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(100); + adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(200); + + return ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, gap_event_handler, NULL); +} + +// Task para actualizar BPM y Button +void sensor_update_task(void *pvParameters) { + uint16_t notify_handles[] = {0, 0}; // Button y HR + + while (1) { + // Simular sensor + heart_rate = 60 + (esp_random() % 40); + button_state = (esp_random() % 100) > 80 ? 1 : 0; + + ESP_LOGI(TAG, "HR=%d, Button=%d", heart_rate, button_state); + + // TODO: Send notifications aquí + // ble_gatts_notify_custom(conn_handle, handle, om); + + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +} + +static void ble_app_on_sync(void) { + ble_hs_util_ensure_addr(0); + ble_hs_id_infer_auto(0, &own_addr_type); + + // Registrar servicios GATT + ble_gatts_count_cfg(gatt_svr_svcs); + ble_gatts_add_svcs(gatt_svr_svcs); + + adv_init(); + + xTaskCreate(sensor_update_task, "sensor_update", 2048, NULL, 5, NULL); +} + +void app_main(void) { + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { + nvs_flash_erase(); + nvs_flash_init(); + } + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + + nimble_port_init(); + ble_hs_cfg.sync_cb = ble_app_on_sync; + ble_hs_cfg.att_max_mtu = 256; + + nimble_port_freertos_init(BLE_HS_TASK_PRIORITY); +} +``` + +--- + +## EJERCICIO 3: Security + Bonding (Avanzado) + +```c +// main/main.c (Security enabled) + +#include "host/ble_sm.h" +#include "host/ble_gap.h" + +static const char *TAG = "BLE_SECURITY"; + +// Pairing complete callback +static int pairing_cb(uint16_t conn_handle, struct ble_gap_event *event, void *arg) { + struct ble_gap_conn_desc desc; + + switch (event->type) { + case BLE_GAP_EVENT_ENC_CHANGE: + if (event->enc_change.status == 0) { + ESP_LOGI(TAG, "Encryption enabled on conn %d", conn_handle); + } else { + ESP_LOGE(TAG, "Encryption failed: %d", event->enc_change.status); + } + break; + + case BLE_GAP_EVENT_PASSKEY_ACTION: + ESP_LOGI(TAG, "Passkey action: %d", + event->passkey.params.action); + // En periférico (display), imprime passkey + if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + ESP_LOGI(TAG, "Passkey to show: %06d", + event->passkey.params.numcmp); + } + break; + + case BLE_GAP_EVENT_CONN_UPDATE: + if (ble_gap_conn_find(conn_handle, &desc) == 0) { + ESP_LOGI(TAG, "Connection updated: itvl=%d, latency=%d, timeout=%d", + desc.conn_itvl, desc.conn_latency, + desc.supervision_tout); + } + break; + } + + return 0; +} + +// Configurar Security Manager +static void ble_app_on_sync(void) { + ble_hs_util_ensure_addr(0); + ble_hs_id_infer_auto(0, &own_addr_type); + + // Security Manager config + ble_hs_cfg.sm_mitm = 1; // MITM protection + ble_hs_cfg.sm_bonding = 1; // Bonding + ble_hs_cfg.sm_sc = 1; // Secure Connections + ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY; // Display-only + + // Registrar GATT services + ble_gatts_count_cfg(gatt_svr_svcs); + ble_gatts_add_svcs(gatt_svr_svcs); + + // Iniciar advertising + adv_init(); +} + +// Char access con encryptión requerida +static int gatt_svr_chr_access_secure(uint16_t conn_handle, + uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, + void *arg) { + struct ble_gap_conn_desc desc; + + // Validar encriptación + if (ble_gap_conn_find(conn_handle, &desc) == 0) { + if (!desc.sec_state.encrypted) { + ESP_LOGE(TAG, "Write denied: not encrypted"); + return BLE_ATT_ERR_INSUFFICIENT_AUTHEN; + } + } + + // Proceder con la operación... + return gatt_svr_chr_access(conn_handle, attr_handle, ctxt, arg); +} +``` + +--- + +# Parte 2: Debugging Avanzado en C++ con Clang + +--- + +## Debugging Setup Completo + +### 1. Configurar VSCode + Clang + GDB + +#### File: `.vscode/settings.json` + +```json +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "xaver.clang-format", + "[c]": { + "editor.defaultFormatter": "xaver.clang-format", + "editor.formatOnSave": true + }, + "C_Cpp.intelliSenseEngine": "disabled", + "C_Cpp.codeAnalysisRunOnSave": true, + "clang.executable": "/usr/bin/clang", + "clang.fallbackFlags": ["-I${workspaceFolder}/components", "-I${workspaceFolder}/main"] +} +``` + +#### File: `.vscode/launch.json` + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "ESP32 GDB", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/your_app.elf", + "args": [], + "stopAtEntry": true, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/opt/esp/tools/xtensa-esp32-elf/esp-12.2.0_20230208/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb", + "miDebuggerServerAddress": "localhost:3333", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Connect to OpenOCD", + "text": "-target-select remote localhost:3333" + }, + { + "description": "Set FreeRTOS awareness", + "text": "source ${workspaceFolder}/.gdbinit" + } + ], + "preLaunchTask": "openocd" + } + ] +} +``` + +#### File: `.vscode/tasks.json` + +```json +{ + "version": "2.0.0", + "tasks": [ + { + "label": "openocd", + "type": "shell", + "command": "openocd", + "args": [ + "-f", "interface/ftdi/esp32_devkitj_v1.cfg", + "-f", "target/esp32.cfg" + ], + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "^.*Open On-Chip Debugger.*$", + "file": 1, + "location": 2, + "message": 3 + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^.*listening on port.*$", + "endsPattern": "^.*$" + } + } + }, + { + "label": "build", + "type": "shell", + "command": "idf.py", + "args": ["build"], + "problemMatcher": [] + }, + { + "label": "flash", + "type": "shell", + "command": "idf.py", + "args": ["-p", "/dev/ttyUSB0", "flash"], + "dependsOn": "build" + } + ] +} +``` + +#### File: `.gdbinit` + +```gdb +set print pretty on +set print array on +set print array-indexes on + +# FreeRTOS-aware debugging +source /opt/esp/tools/esp-gdb-init + +# Breakpoints útiles +break app_main +break esp_app_on_sync +break gatt_svr_chr_access + +# Print stack en caso de crash +define print_stack + bt full +end + +# Macros útiles +define inspect_heap + printf "DRAM free: %d bytes\n", heap_caps_get_free_size(2) + printf "IRAM free: %d bytes\n", heap_caps_get_free_size(4) +end + +define inspect_tasks + info tasks +end +``` + +--- + +### 2. Debugging Práctico: Session Ejemplo + +#### Escenario: Crash en GATT access durante notificación + +**Terminal 1: OpenOCD** +```bash +openocd -f interface/ftdi/esp32_devkitj_v1.cfg -f target/esp32.cfg +``` + +**Terminal 2: VSCode Debug** +```bash +# Presionar F5 en VSCode (ejecuta launch.json) +# O manual: +xtensa-esp32-elf-gdb build/your_app.elf +(gdb) target remote localhost:3333 +(gdb) thb app_main +(gdb) c +``` + +**En GDB:** +```bash +# Cuando paras en app_main +(gdb) b gatt_svr_chr_access +(gdb) c + +# Si crashea en gatt_svr_chr_access +(gdb) bt full # Ver stack trace completo +(gdb) frame 1 +(gdb) p ctxt # Inspeccionar parámetros +(gdb) p ctxt->om # mbuf +(gdb) x/32x ctxt->om->om_data # Ver datos en memory + +# Ver heap en crash +(gdb) p heap_caps_get_free_size(2) + +# Ver registros +(gdb) info registers +``` + +--- + +### 3. AddressSanitizer (ASAN) + +#### Configuración + +```bash +idf.py menuconfig +# → Component config → Compiler options → Enable AddressSanitizer → YES +idf.py build flash monitor +``` + +#### Código con bug + +```c +void buggy_notify_task(void *arg) { + uint8_t *buffer = malloc(10); + + while (1) { + // BUG: Escribir fuera de límites + buffer[15] = 0xFF; // Out of bounds! + + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} +``` + +#### Output ASAN + +``` +================================================================= +==12345==ERROR: AddressSanitizer: heap-buffer-overflow on unknown address + Write of size 1 at 0x3ffb2050 (pc 0x40084567 T0) + +Freed by thread T0 here: + #0 0x400... in malloc (in your_app.elf) + #1 0x400... in buggy_notify_task at main.c:42 + +SUMMARY: AddressSanitizer: heap-buffer-overflow main.c:42 +``` + +--- + +### 4. Memory Profiling + +```c +// main/monitoring.c +#include "esp_heap_caps.h" +#include "esp_system.h" + +void print_heap_status(void) { + printf("\n=== HEAP STATUS ===\n"); + printf("Internal DRAM: %d / %d bytes free (%.1f%%)\n", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT), + 100.0 * heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT) / + heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)); + + printf("Internal IRAM: %d / %d bytes free (%.1f%%)\n", + heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT), + heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT), + 100.0 * heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT) / + heap_caps_get_total_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_32BIT)); + + printf("External RAM: %d bytes free\n", + heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); +} + +void print_task_stack_usage(void) { + char *buffer = malloc(1024); + vTaskList(buffer); + printf("\n=== TASK STATUS ===\n%s\n", buffer); + free(buffer); +} + +// Registrar en timer +void monitor_timer_callback(TimerHandle_t xTimer) { + print_heap_status(); + print_task_stack_usage(); +} +``` + +--- + +### 5. HCI Packet Sniffing en Software + +```c +// main/hci_monitor.c - Captura HCI comandos/eventos + +#include "esp_hci_transport.h" + +static void hci_packet_logger(uint8_t type, const uint8_t *data, uint16_t len) { + printf("[HCI %s] ", type == 0x01 ? "CMD" : type == 0x04 ? "EVT" : "DATA"); + for (int i = 0; i < (len < 20 ? len : 20); i++) { + printf("%02X ", data[i]); + } + if (len > 20) printf("..."); + printf(" (%d bytes)\n", len); +} + +// Hookear en HCI initialization +void app_main(void) { + // ... init code ... + + // TODO: No hay hook público en ESP-IDF para HCI logging en runtime + // Alternativa: Usar btmon en host Linux +} +``` + +--- + +### 6. GDB Macros para BLE + +```gdb +# File: ~/.gdbinit (o incluido en .vscode/.gdbinit) + +define ble_info + printf "=== BLE INFO ===\n" + printf "GAP State: %d\n", ble_hs_pvcy_set_gap_state + printf "GATT Handle: %d\n", gatt_conn_handle +end + +define ble_breakpoints + break ble_gap_event_fn + break ble_gattc_notification_cb + break ble_gatts_register_svc_cb + printf "BLE Breakpoints set\n" +end + +define ble_stack_trace + printf "Stack trace para BLE:\n" + bt 10 +end + +define inspect_conn_desc + p *(struct ble_gap_conn_desc *)$arg0 +end +``` + +--- + +### 7. Análisis de Logs Filtrados + +```bash +# Capturar logs durante debugging +idf.py -p /dev/ttyUSB0 monitor | tee debug_session.log + +# Post-análisis en bash +grep "BLE_GATT" debug_session.log +grep "ERROR" debug_session.log +grep "time to.*ms" debug_session.log +``` + +--- + +### 8. Testing Automatizado en C++ + +```cpp +// main/test_ble_suite.cpp (con Catch2 framework) + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" +#include "host/ble_hs.h" + +TEST_CASE("BLE Stack Initialization", "[ble]") { + int rc = ble_hs_init(); + REQUIRE(rc == 0); +} + +TEST_CASE("GATT Characteristic Access", "[gatt]") { + // Setup + uint8_t test_value = 42; + + // Test write + REQUIRE(gatt_test_write(test_value) == 0); + + // Verify + REQUIRE(test_value == 42); +} + +TEST_CASE("Security Pairing Flow", "[security]") { + // Mock central connect + REQUIRE(mock_central_connect() == 0); + + // Trigger pairing + REQUIRE(ble_sm_alg_p256() != NULL); +} +``` + +--- + +# Parte 3: Tips Avanzados + +--- + +## 1. Optimización de Consumo de Potencia + +```c +// Configurar connection parameters para batería +static struct ble_gap_upd_params battery_opt_params = { + .itvl_min = 800, // 1000 ms + .itvl_max = 800, + .latency = 199, // Skip 199 de cada 200 events + .supervision_timeout = 320, // 3.2 s + .min_ce_len = 0, + .max_ce_len = 0 +}; + +// Aplicar durante conexión +ble_gap_update_params(conn_handle, &battery_opt_params); + +// Medición en runtime +void measure_power_consumption(void) { + // Usar INA219 (power monitor) vía I2C si disponible + // O leer current consumption via ADC mock + uint32_t t_start = esp_timer_get_time(); + vTaskDelay(10000 / portTICK_PERIOD_MS); // 10 s + uint32_t t_elapsed = (esp_timer_get_time() - t_start) / 1000000; + + ESP_LOGI("PWR", "10 seconds elapsed, check device power meter"); +} +``` + +--- + +## 2. Rust + BLE (Interop) + +Si quieres **Rust para seguridad** + C para BLE: + +```rust +// main/src/lib.rs (bindgen-generated bindings) + +extern "C" { + pub fn ble_gap_adv_start( + own_addr_type: u8, + direct_addr: *const libc::c_void, + duration_ms: i32, + adv_params: *const libc::c_void, + cb: extern "C" fn(event: *mut libc::c_void) -> i32, + cb_arg: *mut libc::c_void, + ) -> i32; +} + +pub fn start_advertising_safe() -> Result<(), String> { + unsafe { + match ble_gap_adv_start( + 0, // own_addr_type + std::ptr::null(), + -1, // BLE_HS_FOREVER + std::ptr::null(), + gap_callback, + std::ptr::null_mut(), + ) { + 0 => Ok(()), + code => Err(format!("BLE error: {}", code)), + } + } +} +``` + +--- + +## 3. CUDA para Federated Learning Coordinator + +Si coordinas múltiples ESP32s con modelo ML: + +```cpp +// coordinator/src/federated_aggregator.cu (pseudo-code) + +__global__ void aggregate_gradients(float *global_model, float **local_gradients, + int num_devices, int model_size) { + int idx = blockIdx.x * blockDim.x + threadIdx.x; + if (idx < model_size) { + float sum = 0.0f; + for (int i = 0; i < num_devices; i++) { + sum += local_gradients[i][idx]; + } + global_model[idx] += (sum / num_devices) * LEARNING_RATE; + } +} + +void aggregate_from_ble_devices(std::vector gradients) { + // Convert uint8_t (quantized) to float + // Aggregate on GPU + // Send back to ESP32s +} +``` + +--- + +## 4. Verificación en Blockchain (Substrate) + +```rust +// substrate_pallet/src/lib.rs (pseudo) + +#[pallet::call_index(0)] +pub fn register_iot_reading( + origin: OriginFor, + device_id: u64, + temperature: u16, + signature: BoundedVec>, +) -> DispatchResult { + let caller = ensure_signed(origin)?; + + // Verificar BLE-signed data + ensure!( + verify_signature(&device_id.to_le_bytes(), &signature), + Error::::InvalidSignature + ); + + // Almacenar en blockchain + >::insert((caller, device_id), temperature); + + Ok(()) +} +``` + +--- + +**¡Con esto tienes debugging profundo y profesional para BLE + ESP32!**