Manual Técnico — BeeBox
Arquitectura completa del sistema · v2.2
← Manuales
Bee-Boys Solutions · InnovaTecNM 2026
⚙️
Manual
Técnico
BeeBox · Documentación Completa del Sistema
Stack: ESP32-S3 · Cloudflare Workers · KV · Pages · Vite
Firmware: Arduino C++ · WebServer · ArduinoJson v7
Versión del sistema: v2.2 · Mayo 2026
Asesor: Eric González Vallejo · Ing. Mecatrónica · TecNM Ciudad Hidalgo
Bee-Boys Solutions
Cajón Apícola Inteligente
Manual Técnico
Rev. 2.2 · 2026

Índice

1. Arquitectura del sistemaPág. 1
2. Módulo Worker API (Cloudflare)Pág. 2
3. Módulo Pages / Frontend (Vite)Pág. 2
4. Dashboard WebPág. 3
5. Firmware ESP32-S3Págs. 4–5
6. Seguridad del sistemaPág. 6
7. Puesta en marcha completaPág. 7
8. Errores conocidos y solucionesPág. 8
9. Preguntas frecuentes de juecesPágs. 9–10

1. Arquitectura del sistema

┌─────────────────────────────────────────────────────────┐ │ PLATAFORMA BEEBOX v2.2 │ ├─────────────────────────────────────────────────────────┤ │ │ │ [Apicultor / Teléfono] │ │ │ HTTPS │ │ ▼ │ │ [beeboxmx.com] ←────────────────────────────────┐ │ │ Cloudflare Pages │ │ │ Vite + Vanilla JS │ │ │ │ HTTPS POST/GET │ │ │ ▼ │ │ │ [Cloudflare Worker API] → [KV Store] │ │ │ beebox-api.workers.dev device:{pin} │ │ │ │ HTTP POST /api/sync cada 3s │ │ │ ▼ │ │ │ [ESP32-S3 en el cajón] ─── WiFi AP ─────────────┘ │ │ 192.168.4.1 (panel local) │ │ │ GPIO │ │ ┌──────┴──────┐ │ │ [Relay 1] [Relay 2] │ │ Nebulizador Ventilador │ └─────────────────────────────────────────────────────────┘

Flujo de comando (cloud → dispositivo)

1. Apicultor presiona "Activar 5 min" en beeboxmx.com → 2. Dashboard POST /api/command al Worker con PIN → 3. Worker guarda {pendingCommand:{action:"nebulize",duration:300}} en KV → 4. ESP32 hace POST /api/sync cada 3s → recibe el comando en la respuesta → 5. ESP32 activa GPIO 13 (relay nebulizador, lógica inversa: LOW=ON) → 6. Tras 300s el ESP32 apaga el relay automáticamente → 7. Dashboard lee estado actualizado vía GET /api/status
⚡ Módulo 1
2
Worker API — Cloudflare
workers/api.js · KV: BEEBOX_STORE · wrangler.toml

Endpoints

MétodoRutaAuthFunción
POST/api/syncX-Device-PinESP32 reporta estado y recibe pendingCommand
GET/api/statusX-Device-PinDashboard lee estado del dispositivo desde KV
POST/api/commandX-Device-PinDashboard envía comando (guardado en KV, el ESP32 lo recoge en el próximo sync)
POST/api/contactSin authFormulario de contacto de la landing page

Estructura del estado en KV

// Clave: device:{pin}  — ej: device:482916
{
  "nebulizer": "off",       // "on" | "off"
  "treatment": "off",       // "on" | "off"
  "pump": "off",            // "on" | "off"
  "battery": 85,            // 0–100
  "temperature": 24.3,      // °C
  "humidity": 61.0,         // %
  "fanOnSec": 5,            // segundos ON del ciclo
  "fanOffSec": 5,           // segundos OFF del ciclo
  "timeLeft": -1,           // segundos restantes del tratamiento
  "lastSeen": "2026-05-17T10:00:00.000Z",
  "pendingCommand": null,   // {action,duration} | null
  "history": []             // últimas 10 activaciones
}

Puesta en marcha del Worker

