Automatización IoT

Construye un monitor de calidad del aire interior con sensores de CO2 y PM2.5 y MQTT

Conecta un sensor de CO2 SCD41 y un sensor de partículas PMS5003 a un ESP32, publica las lecturas como JSON por MQTT y configura alertas de ventilación cuando el aire se vicia o se llena de polvo.

Pequeño monitor de calidad del aire interior con sensores de CO2 y PM2.5 sobre una placa ESP32 que envía datos por Wi-Fi
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

Un monitor de calidad del aire interior que mide el CO2 y las partículas finas te indica cuándo una sala necesita aire fresco o filtración mucho antes de que la gente se sienta cansada o empiece a toser. En este tutorial construirás uno con un ESP32, un sensor de CO2 Sensirion SCD41 y un sensor de partículas Plantower PMS5003, publicarás las lecturas como JSON por MQTT y configurarás alertas de ventilación en un broker de ConnectMQ.

Qué miden los sensores

  • El CO2 (ppm) es el mejor indicador de la ventilación en salas ocupadas. El aire exterior ronda los 400 a 450 ppm. Las recomendaciones de ventilación de edificios suelen usar valores de alrededor de 800 a 1.000 ppm como señal de que el aporte de aire fresco es insuficiente para el número de personas en la sala. El SCD41 es un verdadero sensor NDIR fotoacústico, no una estimación a partir de lecturas de COV.
  • El PM2.5 (microgramos por metro cúbico) mide las partículas finas procedentes de cocinar, velas, la contaminación exterior o el polvo de obra. El PMS5003 usa dispersión láser e informa de PM1.0, PM2.5 y PM10.
  • La temperatura y la humedad vienen incluidas con el SCD41 y aportan contexto sobre el confort.

La detección de COV es una ampliación útil. Un Sensirion SGP41 o un Bosch BME680 añade un índice de COV que reacciona a los productos de limpieza y a las emisiones de los materiales. Se ha dejado fuera del montaje principal para mantener el código breve.

Componentes y cableado

Componente Pin Pin del ESP32 Notas
SCD41 VDD 3V3 Las placas de conexión suelen incluir resistencias pull-up
SCD41 GND GND
SCD41 SDA GPIO 21 Datos I2C
SCD41 SCL GPIO 22 Reloj I2C
PMS5003 VCC 5V (VIN) El ventilador necesita 5 V
PMS5003 GND GND
PMS5003 TX GPIO 16 (RX2) El nivel lógico de datos del sensor es de 3,3 V
PMS5003 RX GPIO 17 (TX2) Opcional, solo para comandos

Usa el Arduino IDE 2.x con el paquete de placas esp32 (core 3.x) e instala Sensirion I2C SCD4x (versión 1.x) y PubSubClient desde el gestor de librerías. La trama del PMS5003 se analiza directamente en el sketch, así que no hace falta ninguna librería adicional.

Coloca el monitor a la altura de la respiración, lejos de ventanas, puertas y difusores de impulsión, y mantén despejada la entrada de aire del PMS5003. Un sensor junto a una ventana abierta mide el exterior, no la sala.

El sketch completo

#include <WiFi.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <SensirionI2cScd4x.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";

// Broker público de pruebas. En producción usa 8883 + WiFiClientSecure y las credenciales del portal.
const char* BROKER = "broker.connectmq.com";
const int   PORT   = 1883;
const char* TOPIC  = "connectmq/demo/iaq-3hv9/office-201/air";

const unsigned long PUBLISH_MS = 30000;

WiFiClient net;
PubSubClient mqtt(net);
SensirionI2cScd4x scd4x;
HardwareSerial pmsSerial(2);

uint16_t co2 = 0;
float tempC = NAN, rh = NAN;
uint16_t pm1 = 0, pm25 = 0, pm10 = 0;
bool pmValid = false;
unsigned long lastPublish = 0;

// Lee una trama de 32 bytes del PMS5003: 0x42 0x4D, longitud, 13 palabras de datos, suma de comprobación
bool readPms() {
  static uint8_t buf[32];
  while (pmsSerial.available() >= 32) {
    if (pmsSerial.peek() != 0x42) { pmsSerial.read(); continue; }
    pmsSerial.readBytes(buf, 32);
    if (buf[1] != 0x4D) continue;
    uint16_t sum = 0;
    for (int i = 0; i < 30; i++) sum += buf[i];
    uint16_t check = (buf[30] << 8) | buf[31];
    if (sum != check) continue;
    // valores de "entorno atmosférico"
    pm1  = (buf[10] << 8) | buf[11];
    pm25 = (buf[12] << 8) | buf[13];
    pm10 = (buf[14] << 8) | buf[15];
    return true;
  }
  return false;
}

void connectWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(250);
}

