upload learn from scratch book, exercises and projects
This commit is contained in:
1782
learn/BLE_ESP32_Fundamentals.md
Normal file
1782
learn/BLE_ESP32_Fundamentals.md
Normal file
File diff suppressed because it is too large
Load Diff
516
learn/BLE_Projects_roadmap_ideas.md
Normal file
516
learn/BLE_Projects_roadmap_ideas.md
Normal file
@@ -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<uint8_t*> 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<GattService>,
|
||||
}
|
||||
|
||||
impl BleDevice {
|
||||
pub fn new() -> Result<Self, BleError> {
|
||||
// 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<T>,
|
||||
device_id: u64,
|
||||
reading: u32,
|
||||
proof: BoundedVec<u8, ConstU32<64>>,
|
||||
) -> 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!** 📚
|
||||
986
learn/BLE_example_exercises.md
Normal file
986
learn/BLE_example_exercises.md
Normal file
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#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<uint8_t *> 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<T>,
|
||||
device_id: u64,
|
||||
temperature: u16,
|
||||
signature: BoundedVec<u8, ConstU32<64>>,
|
||||
) -> DispatchResult {
|
||||
let caller = ensure_signed(origin)?;
|
||||
|
||||
// Verificar BLE-signed data
|
||||
ensure!(
|
||||
verify_signature(&device_id.to_le_bytes(), &signature),
|
||||
Error::<T>::InvalidSignature
|
||||
);
|
||||
|
||||
// Almacenar en blockchain
|
||||
<Readings<T>>::insert((caller, device_id), temperature);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**¡Con esto tienes debugging profundo y profesional para BLE + ESP32!**
|
||||
Reference in New Issue
Block a user