1
npm install en carpeta beebox-web
2
npx wrangler login → autenticar con cuenta Cloudflare
3
npx wrangler kv namespace create BEEBOX_STORE → copiar ID resultante a wrangler.toml
4
npx wrangler deploy → URL del Worker aparece al final
5
Verificar: https://beebox-api.SUBDOMINIO.workers.dev/api/status → debe responder {"error":"PIN requerido"}
🌐 Módulo 2
3
Pages / Frontend — Cloudflare
Vite · src/ → dist/ · Deploy automático en push a main

Variables de entorno (Cloudflare Dashboard)

VariableValor producción
VITE_WORKER_URLhttps://beebox-api.banarysource.workers.dev
VITE_SITE_URLhttps://beeboxmx.com
⚠️ Error frecuente
Si VITE_WORKER_URL apunta a beeboxmx.com (la Pages misma), el dashboard no llega al Worker. Debe apuntar siempre al subdominio .workers.dev.

Rutas del sitio

RutaArchivo
/src/index.html
/dashboardsrc/dashboard.html
/presentacion.htmlsrc/public/presentacion.html
/print/src/public/print/index.html
💻 Módulo 3
4
Dashboard Web
src/dashboard.html · src/js/dashboard.js · src/js/api.js

Flujo de autenticación

Usuario ingresa PIN → handleLogin() valida formato (/^\d{6}$/) → PIN especial 311286 muestra easter egg → getStatus() llama Worker con header X-Device-Pin → si responde 200, guarda PIN en localStorage['beebox_pin'] → muestra dashboard → inicia polling cada 10s.

Modos de conexión (api.js)

ModoCondiciónEndpointIndicador
cloudFetch Worker exitoso y lastSeen < 45shttps://beebox-api…/api/status🟢 Nube activa
localWorker falla → fetch 192.168.4.1/status exitosohttp://192.168.4.1/status🟡 Conexión local (AP)
offlineAmbos fallan o lastSeen > 45s🔴 Sin conexión

Command lock — UI optimista

Al enviar un comando, el dashboard actualiza la UI de inmediato (optimista) y bloquea el polling 9 segundos (LOCK_MS=9000) para evitar que el poll sobreescriba el estado antes de que el ESP32 confirme. Si el ESP32 responde antes, el lock se libera y muestra "✓ Confirmado".

Controles disponibles

AcciónComando enviadoDescripción
Activar N min / manual{action:"nebulize", duration:N}N=segundos, 0=manual sin límite
Detener tratamiento{action:"nebulize_off"}Apaga nebulizador y ventilador
Nebulizador individual{action:"neb_on"} / neb_offSin ciclo de ventilador
Ventilador individual{action:"fan_on"} / fan_offPara pruebas independientes
Ciclo del ventilador{action:"fan_cycle", onSec:N, offSec:N}Guarda en NVS del ESP32

Countdown del tratamiento

El timer es cliente-side (localStorage: bb_neb_start + bb_neb_dur). Resiste recarga de página. El ESP32 también lleva su propio timer independiente (nebulizerEndMs). Si el ESP32 termina el tratamiento antes, el poll de 10s lo detecta y limpia el timer del cliente.

🔌 Módulo 4 — Firmware (1/2)
5
Firmware ESP32-S3
firmware/beebox_esp32/beebox_esp32.ino · Arduino IDE · ArduinoJson v7

Hardware — ESP32-S3 Supermini

PinFunciónLógica
GPIO 13Relay nebulizadorInversa: LOW=ON
GPIO 12Relay ventiladorInversa: LOW=ON
GPIO 5Reset de fábricaINPUT_PULLUP → GND
GPIO 0BOOT (bootloader)Solo para programar
GPIO 48LED RGB WS2812BRequiere NeoPixel
Parámetro Arduino IDEValor
BoardESP32S3 Dev Module
USB CDC On BootEnabled
Flash Size4MB (32Mb)
Partition SchemeDefault 4MB with spiffs
Upload Speed921600

Estados del firmware

EstadoCondiciónComportamiento
SETUPFlash sin config (primer boot / post-reset)WIFI_AP · BeeBox-XXXX sin contraseña · DNS capta todo tráfico → 192.168.4.1 · Sirve portal de configuración branded
RUNNINGConfig guardada en NVS (PIN + SSID + Worker)WIFI_AP_STA · AP siempre activo + conecta a WiFi · POST /api/sync cada 3s · Servidor local en 192.168.4.1 con auth PIN

