dummybot/docs/FIRMWARE.md
javiservices 3a80d5e76b
Some checks failed
Deploy DummyBot / Build & Deploy to ${{ env.DEPLOY_HOST }} (push) Has been cancelled
docs: documentar deploy automático + firmware ESP32
2026-06-18 12:32:35 +02:00

190 lines
6.2 KiB
Markdown

# Firmware del ESP32
Firmware para el bot físico DummyBot. ESP32 + OLED SSD1306 128x64 + WiFi
+ polling backend.
## Hardware
- **ESP32-D0WD-V3** (DevKitC / WROOM-32, 4 MB flash)
- **Pantalla OLED SSD1306 0.96"** (128x64, I2C)
- **3 botones** (NEXT / DONE / SNOOZE)
- **1 motor vibrador 1027** (3V DC, MOSFET en GPIO 25)
### Pinout
| Función | GPIO | Notas |
|------------------|-------|--------------------------------------|
| OLED SDA | 21 | I2C data |
| OLED SCL | 22 | I2C clock |
| OLED addr | 0x3C | I2C address |
| Botón NEXT | 26 | INPUT_PULLDOWN, flanco ascendente |
| Botón DONE | 27 | INPUT_PULLDOWN, flanco ascendente |
| Botón SNOOZE | 14 | INPUT_PULLDOWN, flanco ascendente |
| Vibrador | 25 | OUTPUT, MOSFET gate |
| BOOT (reset cfg) | 0 | INPUT_PULLUP, mantener 5s al arrancar |
### Cableado OLED
```
ESP32 (WROOM-32) OLED SSD1306 0.96"
───────────────────── ─────────────────────
3V3 ───────▶ VDD
GND ───────▶ GND
GPIO 22 ───────▶ SCK (I2C SCL)
GPIO 21 ───────▶ SDA (I2C SDA)
```
### Cableado botones
Los botones se conectan entre GND y el GPIO correspondiente. Con
`INPUT_PULLDOWN` interno del ESP32, el GPIO lee LOW en reposo y HIGH al
pulsar. El firmware detecta el flanco ascendente con debounce de 200 ms.
```
ESP32 Botón (entre pin y GND)
───── ─────
GPIO 26 ──────▶ Siguiente
GPIO 27 ──────▶ Hecha
GPIO 14 ──────▶ Snooze 5 min
GND ──────▶ Común
```
### Cableado vibrador
```
ESP32 MOSFET (AO3400 / 2N7000) Vibrador 1027
───── ────── ─────
GPIO 25 ──[100Ω]──▶ Gate
├──▶ Source ──▶ GND
└──▶ Drain ──▶ Vibrador ──▶ 3V3
```
Si solo se dispone de NPN (BC547 / 2N2222 / S8050), esquema alternativo:
motor entre 3V3 y colector, resistencia 1kΩ en base.
## Build flags (platformio.ini)
```ini
build_flags =
-DCORE_DEBUG_LEVEL=3
-DUSER_SETUP_LOADED=1
-DDEFAULT_WIFI_SSID=\"PHARMACO\"
-DDEFAULT_WIFI_PASS=\"arnoldgym2010\"
-DBACKEND_URL=\"http://157.180.77.232:18080\"
-DDEFAULT_DEVICE_TOKEN=\"<token-del-bot>\"
```
## Build y flasheo
```bash
# Compilar
pio run
# Flashear
pio run -t upload --upload-port /dev/ttyUSB0
# Monitor serie
pio device monitor --port /dev/ttyUSB0 --baud 115200
```
## Configuración en runtime (captive portal)
Si no hay config WiFi válida en NVS, el bot abre un AP `DummyBot-Setup`
(pass `dummybot`) en `192.168.4.1`. Desde el móvil, configurar WiFi y token
del dispositivo. El bot se reinicia automáticamente y conecta.
Para resetear la config: mantener pulsado el botón **BOOT (GPIO 0)** durante
5 segundos al arrancar.
## Comandos serie (debug)
Conectar a `115200 baud`:
| Comando | Acción |
|---------|-------------------------------------------------|
| `S` | Imprime estado (WiFi, IP, tarea actual) |
| `B0` | Simula pulsación botón NEXT (GPIO 26) |
| `B1` | Simula pulsación botón DONE (GPIO 27) |
| `B2` | Simula pulsación botón SNOOZE (GPIO 14) |
| `P` | Lee estado actual de los 3 pines de botón |
| `A` | Lectura continua del ADC en GPIO 34 (60s) |
| `C` | Calibración guiada de umbrales ADC (3 botones) |
| `W` | Watch: monitor rápido de cambios en pines |
## Arquitectura del firmware
```cpp
// Namespace principales
namespace Config { void load(); void save(); void clear(); }
namespace UI { showBoot, showConnecting, showIdle, showTask, showPortal }
namespace Vib { buzz(), tick() patrón 2-buzz al recibir tarea nueva }
namespace Api { pollPending(), markDone(), snooze() }
namespace Input { tick() polling 3 botones con debounce por flanco }
namespace Portal { start(), stop(), tick() AP + DNS + WebServer }
namespace Wifi { wifiStartSTA(), wifiTick() máquina de estados }
```
### Máquina de estados WiFi
```
BOOT → CONNECTING (timeout 20s)
├─ OK → CONNECTED (poll cada 30s)
└─ FAIL → PORTAL (AP DummyBot-Setup)
CONNECTED
└─ WiFi perdido → CONNECTING
PORTAL
└─ ESP.restart() al guardar config
```
### Flujo de polling
```
CONNECTED
→ Api::pollPending() cada 30s
→ GET /api/device/tasks/pending
→ Si tiene tareas: muestra primera en OLED
→ Si cambia id: buzz (vibrador)
```
## Troubleshooting
### WiFi no conecta
- Verificar SSID y password (son case-sensitive).
- Si el router está en 5 GHz, el ESP32 solo soporta 2.4 GHz.
- Si la red bloquea nuevos dispositivos (MAC filtering), añadir la MAC del
ESP32 (visible en el log de `WiFi.status()`).
### Botones no responden
- Verificar que el botón hace contacto entre el GPIO y GND.
- Comandos serie `P` muestra el estado actual de los pines. Pulsar y ver
si cambia de 0 a 1.
- Si el botón usa active-low (HIGH en reposo, LOW al pulsar), cambiar a
`INPUT_PULLUP` y la lógica de flanco a descendente.
### OLED no enciende
- Verificar 3V3 en VDD del OLED.
- Verificar SDA (GPIO 21) y SCL (GPIO 22) — pines fijos I2C del ESP32.
- I2C addr por defecto 0x3C. Si la OLED es 0x3D, cambiar `OLED_ADDR`.
### API no responde
- Verificar que el bot tiene acceso a internet (ping al backend desde el
ESP32 con el comando serie `S`).
- Si el backend usa HTTPS con cert auto-firmado, hay que añadir el cert al
firmware (más complejo) o usar HTTP plano.
## Versiones
- **v6.1** (actual): producción, sin botones (pendiente nueva placa).
Funcionalidad core: WiFi, OLED, polling, portal cautivo.
- **v6.0** (legacy): intentona con ADC para matriz de botones, no funcionó
en hardware del usuario.
- **v5** (legacy): SPIFFS en lugar de NVS, varios bugs en defaults WiFi.