void connectMqtt() {
  while (!mqtt.connected()) {
    String id = "iaq-" + String((uint32_t)ESP.getEfuseMac(), HEX);
    if (!mqtt.connect(id.c_str())) delay(3000);
  }
}

void setup() {
  Serial.begin(115200);
  Wire.begin(21, 22);
  pmsSerial.begin(9600, SERIAL_8N1, 16, 17);

  scd4x.begin(Wire, SCD41_I2C_ADDR_62);
  scd4x.stopPeriodicMeasurement();      // por si seguía midiendo tras un reinicio por software
  delay(500);
  if (scd4x.startPeriodicMeasurement() != 0) {
    Serial.println("SCD41 not found, check wiring");
  }

  connectWifi();
  mqtt.setServer(BROKER, PORT);
  mqtt.setBufferSize(512);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  if (readPms()) pmValid = true;

  bool ready = false;
  if (scd4x.getDataReadyStatus(ready) == 0 && ready) {
    uint16_t c; float t, h;
    if (scd4x.readMeasurement(c, t, h) == 0 && c != 0) {
      co2 = c; tempC = t; rh = h;
    }
  }

  if (millis() - lastPublish >= PUBLISH_MS && co2 > 0) {
    lastPublish = millis();
    char msg[256];
    snprintf(msg, sizeof(msg),
      "{\"co2_ppm\":%u,\"temp_c\":%.1f,\"rh\":%.1f,\"pm1\":%u,\"pm25\":%u,\"pm10\":%u,\"pm_valid\":%s,\"rssi\":%d}",
      co2, tempC, rh, pm1, pm25, pm10, pmValid ? "true" : "false", WiFi.RSSI());
    mqtt.publish(TOPIC, msg);
    Serial.println(msg);
  }
}

En modo periódico, el SCD41 produce una medición nueva cada cinco segundos, y el PMS5003 envía tramas aproximadamente una vez por segundo. El sketch conserva los últimos valores y publica cada 30 segundos, más que suficiente para la calidad del aire de una sala.

Observa cómo llegan los datos

Suscríbete desde un terminal:

mosquitto_sub -h broker.connectmq.com -p 1883 -i iaq-view-3hv9 -t "connectmq/demo/iaq-3hv9/#" -v

Deberías ver mensajes como:

connectmq/demo/iaq-3hv9/office-201/air {"co2_ppm":742,"temp_c":23.4,"rh":41.8,"pm1":3,"pm25":6,"pm10":7,"pm_valid":true,"rssi":-58}

Respira suavemente cerca del SCD41 y el valor de CO2 subirá en menos de un minuto. Enciende una cerilla cerca y apágala soplando, y el PM2.5 se disparará. Ambas son comprobaciones rápidas de que los sensores y el analizador funcionan. El servidor MQTT público gratuito es ideal para esta fase de pruebas porque no necesitas cuenta, pero no es para datos de producción.

Pasar a un broker privado con TLS

Los datos del aire de una sala revelan cuándo hay personas presentes, así que los monitores de producción deben usar un broker privado. Crea un broker en el portal de ConnectMQ y cambia el código de conexión:

#include <WiFiClientSecure.h>

WiFiClientSecure net;              // sustituye a WiFiClient
const int PORT = 8883;

// en setup(), antes de mqtt.setServer():
// Carga el certificado ISRG Root X1 (Let's Encrypt) para una validación completa:
// net.setCACert(ISRG_ROOT_X1_PEM);

// en connectMqtt():
// mqtt.connect("YOUR_CLIENT_ID", "YOUR_USERNAME", "YOUR_PASSWORD");

Usa el client ID exactamente como se emitió, porque ConnectMQ vincula los client ID a las credenciales. Nuestro tutorial de ESP32 con MQTT explica con más detalle la gestión de certificados en el ESP32.

Alertas de ventilación

Configura en el portal de ConnectMQ reglas de alerta por umbral sobre los campos co2_ppm y pm25. Puntos de partida sugeridos, que debes adaptar a las recomendaciones locales y al uso de la sala:

Nivel CO2 PM2.5 Acción sugerida
Bueno por debajo de 800 ppm por debajo de 12 No hay que hacer nada
Moderado de 800 a 1.200 ppm de 12 a 35 Aumentar la ventilación, revisar el filtro
Malo por encima de 1.200 ppm por encima de 35 Abrir ventanas o reforzar la ventilación, localizar la fuente de partículas

Las reglas se disparan con cada valor que cruza el umbral, y el registro de alertas conserva el historial para que veas qué salas suelen estar cargadas a determinadas horas. Para accionar un ventilador o una compuerta automáticamente, suscríbete al topic del aire desde un flujo de Node-RED o una automatización de Home Assistant y conmuta el actuador mediante su propio topic de órdenes MQTT. La integración MQTT de Home Assistant hace que el monitor aparezca como entidades de sensor con unas pocas líneas de YAML.