AP SSID consistente

El SSID se calcula una sola vez en setup() usando ESP.getEfuseMac() antes de inicializar WiFi. Esto garantiza el mismo SSID en ambos modos. WiFi.macAddress() retorna MACs distintas según el modo WiFi (bug conocido del core ESP32).

uint64_t m = ESP.getEfuseMac();
snprintf(gApSSID, 20, "BeeBox-%02X%02X",
  (uint8_t)((m >> 32) & 0xFF),
  (uint8_t)((m >> 40) & 0xFF));

Ciclo del ventilador durante tratamiento

El ventilador cicla automáticamente durante el tratamiento activo. El ciclo (5s ON / 5s OFF por defecto) se controla en loop() usando fanLastToggleMs y es configurable vía comando fan_cycle. Los valores se persisten en NVS (fanOnMs / fanOffMs).

Persistencia NVS (flash interno)

ClaveValorModificado por
beebox/ssidSSID de la red WiFiPortal setup · /save-wifi
beebox/passContraseña WiFiPortal setup · /save-wifi
beebox/pinPIN de 6 dígitosPortal setup (no modificable desde dashboard)
beebox/workerURL del WorkerPortal setup (sección avanzada)
beebox/fanOnMsTiempo ON del ventilador (ms)Comando fan_cycle desde dashboard
beebox/fanOffMsTiempo OFF del ventilador (ms)Comando fan_cycle desde dashboard
🔌 Módulo 4 — Firmware (2/2)

Rutas del servidor local (running mode)

RutaMétodoAuthHandler
/GETSirve HTML_LOCAL (panel completo embebido)
/okGETPantalla de espera post-configuración con polling activo a /ping
/pingGETResponde {"ok":true} sin auth — usado por /ok para detectar reinicio
/statusGETX-Pin ✓Devuelve stateJson() — estado real de todas las salidas y sensores
/commandPOSTX-Pin ✓Ejecuta comando JSON → executeCommand()
/change-wifiGETSesión ✓Scanner de redes WiFi con selección interactiva
/save-wifiPOSTSesión ✓Guarda nueva red WiFi · reconnecta sin reinicio · 303 → /?wf=1

Loop principal — tareas por intervalo

TareaIntervaloCondición
Reinicio diferido (post-portal-save)pendingRestart=true y millis()≥restartAt
WiFi.begin() diferido (post-save-wifi)150mspendingWifiBegin=true y millis()≥wifiBeginAt
Temporizador del tratamientoContinuotreatmentOn y millis()≥nebulizerEndMs
Ciclo ventiladorfanCycleOnMs / fanCycleOffMstreatmentOn=true
Lectura de sensores10sSiempre (running mode)
Sync con Worker3sWiFi.status()==WL_CONNECTED
Reconexión WiFi20sWiFi desconectado y SSID configurado
Reset de fábricaContinuoGPIO 5 LOW durante ≥10s

stateJson() — estado en tiempo real

El endpoint /status llama stateJson() que lee directamente los globales en RAM del firmware: nebulizerOn, treatmentOn, fanOn, nebulizerEndMs. Estos globales nunca se borran cuando cae WiFi — el panel local siempre muestra el estado físico real.

// Respuesta típica de /status:
{"nebulizer":"on","treatment":"on","pump":"off",
 "battery":82,"temperature":25.3,"humidity":68.0,
 "fanOnSec":5,"fanOffSec":5,"timeLeft":247}

Captive portal (setup mode)

Endpoints para captive portal detection de iOS, Android y Windows: /hotspot-detect.html y /generate_204 responden 200 con meta-refresh a 192.168.4.1. /connecttest.txt, /redirect y onNotFound responden 302 → 192.168.4.1. Esto activa el popup automático del OS en iOS 14+ y Android 10+.

🔒 Módulo 5
6
Seguridad del sistema
Auditoría v2.2 · 5 correcciones aplicadas · Mayo 2026

Capas de seguridad

