Firmware seguro para dispositivos IoT basado en ESP32-C3, escrito en Rust con ESP-IDF.
┌─────────────────────────────────────────────────────────────────┐
│ ESP32-C3 │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ WiFi │ │ Secure │ │ Device Client │ │
│ │ Module │ │ Storage │ │ (HTTP Polling) │ │
│ │ │ │ (NVS) │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │
│ │ │ │ │
│ └────────────────┼─────────────────────┘ │
│ │ │
│ ┌───────────────────────┴───────────────────────────────────┐ │
│ │ Main Loop │ │
│ │ - Poll commands cada 5s │ │
│ │ - Enviar telemetria cada 60s │ │
│ │ - Ejecutar comandos recibidos │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
│ HTTPS
▼
┌─────────────────────────────────────────────────────────────────┐
│ core.leonobitech.com │
├─────────────────────────────────────────────────────────────────┤
│ POST /api/devices/register - Registrar dispositivo │
│ POST /api/devices/:id/telemetry - Enviar telemetria │
│ GET /api/devices/:id/commands/pending - Obtener comandos │
│ POST /api/devices/:id/commands/:id/ack - Confirmar comando │
│ POST /api/devices/:id/status - Enviar estado │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ ESP32-C3 │ │ Usuario │ │ Backend │
│ (Sin config)│ │ (Movil/PC) │ │ │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ Inicia SoftAP │ │
│ "Leonobitech-Setup"│ │
│ Pass: "setup1234" │ │
│◄───────────────────┤ │
│ Conecta WiFi │ │
│ │ │
│◄───────────────────┤ │
│ GET 192.168.4.1 │ │
│ │ │
│────────────────────► │
│ Portal HTML │ │
│ │ │
│◄───────────────────┤ │
│ POST /configure │ │
│ {ssid, password, │ │
│ deviceId, apiKey}│ │
│ │ │
│ Guarda en NVS │ │
│ (encriptado) │ │
│ │ │
│ ══════ RESTART ════│ │
│ │ │
┌──────────────┐ ┌──────────────┐
│ ESP32-C3 │ │ Backend │
└──────┬───────┘ └──────┬───────┘
│ │
│ Conecta WiFi (credenciales NVS) │
│ │
│─────── POST /register ──────────────────────►
│ {device_id, firmware_version} │
│◄────────────────────────────────────────────│
│ {status: "success"} │
│ │
│ │
│ ══════════ LOOP PRINCIPAL ══════════ │
│ │
│ [Cada 5s] │
│─────── GET /commands/pending ───────────────►
│◄────────────────────────────────────────────│
│ {commands: [...]} │
│ │
│ [Si hay comando] │
│ Ejecuta comando │
│─────── POST /commands/:id/ack ──────────────►
│ {success: true, message: "..."} │
│ │
│ [Cada 60s] │
│─────── POST /telemetry ─────────────────────►
│ {free_heap, wifi_rssi, uptime} │
│ │
hardware/
├── .cargo/
│ └── config.toml # Configuracion de Cargo para ESP32
├── .github/
│ └── workflows/
│ └── rust_ci.yml # CI/CD con GitHub Actions
├── src/
│ ├── main.rs # Entry point y loop principal
│ ├── wifi.rs # Conexion WiFi WPA2/WPA3
│ ├── provisioning.rs # Portal SoftAP para configuracion
│ ├── secure_storage.rs # NVS encriptado para credenciales
│ ├── device_client.rs # Cliente HTTP para backend
│ └── http_client.rs # Cliente HTTP generico (legacy)
├── Cargo.toml # Dependencias Rust
├── build.rs # Script de build
├── sdkconfig.defaults # Configuracion ESP-IDF
├── rust-toolchain.toml # Version de Rust (nightly)
└── README.md
| Comando | Descripcion | Parametros |
|---|---|---|
get_status |
Solicita estado completo del dispositivo | - |
led_on |
Enciende LED | - |
led_off |
Apaga LED | - |
set_brightness |
Ajusta brillo | {level: 0-100} |
restart |
Reinicia dispositivo | - |
factory_reset |
Borra credenciales y reinicia | - |
| Modo | Config | Secure Boot | Flash Encryption | Uso |
|---|---|---|---|---|
| Development | sdkconfig.defaults |
No | No | CI, desarrollo local |
| Production | sdkconfig.defaults.production |
Si | Si | Dispositivos finales |
- Verifica firma del firmware antes de ejecutar
- Previene ejecucion de firmware no autorizado
- Generar clave:
espsecure.py generate_signing_key --version 2 secure_boot_signing_key.pem - IMPORTANTE:
secure_boot_signing_key.pemNO debe commitearse
- Encripta contenido de la flash
- Protege firmware y datos almacenados
- Habilitado solo con
sdkconfig.defaults.production
- Credenciales WiFi encriptadas en NVS
- API Key hasheada antes de almacenar
- Zeroization de credenciales en memoria despues de uso
- Solo HTTPS con certificados de CA bundle
- Headers
x-device-idyx-api-keypara autenticacion - API Key hasheada con HMAC-SHA512 en backend
# Instalar Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Instalar toolchain ESP32
cargo install espup
espup install
# Configurar variables de entorno
. $HOME/export-esp.sh# Build debug (development)
cargo build
# Build release (development/CI - sin Secure Boot)
cargo build --release
# Build release PRODUCTION (con Secure Boot)
# Requiere: secure_boot_signing_key.pem
SDKCONFIG_DEFAULTS=sdkconfig.defaults.production cargo build --release
# Flash al dispositivo
cargo run --releaseEditar sdkconfig.defaults para ajustar:
- Tamano de particiones
- Configuracion WiFi
- Opciones de seguridad
Datos enviados cada 60 segundos:
{
"device_id": "esp32-001",
"free_heap": 245760,
"wifi_rssi": -52,
"uptime_secs": 3600,
"sensors": null
}El firmware se comunica con core.leonobitech.com:
| Endpoint | Metodo | Descripcion |
|---|---|---|
/api/devices/register |
POST | Registro inicial |
/api/devices/:id/telemetry |
POST | Envio de telemetria |
/api/devices/:id/commands/pending |
GET | Obtener comandos |
/api/devices/:id/commands/:cmdId/ack |
POST | Confirmar ejecucion |
/api/devices/:id/status |
POST | Enviar estado completo |
- leonobitech/backend - API Backend (Express + Prisma)
- leonobitech/frontend - Dashboard (Next.js)
Propietario - Leonobitech