En invierno, el aire de Coyhaique, Temuco, Osorno o Padre Las Casas puede ser peor que el de ciudades que salen en las noticias por su smog. El problema es que la red SINCA tiene una estación por comuna. cuando hay. y esa estación no mide lo que respiras en tu pasaje, con la estufa a leña del vecino a treinta metros. Un medidor propio resuelve exactamente eso: convierte el humo invisible en un número que puedes mirar, guardar y comparar.
Qué vas a lograr
Vas a armar un medidor portátil de material particulado con un ESP32, un sensor láser Sensirion SPS30 y una pantalla TFT de 240x320. El equipo muestra en pantalla las diez métricas del sensor (masa de MP1,0 / MP2,5 / MP4,0 / MP10 y conteo de partículas en cinco rangos de tamaño), sube todo a una nube por MQTT y te manda un WhatsApp cuando el aire pasa a categoría de alerta. Al terminar vas a saber por qué un sensor láser no se compara con un sensor de polvo de mil pesos, cómo se calcula el índice de calidad del aire con los tramos chilenos en vez de los indios, y por qué un equipo a batería necesita un encendido escalonado para no reiniciarse solo.
Tiempo de armado: unas dos horas si ya tienes el Arduino IDE configurado para ESP32.
Antes de empezar: lo que hay que tener listo
El tutorial original salta directo al circuito y da por hecho el entorno. Si es tu primer proyecto con ESP32, resuelve esto primero y te ahorras la mitad de los dolores de cabeza:
- Soporte de placas ESP32 en el Arduino IDE. En Preferencias, agrega la URL de gestor de tarjetas adicionales de Espressif, y después instala el paquete
esp32desde el Gestor de Tarjetas. Selecciona la placa ESP32 Dev Module. - Driver del puerto serie. Las placas ESP32 económicas suelen traer un conversor USB serie CH340 en vez del CP2102. Si al conectar la placa no aparece ningún puerto COM o tty, el que falta es el driver del CH340, no la placa.
- Las cuatro librerías. Desde el Gestor de Librerías instala
Adafruit GFX Library,Adafruit ILI9341ySensirion UART SPS30. La libreríaCircuitDigestCloudno está en el gestor: se instala descargándola del repositorio del proyecto y usando Programa > Incluir librería > Añadir biblioteca .ZIP. - Una red de 2,4 GHz a mano. Vale la pena verificarlo antes de soldar nada: si tu router entrega una sola SSID combinada de 2,4 y 5 GHz, lo más simple es crear una red de invitados en 2,4 GHz solo para tus proyectos.
- Un multímetro. No es opcional en este proyecto: vas a necesitarlo para ajustar la salida del convertidor boost a 5 V antes de conectarlo al ESP32. Si lo conectas sin medir y el trimpot venía en 12 V, quemas la placa en el primer encendido.
Por qué el material particulado se mide con láser
Los sensores de polvo baratos (el clásico DSM501A, por ejemplo) prenden un LED infrarrojo y miden cuánto se oscurece un fotodiodo cuando pasa polvo: entregan un porcentaje de tiempo con partículas presentes, no una concentración. Sirven para decir "hay polvo", no para decir cuánto.
El SPS30 hace algo distinto. Tiene un ventilador que fuerza un caudal conocido de aire por una cámara donde un diodo láser ilumina un punto único. Cada partícula que cruza el haz dispersa luz, y un fotodetector registra ese pulso. La amplitud del pulso depende del diámetro de la partícula y la cantidad de pulsos por segundo depende de cuántas hay. Con el caudal conocido y esos dos datos, el firmware interno del sensor calcula concentración en masa (microgramos por metro cúbico) y conteo de partículas por centímetro cúbico, separado en rangos de tamaño.
Esa distinción importa más de lo que parece. Dos ambientes pueden marcar el mismo MP2,5 en masa y tener perfiles de partícula completamente distintos: humo de leña da muchísimas partículas ultrafinas de poca masa, mientras que polvo de construcción da pocas partículas grandes y pesadas. Si solo miras la masa, no distingues una cosa de la otra. El conteo por rangos sí lo hace, y por eso vale la pena un sensor que lo entregue.
SPS30, PMS5003 y SDS011 comparados
| Característica | Sensirion SPS30 | Plantower PMS5003 | Nova SDS011 |
|---|---|---|---|
| Masa | MP1,0 / MP2,5 / MP4,0 / MP10 | MP1,0 / MP2,5 / MP10 | Solo MP2,5 y MP10 |
| Conteo de partículas | Sí, 5 rangos (0,5 a 10 µm) | Sí, 6 rangos (0,3 a 10 µm) | No entrega conteo |
| Principio | Dispersión láser | Dispersión láser | Dispersión láser |
| Interfaz | UART o I2C | UART | UART o PWM |
| Dónde luce | Índice completo y dashboards | Proyectos indoor económicos | Registradores simples |
Este proyecto usa el SPS30 porque es el único de los tres que entrega las nueve variables de masa y conteo que necesita la grilla de la pantalla y la lógica de umbrales. Si ya tienes un PMS5003, el proyecto funciona igual: pierdes MP4,0 y cambia la librería, el resto del código se mantiene.
El índice: por qué no sirve copiar el AQI del tutorial original
Acá está el cambio más importante respecto de la versión original de este proyecto, que es india y calcula el índice con los tramos del CPCB. Un número de índice no es una medición: es una escala política que traduce microgramos a categorías, y cada país define su propia tabla. Si publicas "AQI 180" con tramos indios en Chile, un lector chileno va a interpretar mal el dato, porque acá 180 de ICAP significa otra cosa.
Chile usa el ICAP (Índice de Calidad del Aire referido a Partículas) que publica el SINCA del Ministerio del Medio Ambiente. Está construido para que el valor 100 coincida con la norma diaria de MP2,5. 50 µg/m³. y los tramos superiores marcan los episodios críticos que gatillan restricciones reales: Alerta, Preemergencia y Emergencia.
| ICAP | MP2,5 24 h (µg/m³) | Categoría |
|---|---|---|
| 0 - 50 | 0 - 25 | Bueno |
| 51 - 100 | 26 - 50 | Regular |
| 101 - 200 | 51 - 79 | Alerta |
| 201 - 300 | 80 - 109 | Preemergencia |
| 301 - 400 | 110 - 169 | Emergencia |
| 401 - 500 | 170 o más | Emergencia crítica |
Para que el equipo hable en chileno, reemplaza las funciones getSubIndex_PM25() y getSubIndex_PM10() del código original por esta, que usa los tramos del ICAP:
// Sub-indice ICAP para MP2,5 con los tramos que usa el SINCA (MMA, Chile)
// 0-25 -> 0-50 | 26-50 -> 51-100 | 51-79 -> 101-200
// 80-109 -> 201-300 | 110-169 -> 301-400 | >=170 -> 401-500
float calcularICAP_MP25(float pm25) {
if (pm25 <= 0.0f) return 0.0f;
if (pm25 <= 25.0f) return (50.0f / 25.0f) * pm25;
if (pm25 <= 50.0f) return 51.0f + ((100.0f - 51.0f) / (50.0f - 25.0f)) * (pm25 - 25.0f);
if (pm25 <= 79.0f) return 101.0f + ((200.0f - 101.0f) / (79.0f - 50.0f)) * (pm25 - 50.0f);
if (pm25 <= 109.0f) return 201.0f + ((300.0f - 201.0f) / (109.0f - 79.0f)) * (pm25 - 79.0f);
if (pm25 <= 169.0f) return 301.0f + ((400.0f - 301.0f) / (169.0f - 109.0f)) * (pm25 - 109.0f);
return 401.0f + ((500.0f - 401.0f) / 100.0f) * (pm25 - 169.0f);
}
Después cambia la llamada dentro del loop(): donde el código original hace calculateAQI_CPCB(mc2p5, mc10p0), usa calcularICAP_MP25(mc2p5). Con eso el umbral de alerta de 200 que ya trae el firmware pasa a significar algo concreto: 200 de ICAP es el borde entre Alerta y Preemergencia, y ahí el WhatsApp deja de ser un aviso decorativo.
Dos advertencias honestas sobre esto. Primero, el ICAP oficial se calcula sobre promedios de 24 horas, no sobre una lectura instantánea: tu equipo entrega un ICAP "equivalente puntual", que es utilísimo para ver picos (prender la estufa, cocinar, un vecino quemando) pero no es comparable uno a uno con la cifra de la estación. Segundo, si quieres el número oficial, promedia las lecturas de un día completo antes de aplicar la función.
Hardware: la cadena de poder primero, los datos después