CapaMecanismoAlcance
CloudHeader X-Device-Pin en todas las peticiones al WorkerTodas las rutas /api/* excepto /api/contact
Local — comandosHeader X-Pin validado en checkPin()/status y /command
Local — páginasSesión sessionValidUntil renovada por checkPin() (5 min)/change-wifi y /save-wifi
Transporte cloudHTTPS obligatorio (Cloudflare)Todas las peticiones al Worker y Pages
Persistencia PINNVS del ESP32 (flash cifrado internamente)PIN nunca viaja en texto en URLs

Auditoría de seguridad v2.2 — hallazgos y correcciones

#SeveridadProblemaCorrección aplicada
1CriticalPIN expuesto en URL (/change-wifi?pin=XXXXXX) → queda en historial del navegadorSesión local (sessionValidUntil) renovada por checkPin(). /change-wifi y /save-wifi validan sesión, no PIN en URL
2WarningVLA (Variable Length Array) en buildWifiList(): int idx[n] no es C++ estándar, posible stack overflowArray fijo int idx[MAX_NETS=20]; sort sobre min(n,20) redes
3Warningdelay(100) bloqueante en handleSaveWifi() bloquea server.handleClient() durante HTTP responseWiFi.disconnect() antes del 303; WiFi.begin() diferido 150ms en loop() via flag pendingWifiBegin
4WarningsendLocalJson() no incluía X-Pin en Access-Control-Allow-Headers (inconsistente con handleLocalOptions)Corregido a "Content-Type, X-Pin" en sendLocalJson()
5InfoComentario duplicado "Configuración: NVS (flash)" en el firmwareEliminado el bloque duplicado

Flujo de sesión local

Usuario ingresa PIN en HTML_LOCAL → fetch('/status', {headers:{'X-Pin':pin}})checkPin() valida → si OK: sessionValidUntil = millis() + 300000 → Usuario abre /change-wifi → handleChangeWifi() verifica millis() < sessionValidUntil → si sesión expiró (5 min sin actividad) → redirect a / para re-login.
✓ PIN nunca aparece en URLs ni en código fuente del HTML servido
El hidden field del formulario /change-wifi fue eliminado en la auditoría v2.2. El único lugar donde viaja el PIN es el header X-Pin en peticiones AJAX al servidor local.
7
Puesta en marcha completa
Desde cero hasta sistema funcionando · Verificado en producción
Estado producción actual (Mayo 2026)
Worker: https://beebox-api.banarysource.workers.dev ✓ · Pages: https://beeboxmx.com ✓ · Firmware: v2.2 compilado y probado en ESP32-S3 Supermini ✓

Primera vez — sistema nuevo

#PasoComando / AcciónVerificación
1Dependenciasnpm install en beebox-web/Sin errores en consola
2Auth Cloudflarenpx wrangler loginNavegador muestra "Logged in"
3Crear KVnpx wrangler kv namespace create BEEBOX_STORECopiar ID devuelto a wrangler.toml
4Deploy Workernpx wrangler deployURL .workers.dev en consola
5Var. entorno PagesCloudflare Dashboard → Pages → Settings → Env VarsVITE_WORKER_URL = URL del Worker
6Redeploy PagesCloudflare Dashboard → Pages → Deployments → RetryBuild exitoso, sin errores Vite
7Instalar libreríaArduino IDE → Tools → Manage Libraries → ArduinoJson v7Librería aparece instalada
8Config ArduinoBoard: ESP32S3 Dev Module · USB CDC: Enabled · Flash: 4MB · Erase All Flash: EnabledBoard seleccionada correctamente
9Primer flashCompilar y subir (Ctrl+U)Serial Monitor 115200: "[Setup] Red WiFi: BeeBox-XXXX"
10Configurar ESP32Conectar celular a BeeBox-XXXX → portal → WiFi + PIN + GuardarDispositivo reinicia y conecta
11Verificar cloudbeeboxmx.com → ingresar PINIndicador 🟢 Nube activa
12Test nebulizaciónBotón "1 min" en dashboardSerial: "[RELAY] Nebulizador ON 60s"

Flasheos siguientes (sin borrar NVS)

Cambiar Erase All Flash Before Sketch Upload a Disabled. Esto preserva la configuración WiFi y PIN en NVS. Solo volver a Enabled si el ESP32 no arranca correctamente (core dump en bucle).

8
Errores conocidos y soluciones
Clasificados por módulo · Verificados en pruebas reales

Firmware / Arduino IDE

Síntoma / ErrorCausaSolución
Core dump flash config is corrupted + bucle de reinicioFlash con datos de sketch anterior incompatiblesTools → Erase All Flash: Enabled → subir de nuevo
ArduinoJson.h: No such file or directoryLibrería no instaladaTools → Manage Libraries → ArduinoJson v7.x
Puerto COM no aparece en Arduino IDENecesita modo bootloader manualMantener BOOT → conectar USB → soltar BOOT → Upload
Serial Monitor vacío sin outputUSB CDC On Boot desactivadoTools → USB CDC On Boot → Enabled → reflashear
exceeds flash chip size 0x400000Flash Size configurado como 8MB en lugar de 4MBTools → Flash Size → 4MB (32Mb)
SSIDs diferentes BeeBox-CA3F (setup) vs BeeBox-105C (running)WiFi.macAddress() retorna MAC diferente según modo WiFiResuelto en v2.2: usar ESP.getEfuseMac() en setup()

Worker / Cloud

Síntoma / ErrorCausaSolución
command not found: wranglerNo instalado globalmenteUsar npx wrangler en lugar de wrangler
KV namespace not foundIDs en ceros en wrangler.tomlCrear namespace y pegar ID real en wrangler.toml
{"error":"PIN requerido"}Petición sin header X-Device-Pin (normal al probar con navegador)No es error — el Worker funciona correctamente
{"error":"Dispositivo no encontrado"}PIN nunca hizo sync (dispositivo nunca ha conectado)El ESP32 debe hacer al menos un POST /api/sync exitoso
[Cloud] HTTP 401PIN guardado en NVS no coincide con el registrado en el WorkerReset de fábrica → reconfigurar con PIN correcto

Dashboard / Frontend

SíntomaCausaSolución
Siempre muestra "Sin conexión" aunque el ESP32 esté activoVITE_WORKER_URL apunta a beeboxmx.com en lugar del WorkerCloudflare Pages → Env Vars → corregir a la URL .workers.dev → redeploy
Botones deshabilitados aunque hay conexiónlastSeen en KV tiene más de 45s (ESP32 sin internet)Normal — el dashboard pasa a modo offline. Usar panel local 192.168.4.1
Countdown desaparece al recargar la páginalocalStorage no disponible (modo privado / incógnito)Usar navegador normal. El ESP32 mantiene su propio timer independiente
9
Preguntas frecuentes de jueces
Respuestas preparadas · InnovaTecNM 2026
¿Cómo funciona el sistema de forma resumida?
BeeBox es una plataforma IoT de 4 capas: dispositivo ESP32-S3 con relays en el cajón, API serverless en Cloudflare Workers que almacena el estado en KV, dashboard web en Cloudflare Pages, y panel local en el propio ESP32 vía AP WiFi. El apicultor activa tratamientos desde cualquier dispositivo con internet; el ESP32 sincroniza cada 3 segundos y ejecuta los comandos de forma autónoma.
¿Qué pasa si no hay internet en el apiario?
El ESP32 siempre crea su propia red WiFi (BeeBox-XXXX) simultáneamente con la conexión a internet, usando el modo WIFI_AP_STA. El apicultor se conecta directamente a esa red y accede al panel de control local en 192.168.4.1. Las funciones son idénticas al panel cloud. El AP permanece activo incluso cuando hay conexión WiFi.
¿Cómo garantizan la seguridad del sistema?
Cuatro capas: (1) PIN de 6 dígitos almacenado en flash del ESP32, nunca en texto en URLs; (2) peticiones locales con header HTTP X-Pin validado por el servidor; (3) sesión local de 5 minutos que expira tras inactividad; (4) comunicación cloud vía HTTPS obligatorio en Cloudflare. En la auditoría de seguridad v2.2 identificamos y corregimos 5 vulnerabilidades incluyendo PIN expuesto en URLs.
¿Por qué nebulización piezoeléctrica y no resistiva o térmica?
Los nebulizadores piezoeléctricos generan aerosol frío (~4°C sobre temperatura ambiente) sin elemento calefactor. Esto es crítico para las abejas: el calor por encima de 35°C las estresa y puede dañar la cera. Además consumen 10–20 veces menos energía (1–2W vs 20–50W del resistivo), tienen vida útil más larga (3,000–5,000 horas) y el repuesto cuesta menos de $80 MXN.
¿Cuánto cuesta el sistema completo?
Componentes por unidad: ESP32-S3 Supermini ~$90 MXN, módulo relay doble ~$35 MXN, nebulizador piezoeléctrico ~$140 MXN, caja de control y cableado ~$80 MXN. Total componentes: ~$345 MXN (~$18 USD). Precio de venta: $1,200 MXN. La infraestructura cloud (Cloudflare) es gratuita hasta ~3,500 dispositivos activos simultáneos.
¿Pueden manejar múltiples cajones por apicultor?
Sí. La arquitectura escala horizontalmente: cada cajón tiene su propio ESP32 con un PIN único. El mismo Worker y KV store maneja n dispositivos sin cambios en el código. El roadmap v3.0 incluye un panel administrador para controlar múltiples cajones desde una cuenta, con activación grupal de tratamientos.
¿Qué tan precisos son los sensores de temperatura y humedad?
En la versión de demo actual usamos sensores simulados para mostrar el flujo completo de datos. El firmware tiene los hooks listos para integrar un sensor DHT22 real (±0.5°C de precisión en temperatura, ±2% en humedad) en GPIO 4. El hardware de control de relays y nebulización está completamente funcional y validado. Los sensores son el siguiente paso de desarrollo (roadmap v2.3, Q3 2026).
¿Por qué Cloudflare Workers y no AWS Lambda o un servidor propio?
Tres razones principales: (1) Latencia edge: Cloudflare Workers ejecuta en el edge point más cercano al usuario, sin cold starts (vs 100–500ms de AWS Lambda). (2) Costo: plan gratuito cubre 100,000 requests/día por Worker, suficiente para ~3,500 dispositivos sincronizando cada 3 segundos. (3) Simplicidad: sin gestión de servidores, escala automáticamente, KV integrado para almacenamiento de estado.
¿Cómo protegen los datos personales del apicultor?
El sistema no recopila datos personales. El único identificador es el PIN de 6 dígitos que el propio apicultor elige. Los datos almacenados en Cloudflare KV son exclusivamente estado del dispositivo (temperatura, humedad, historial de activaciones). No hay nombre, email, teléfono ni geolocalización. Cloudflare cumple GDPR y tiene certificaciones SOC 2 Type II.
¿Cuál es la vida útil del sistema y qué mantenimiento requiere?
El módulo ESP32-S3 no tiene partes móviles y su MTBF es >50,000 horas (~6 años continuo). El nebulizador piezoeléctrico es el único consumible: vida útil de 3,000–5,000 horas de operación (con tratamientos semanales de 10 minutos son más de 10 años). Mantenimiento: limpiar el transductor con agua destilada cada 3 meses. El firmware se actualiza vía USB con Arduino IDE.
¿Qué tratamiento aplican exactamente? ¿Está certificado?
El sistema nebuliza ácido oxálico dihidratado al 4.2% en solución acuosa tibia, el tratamiento orgánico estándar certificado para control de Varroa destructor en muchos países. Las partículas de 1–5 micras penetran entre los opérculos de cría cerrada. El ventilador cicla (5s ON / 5s OFF) para distribuir uniformemente el aerosol en todo el volumen del cajón. BeeBox no produce ni vende el compuesto; el apicultor lo prepara según protocolo estándar.
¿Cómo validan que el tratamiento fue efectivo?
El dashboard registra cada activación con fecha, hora, duración y origen (nube/local) en el historial. Los sensores de humedad permiten confirmar que el aerosol se dispersó (la humedad relativa sube durante la nebulización). La efectividad biológica del ácido oxálico sobre Varroa es ampliamente documentada en la literatura científica apícola con tasas de mortalidad parasitaria del 90–97%.
¿Qué diferencia a BeeBox de otras soluciones en el mercado?
BeeBox combina tres factores únicos para el mercado mexicano: (1) Costo accesible: componentes por $345 MXN, infraestructura cloud gratuita; las soluciones importadas cuestan $3,000–8,000 MXN. (2) Control remoto real: otras soluciones son timers físicos; BeeBox es la única con app web y control desde cualquier lugar. (3) Soporte local en español: desarrollado en México, documentado en español, equipo disponible para soporte.