Sensor y telemetría¶
En este capítulo el filtro comienza a medir el aire y enviar datos a la nube. La técnica clave es tu propio campo en telemetría: el diccionario del ecosistema no sabe nada sobre VOC, pero el núcleo permite agregar cualquier campo a la telemetría.
1. Biblioteca del sensor¶
En platformio.ini agrega a lib_deps:
2. Leer SGP40¶
En src/main.cpp (los pines son del esquema):
#include <Wire.h>
#include <Adafruit_SGP40.h>
static Adafruit_SGP40 s_sgp;
static int32_t g_vocIndex = -1; // -1 = sin datos aún
static void initVocSensor() {
Wire.begin(/*SDA=*/8, /*SCL=*/9);
if (!s_sgp.begin()) {
Serial.println("[VOC] SGP40 not found, check wiring");
}
}
static void readVocSensor() {
// measureVocIndex() realiza su propia compensación interna del sensor.
// Índice: ~100 = aire normal, más alto = más sucio (máx 500).
g_vocIndex = s_sgp.measureVocIndex();
}
Agrega la llamada a initVocSensor() en setup() después de s_link.begin(), y readVocSensor() — en loop() una vez por segundo (con temporizador millis, ¡no a través de delay!):
static uint32_t s_lastReadMs = 0;
void loop() {
s_link.loop();
uint32_t now = millis();
if (now - s_lastReadMs >= 1000) {
s_lastReadMs = now;
readVocSensor();
}
}
Sin delay() en loop
s_link.loop() debe llamarse constantemente — en él se mantienen Wi-Fi, MQTT y comandos del portal. delay(1000) congelará todo esto. Solo temporizadores millis.
3. Tu propio campo en telemetría¶
Cada telemetryPeriodMs el núcleo reúne automáticamente un mensaje JSON de telemetría y lo envía a la nube. Para tu dispositivo (una unidad, solo ventilador del diccionario) el núcleo reúne este mensaje:
Desglosemos la estructura:
units— array de unidades (cámaras) del dispositivo. El secador iDryer en serie puede tener hasta cuatro cámaras independientes, por eso la telemetría es siempre un array, incluso si hay una cámara;units[0]— primera (y única para nosotros) unidad: especificamosunitsCount = 1enConfig;fanStatus— campo del diccionario, apareció porquehasFan = true;rssi,uptime— nivel de Wi-Fi y tiempo de trabajo, el núcleo siempre agrega esto.
No hay nada sobre VOC en este mensaje — el núcleo no sabe del sensor. Pero justo antes de enviar, el núcleo le da al código la oportunidad de agregar sus propios campos al mensaje. Para hacer esto, registras un callback (devolución de llamada, "llamada inversa") — una función que le das al núcleo, y el núcleo mismo la llama en cada publicación, pasando adentro el JSON recopilado (el argumento doc es ese).
En setup():
s_link.onTelemetryPublish([](JsonObject doc) {
// doc — ese mismo mensaje de telemetría recopilado por el núcleo (ver JSON arriba).
// Agregamos nuestro campo vocIndex a la primera unidad.
if (g_vocIndex >= 0) {
doc["units"][0]["vocIndex"] = g_vocIndex;
}
});
La línea doc["units"][0]["vocIndex"] = g_vocIndex; se lee así: "en el mensaje doc toma el array units, en él el elemento 0 (nuestra única unidad) y escribe allí el campo vocIndex". El nombre del campo lo eliges tú — en el siguiente capítulo harás referencia a él para mostrar el valor en la tarjeta.
Si encuentras la palabra hook
En el código fuente del núcleo este callback se llama PublishHook — "hook" ("gancho") significa lo mismo: un punto donde la biblioteca te permite "enganchar" tu función. Los términos son intercambiables; en esta documentación decimos "callback".
Lambda y por qué está "vacía"
La construcción [](JsonObject doc) { ... } se llama lambda — es una función sin nombre, escrita en el lugar de uso, para no separarla y no inventar un nombre.
Los corchetes al principio — "lista de captura": en ellos enumeras las variables locales que la función toma consigo. La regla del núcleo: los corchetes siempre están vacíos ([]) — la lambda no captura nada y no arrastra ningún estado (esto se llama stateless, "sin estado").
La razón es técnica: las lambdas con captura requieren memoria dinámica, y sus asignaciones frecuentes en ESP32 fragmentan el montón y en el peor caso derriban Wi-Fi. Por eso el núcleo solo acepta funciones simples.
Conclusión práctica: todo lo que el callback necesita, guárdalo en variables globales — como nuestra g_vocIndex. Esta regla aplica a todos los callbacks de idryer-core.
El estado del ventilador se publica por la ruta del diccionario — simplemente escribe en el campo del núcleo cuando enciendas/apagues (la lógica en el capítulo 7):
4. Verificación¶
Después de cargar el firmware en el flujo MQTT del dispositivo (o en el registro Serial de publicaciones) la telemetría se ve así:
{
"units": [ { "unitId": "U1", "fanStatus": false, "vocIndex": 103 } ],
"rssi": -52,
"uptime": 120
}
vocIndex — tu propio campo, llegó a la nube junto al fanStatus del diccionario. El portal ya lo recibe y guarda, pero aún no sabe qué hacer con él: muéstrale esto en el siguiente capítulo.
Respira sobre el sensor o acerca un marcador — el índice debe crecer notablemente en segundos.