La mayoría de los tutoriales parte cableando los datos y deja la alimentación para el final. Acá conviene al revés, porque el 90% de los problemas de este proyecto son de energía.
Cadena de alimentación. La batería 18650 va montada en su porta batería, y de ahí al módulo TP4056: positivo a B+, negativo a B-. La salida OUT+ del TP4056 pasa por el interruptor de palanca y llega al IN+ del convertidor boost; el OUT- del TP4056 va directo al IN- del boost. La salida del boost, ajustada a 5 V, alimenta el pin VIN del ESP32 y su OUT- va a GND.
Fíjate donde queda el interruptor: entre el TP4056 y el boost, no entre el boost y el ESP32. Si lo pones después del boost, el convertidor sigue trabajando en vacío y la batería se descarga igual con el equipo "apagado". Es el error más común del montaje.
Sensor SPS30 (UART). Aliméntalo desde VIN, es decir desde los 5 V del boost, nunca desde los 3,3 V del ESP32. El ventilador del SPS30 pide alrededor de 800 mA en el arranque y el regulador de 3,3 V de una placa ESP32 típica entrega bastante menos que eso: si lo cuelgas ahí, el sensor no parte o arrastra al microcontrolador al reset. Los datos van cruzados al puerto serial 2 del ESP32:
| SPS30 | ESP32 |
|---|---|
| VCC | VIN (5 V) |
| GND | GND |
| RX | GPIO 17 (TX2) |
| TX | GPIO 16 (RX2) |
Pantalla TFT (SPI). VCC y LED a 3,3 V, GND a GND, y los pines de control así:
| TFT | ESP32 |
|---|---|
| CS | GPIO 2 |
| RESET | GPIO 4 |
| DC | GPIO 5 |
| SDI (MOSI) | GPIO 23 |
| SCK | GPIO 18 |
| SDO (MISO) | GPIO 19 |
Por qué el equipo necesita arrancar de a poco
Un 18650 con un boost chico tiene impedancia de salida real. Cuando el ventilador del sensor, el backlight de la pantalla y la radio WiFi arrancan al mismo tiempo, el consumo instantáneo salta por sobre lo que el conversor puede sostener y la tensión cae. El detector de brownout del ESP32 ve esa caída y reinicia el chip; el chip reinicia, vuelve a intentar todo junto, y el equipo queda en un ciclo de reinicios infinito que parece un problema de código pero es puro tema eléctrico.
La solución que usa este firmware es un arranque escalonado de unos doce segundos: dos segundos para que el boost estabilice sus 5 V, cinco para la pantalla, cinco más para que el ventilador del sensor llegue a régimen, y solo entonces se enciende el WiFi, además con la potencia de transmisión bajada a 15 dBm para recortar los peaks de corriente de la radio. El detector de brownout se desactiva a propósito para que no interrumpa esa secuencia.
Firmware: los cinco bloques que hay que entender
El firmware se compila en el Arduino IDE con soporte para ESP32 y necesita las librerías Adafruit_GFX, Adafruit_ILI9341, SensirionUartSps30 y CircuitDigestCloud.
1. Credenciales
#define WIFI_SSID "xxxxx" //your Wi-Fi SSID
#define WIFI_PASS "xxxxx" //your Wi-Fi password
#define DEVICE_ID "xxxxxxxxxx" // Physical Device ID
#define CONNECTION_KEY "xxxxxxxxxx" // Connection Key
#define API_KEY "xxxxxxxxxx" // API Key
// WhatsApp Notification Settings
#define WHATSAPP_PHONE "xxxxxxxxxx" // Linked phone number with country code
Todo lo sensible en un solo lugar: red WiFi, identificadores del dispositivo en la nube y el número de WhatsApp con código de país (para Chile, +56 y luego los nueve digitos). Recuerda que el ESP32 solo se conecta a redes de 2,4 GHz.
2. Grilla de pantalla y umbrales
// --- Layout: 2 columns x 5 rows = 10 boxes, 240x320 Portrait ---
const int boxW = 120; // 2 cols * 120 = 240px width
const int boxH = 64; // 5 rows * 64 = 320px height
const int cols = 2;
const int rows = 5;
struct ParamBox {
const char* label;
const char* unit;
float threshold;
bool isCPCB;
};
ParamBox params[10] = {
{ "PM1.0 MASS", "ug/m3", 30.0, false },
{ "PM2.5 MASS", "ug/m3", 60.0, true },
{ "PM4.0 MASS", "ug/m3", 80.0, false },
{ "PM10 MASS", "ug/m3", 100.0, true },
{ "PM0.5 NUM", "#/cm3", 1000.0,false },
{ "PM1.0 NUM", "#/cm3", 800.0, false },
{ "PM2.5 NUM", "#/cm3", 700.0, false },
{ "PM4.0 NUM", "#/cm3", 500.0, false },
{ "PM10 NUM", "#/cm3", 400.0, false },
{ "CPCB AQI", "", 100.0, true }
};
Acá se define la interfaz: dos columnas por cinco filas de casillas de 120x64 píxeles, que cubren exactamente los 240x320 de la pantalla en vertical. Cada casilla trae su etiqueta, su unidad y su umbral. Si vas a usar el ICAP chileno, este es el lugar donde ajustar el umbral de la última casilla y su etiqueta.
3. Secuencia de arranque
void setup() {
// 1. Disable brownout detector to prevent rebooting on battery voltage dips
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
delay(2000); // Initial 2s delay for power supply voltage stabilization
// --- Step 1: Initialize Display ---
Serial.println("[Boot Step 1/3] Starting TFT Display...");
tft.begin();
tft.setRotation(0); // Portrait mode, 240x320
tft.fillScreen(ILI9341_BLACK);
drawDashboardGrid();
Serial.println("Waiting 5 seconds for display power stabilization...");
delay(5000); // 5-second pause to isolate display power draw
// --- Step 2: Initialize SPS30 Sensor ---
Serial.println("[Boot Step 2/3] Starting SPS30 Sensor...");
SENSOR_SERIAL_INTERFACE.begin(115200, SERIAL_8N1, 16, 17);
sensor.begin(SENSOR_SERIAL_INTERFACE);
sensor.stopMeasurement();
int8_t serialNumber[32] = {0};
error = sensor.readSerialNumber(serialNumber, 32);
if (error != NO_ERROR) {
Serial.print("Error reading serial number: ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
}
error = sensor.startMeasurement(SPS30_OUTPUT_FORMAT_OUTPUT_FORMAT_FLOAT);
if (error != NO_ERROR) {
Serial.print("Error starting measurement: ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
} else {
Serial.println("SPS30 Measurement started successfully.");
}
Serial.println("Waiting 5 seconds for SPS30 internal fan to stabilize...");
delay(5000); // 5-second pause to let fan motor reach full operational speed
// --- Step 3: Lower Wi-Fi TX Power & Connect to Cloud ---
Serial.println("[Boot Step 3/3] Turning on Wi-Fi & Connecting to Cloud...");
// Lower Wi-Fi transmit power to prevent voltage sags on battery power
WiFi.setTxPower(WIFI_POWER_15dBm);
if (!CDcloud.begin(WIFI_SSID, WIFI_PASS, DEVICE_ID, CONNECTION_KEY, API_KEY)) {
Serial.println("CDcloud begin() failed — check credentials. Rebooting...");
delay(2000);
ESP.restart(); // Reset system if initial cloud connection fails
} else {
Serial.println("CDcloud initialized successfully.");
}
Serial.println("Boot sequence complete. Entering main loop.");
}
Es la implementación del arranque escalonado que se explicó arriba. Nota el orden: pantalla, sensor, y WiFi al final. Si algo falla en la conexión inicial a la nube, el código reinicia en vez de quedarse colgado.
4. Reconexión a prueba de fallas
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi Disconnected!");
Serial.println("Shutting down sensor and restarting system to execute staged soft-start...");
// Stop SPS30 fan before resetting system
sensor.stopMeasurement();
// 2-second delay to settle power grid before rebooting
delay(2000);
// Trigger full ESP32 microcontroller software reboot
ESP.restart();
}
Si se cae la red, el equipo apaga el ventilador del sensor antes de reiniciar. Eso ahorra batería y le evita al motor un ciclo de arranque innecesario, que es la pieza que más se desgasta del SPS30.
5. Publicación MQTT y alerta
// 4. Check CPCB AQI Threshold for WhatsApp Alert (Poor Category > 200)
if (cpcbAqi > AQI_POOR_THRESHOLD) {
if (!whatsappAlertTriggered) {
Serial.println("CPCB AQI in POOR Category (>200)! Triggering WhatsApp Alert...");
sendWhatsAppAlert(cpcbAqi);
whatsappAlertTriggered = true; // Lock alert until AQI recovers below 200
}
} else {
// Reset alert state when AQI drops back to Safe/Moderate range (<= 200)
whatsappAlertTriggered = false;
}
El lazo principal corre cada cinco segundos: lee las diez variables, refresca la pantalla y publica a la nube en dos lotes de cinco valores separados por 200 ms. Ese detalle no es capricho: mandar diez valores en un solo paquete hace que el broker descarte parte de la carga. Y la bandera whatsappAlertTriggered funciona como traba, para que una sola vez que se cruce el umbral no se convierta en veinte mensajes seguidos; se libera cuando el índice vuelve a bajar.
Nube y alertas paso a paso
El proyecto original usa CircuitDigest Cloud, que es gratis y tiene la integración con WhatsApp lista.
Paso 1: cuenta. Regístrate o entra con tu correo en la plataforma.

Paso 2: dispositivo. En el panel, entra a la sección de dispositivos y crea uno nuevo con el nombre que quieras (por ejemplo Monitor_Aire_Casa). Guarda el ID, la clave de conexión y la API key, porque son las que van en el bloque de credenciales del código.

Paso 3: variables. Crea diez variables, una por métrica, en modo bidireccional. La clave de cada una es la que el firmware ya trae definida: analog-input-1 para masa de MP1,0 hasta analog-input-10 para el índice.

Las unidades: µg/m³ para las cuatro variables de masa, partículas/cm³ para las cinco de conteo, y sin unidad para el índice.

Paso 4: widgets. En el dashboard agrega un widget de tipo valor por cada variable, asociando cada uno a su clave y unidad.

Paso 5: WhatsApp. En la portada de la plataforma, entra a la sección de notificaciones de WhatsApp, vincula tu número y confirma el código que llega por mensaje. Ese número es el que va en WHATSAPP_PHONE, con +56 adelante.

Resultado y cómo validar que mide bien

Con el código cargado y el equipo prendido, la pantalla muestra las diez métricas actualizándose cada cinco segundos, marcando cuáles están sobre umbral, y el dashboard en la nube replica todo con historial.
Antes de creerle al número, hazle tres pruebas:
- Cero relativo. Deja el equipo media hora en una pieza ventilada y sin fuentes de combustión. Debería marcar valores bajos y estables. Si marca decenas de µg/m³ en esas condiciones, revisa que la entrada de aire del sensor no esté obstruida.
- Respuesta a un estímulo. Prende un fósforo a un metro. El MP2,5 tiene que subir en segundos y bajar después. Si no reacciona, el problema es el sensor o su alimentación, no el código.
- Contraste con la estación. Compara un promedio de varias horas con la estación SINCA más cercana. No van a coincidir exactamente. tu sensor mide tu casa. pero el orden de magnitud y la tendencia diaria deberían parecerse.
Cómo leer las diez cifras de la pantalla
Tener diez números en pantalla no sirve de nada si no sabes cuál mirar. Esta es la lectura práctica de cada grupo:
- MP2,5 en masa es la cifra que importa para salud y la única que se compara con la norma. Son las partículas de menos de 2,5 micrómetros, lo bastante chicas para pasar los bronquios y llegar al alvéolo. La norma chilena diaria es de 50 µg/m³ y la anual de 20 µg/m³; la referencia de la OMS es bastante más estricta, 15 µg/m³ diarios.
- MP10 en masa es polvo más grueso: tierra, construcción, camino sin pavimentar. Se queda en la vía aérea superior. Sube fuerte en verano y con viento, al revés que el MP2,5, que dispara en invierno con la leña.
- MP1,0 y MP4,0 no tienen norma asociada, pero el contraste entre ellas y el MP2,5 te dice de qué tamaño viene la carga. Si MP1,0 está casi igual que MP2,5, casi toda la masa es ultrafina y el origen es combustión.
- Los cinco conteos de partículas son el diagnóstico de origen. Miles de partículas de 0,5 µm con poca masa apuntan a humo; cientos de partículas de 10 µm con harta masa apuntan a polvo mecánico. La masa te dice cuánto; el conteo te dice de qué.
Una regla mental rápida para el MP2,5: bajo 25 µg/m³ el aire está bien; entre 25 y 50 es aceptable pero ya notorio; sobre 80 estás en territorio de alerta ambiental y conviene cerrar ventanas y no hacer ejercicio afuera; sobre 170 es lo que el SINCA llama emergencia.
Encapsulado y flujo de aire
El sensor mide el aire que le entra por la boca de aspiración, así que la caja no es decoración, es parte del instrumento. Tres reglas que el original no menciona:
- Dos aperturas, no una. El SPS30 necesita entrada y salida libres; si tapas la salida, el ventilador recircula su propio aire y las lecturas se aplanan.
- Nada de filtros ni mallas finas delante de la entrada. Una tela mosquitera filtra justamente lo que quieres medir.
- Separa el sensor del calor. El regulador del ESP32, el backlight de la pantalla y la 18650 en carga entibian el aire dentro de la caja. Aire más caliente significa humedad relativa más baja en el punto de medición, y eso corre las lecturas hacia abajo. Deja el sensor lo más cerca posible de la entrada de aire y con el resto de la electrónica atrás.
Y una recomendación de ubicación: el sensor va a altura de respiración, lejos de la cocina y de cualquier corriente directa de aire caliente. Un SPS30 sobre el mueble de la cocina no mide la calidad del aire de la casa, mide la fritura.
Errores comunes
| Síntoma | Causa probable y arreglo |
|---|---|
| El sensor no parte | VCC del SPS30 conectado a 3,3 V. Debe ir a VIN o a 5 V. |
| El equipo se reinicia solo en loop | Batería o boost insuficientes para el peak del ventilador. Usa una 18650 real, no una LiPo de 800 mAh. |
| No se conecta al WiFi | La red está en 5 GHz. El ESP32 solo soporta 2,4 GHz; separa la banda o crea una SSID de 2,4. |
| La batería se descarga apagado | El interruptor quedó después del boost. Muévelo entre OUT+ del TP4056 e IN+ del boost. |
| La pantalla parpadea | Alimentación sucia en la línea de 3,3 V. Verifica la salida del regulador del ESP32 y acorta los cables del backlight. |
| Faltan valores en el dashboard | Los diez valores se publicaron en un solo lote. Mantén los dos lotes con los 200 ms de separación. |
| Las lecturas suben con la humedad | El MP2,5 es higroscópico: sobre 70% de humedad relativa las partículas crecen y el sensor mide más masa. No es una falla, es física. |
Variantes y mejoras
- Registro local en microSD. Agrega un módulo microSD por SPI y guarda cada lectura con su timestamp. Sirve para tener respaldo cuando se cae la red y para armar después los promedios de 24 horas que exige el ICAP oficial. Ojo con los pines: el SPI ya lo ocupa la pantalla, así que comparte el bus y usa un CS distinto.
- Semanas de autonomía con deep sleep. Tal como está, el equipo mide continuo y la 18650 dura horas. Si te basta una muestra cada quince minutos, prende el ventilador treinta segundos, publica y manda el ESP32 a
esp_deep_sleep_start(). El consumo promedio baja más de un orden de magnitud y el mismo pack dura semanas. - Corregir por humedad con un BME280. Suma un sensor de temperatura, humedad y presión, y registra la humedad relativa junto a cada lectura de MP2,5. Con esos pares de datos puedes aplicar una corrección higroscópica y comparar días húmedos con días secos sin engañarte.
- Integración con Home Assistant. En vez de la nube externa, publica a un broker MQTT local (Mosquitto). Home Assistant descubre las diez variables como sensores y puedes gatillar automatizaciones reales: encender el purificador o cerrar la ventilación cuando el ICAP pasa a Alerta.
- Red de varios nodos en la misma cuadra. Como cada equipo publica con su propio ID de dispositivo, puedes armar tres o cuatro y comparar patio, dormitorio y vereda al mismo tiempo. Ahí aparece lo que ninguna estación oficial te va a mostrar: cuánto del humo que respiras adentro entra desde afuera y cuánto lo generas en tu propia casa.
Personalización para Chile
Casi todo el proyecto se consigue en MechatronicStore:
- Placa ESP32 WROOM-32 (Tipo C) (SKU X2-10V2). $7.990
- Pantalla TFT LCD 2.0" SPI 240x320 (SKU D-221). $20.990. Es la equivalencia más directa a la pantalla del tutorial: misma resolución de 240x320 por SPI, pero con controlador ST7789 en vez de ILI9341. En la práctica cambias
Adafruit_ILI9341porAdafruit_ST7789y la grilla de diez casillas queda idéntica, solo más compacta (2,0" en vez de 2,4"). - Placa ESP32 WROOM con pantalla táctil 2.4" 240x320 (SKU A-301). $21.990. La alternativa cómoda: trae el ESP32 y una pantalla de 2,4" 240x320 en la misma placa, del mismo tamaño que la del original. Reemplaza dos ítems del BOM y elimina todo el cableado SPI; a cambio, los GPIO de la pantalla vienen fijos y hay que ajustar los
#definede pines. - Módulo de carga TP4056 Tipo C (SKU GQ2-12). $1.990
- Convertidor boost step up XL6009 (SKU B-209). $1.990. El original usa un módulo V599 chico; el XL6009 es ajustable y tiene más holgura de corriente para el peak del ventilador.
- Batería 18650 3.7V litio (SKU GT2-10). $3.390
- Porta batería 18650 x1 (SKU L-325). $1.390
- Interruptor de palanca ON/OFF 2 pines MTS-101 (SKU GD2-6). $890. El tutorial original describe un interruptor de tres terminales, pero para cortar la línea entre el TP4056 y el boost basta uno de dos pines.
- Cables macho hembra 20cm (SKU C-412). $1.990
- Cable USB Tipo C a USB Tipo A 1mt (SKU B-101). $2.190
Total de lo que se consigue local: alrededor de $42.800 con la pantalla separada.
Lo que hay que importar: el Sensirion SPS30 no está en el catálogo, y acá preferimos decirlo en vez de ofrecer un reemplazo que no cumple. El sensor de polvo DSM501A que sí vendemos entrega una señal PWM de "hay polvo o no", sin masa por rango de tamaño ni conteo de partículas: no puede alimentar las diez variables de este proyecto. Si tu prioridad es medir MP2,5 con un valor confiable, importa el SPS30 (ronda los $50.000 a $60.000 CLP puesto en Chile) y usa el resto del BOM local. Si en cambio quieres partir aprendiendo el concepto con lo que hay a mano, el DSM501A sirve para una versión reducida de una sola variable, pero no es el mismo proyecto.
Presupuesto realista completo: alrededor de $98.000 CLP con el sensor importado.
Recursos
- Tutorial original: DIY ESP32 Air Quality Monitor Using SPS30 Sensor & AI Cloud Alerts
- Repositorio GitHub: mshaheen2004/air quality-monitor
- Plataforma de nube: CircuitDigest Cloud
- Datos oficiales de calidad del aire en Chile: SINCA - Ministerio del Medio Ambiente
Versión chilena inspirada en el proyecto de Circuit Digest, con el índice recalculado a la escala ICAP del SINCA y componentes en stock local en MechatronicStore.