Interpretar los datos a lo largo del tiempo

Las lecturas aisladas son menos informativas que los patrones diarios. En una sala de reuniones típica, el CO2 sube de forma constante durante cada reunión y vuelve a bajar cuando la sala se vacía; la velocidad a la que baja indica lo bien ventilada que está. Una sala que sigue por encima de 1.000 ppm una hora después de que todos se hayan ido tiene un problema de ventilación, no de ocupación. El PM2.5, en cambio, suele mantenerse bajo en interiores y muestra picos breves por cocinar, imprimir, limpiar o por sucesos exteriores como las horas punta de tráfico o el humo.

Para conservar este histórico, ejecuta un suscriptor que escriba cada mensaje en una base de datos de series temporales, o deja que Telegraf se suscriba a los topics del aire. Compara las salas entre sí y fíjate en la peor hora de cada día en lugar de en la media, porque las medias ocultan los momentos en que la gente percibe realmente el aire viciado. Compartir un sencillo gráfico semanal con el equipo de mantenimiento suele bastar para conseguir que se cambie un horario de ventilación.

Si tienes varios monitores, nombra cada topic según la sala y no según el dispositivo, de modo que una placa sustituida publique en el mismo lugar y el histórico se mantenga continuo.

Consejos de calibración y precisión

  1. Autocalibración del CO2. El SCD41 tiene activada por defecto la autocalibración automática, que asume que el sensor ve con regularidad aire fresco con niveles exteriores. En salas que nunca se ventilan por completo, desactívala y realiza en su lugar una recalibración forzada en el exterior.
  2. Calentamiento. Ignora los primeros minutos de lecturas tras el encendido.
  3. Autocalentamiento. Mantén el ESP32 y cualquier regulador de tensión lejos del SCD41, o la lectura de temperatura saldrá alta.
  4. Vida útil del sensor de partículas. El ventilador y el láser del PMS5003 se desgastan. Para despliegues largos, ponlo en reposo entre lecturas mediante su pin SET.
  5. Humedad y PM. Los sensores ópticos de partículas sobrestiman en aire muy húmedo porque las gotas de agua dispersan la luz. Toma con precaución las lecturas de PM por encima de un 80 por ciento de humedad relativa, aproximadamente.

Escalar a todo un edificio

Un monitor por sala de reuniones o zona diáfana es una buena densidad. Cada dispositivo necesita sus propias credenciales y un topic dentro de la jerarquía del edificio, por ejemplo acme/ist01/hq/f03/meeting-301/air. Un piloto de una docena de monitores más un panel cabe en las 25 conexiones del plan Serverless gratuito. Combinar la calidad del aire con sensores de ocupación de oficinas y monitorización HVAC muestra si el aire viciado se debe a demasiadas personas o a una ventilación insuficiente. En edificios inteligentes, de una oficina en Bogotá a un campus en Barcelona, esos mismos topics pueden colorear cada sala en un gemelo digital 3D del edificio. Un broker MQTT gestionado elimina la necesidad de ejecutar cualquier servidor en el edificio, y la guía de automatización IoT enumera más proyectos que puedes construir sobre el mismo broker.

Empieza con ConnectMQ

Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito. Graba el sketch con tus credenciales, abre el explorador de topics en vivo y configura tu primera alerta de CO2. ConnectMQ se encarga de TLS, la autenticación y la disponibilidad.

Preguntas frecuentes

¿Qué nivel de CO2 indica que una sala necesita más ventilación?

Muchas recomendaciones de ventilación consideran que unos valores sostenidos por encima de 1.000 ppm aproximadamente indican un aporte de aire fresco insuficiente para la ocupación. Sigue las recomendaciones locales y observa las tendencias en lugar de lecturas aisladas.

¿Puede un sensor de COV sustituir a un sensor de CO2 real?

No. Los sensores que informan de "CO2 equivalente" lo estiman a partir de los COV y pueden desviarse mucho. Usa un sensor NDIR o fotoacústico como el SCD41 para medir el CO2 real.

¿Con qué frecuencia debe publicar el monitor de calidad del aire interior?

Cada 30 a 60 segundos es suficiente. La calidad del aire cambia a lo largo de minutos, y publicar con menos frecuencia ahorra energía y tráfico en el broker.

¿Necesita 5 V el PMS5003?

Su ventilador necesita una alimentación de 5 V, pero su salida serie usa lógica de 3,3 V, así que el pin TX puede conectarse directamente a una entrada del ESP32.

Artículos relacionados

Guía para configurar Zigbee2MQTT: del coordinador al panel

Instala Zigbee2MQTT con Docker, configura el coordinador y la conexión MQTT, empareja y renombra dispositivos, contrólalos por MQTT y consulta sus estados en un panel, probando primero en un servidor MQTT público gratuito.

8 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.