Objetivo y caso de uso
Qué construirás: Un registrador de calidad del aire de bajo consumo utilizando Arduino Nano 33 IoT, un display e-Paper de Waveshare y un sensor BME680.
Para qué sirve
- Monitoreo continuo de la calidad del aire en entornos urbanos.
- Visualización de datos de calidad del aire en tiempo real en un display e-Paper.
- Registro de datos históricos para análisis de tendencias de contaminación.
- Integración con sistemas de alerta mediante MQTT para notificaciones en tiempo real.
Resultado esperado
- Datos de calidad del aire actualizados cada 10 segundos con latencia mínima.
- Visualización de niveles de CO2, temperatura y humedad en el display e-Paper.
- Envío de datos a un servidor MQTT con una frecuencia de 1 paquete cada 30 segundos.
- Capacidad de operar con una duración de batería de más de 6 meses en modo de bajo consumo.
Público objetivo: Ingenieros y entusiastas de IoT; Nivel: Avanzado
Arquitectura/flujo: Arduino Nano 33 IoT -> BME680 -> e-Paper -> MQTT.
Nivel: Avanzado
Prerrequisitos
Sistema operativo y herramientas
- Sistemas operativos soportados:
- Linux: Ubuntu 22.04 LTS x86_64
- macOS: 13 Ventura o 14 Sonoma (Intel/Apple Silicon)
-
Windows 11 Pro/Enterprise (x64)
-
Toolchain exacta:
- Arduino CLI 0.35.3
- Core de placa: Arduino SAMD Boards 1.8.14
- Bibliotecas Arduino (vía Library Manager):
- GxEPD2 1.5.9
- Adafruit GFX Library 1.11.9
- Adafruit BME680 Library 2.0.3
- Adafruit Unified Sensor 1.1.14
- Adafruit BusIO 1.14.1
- ArduinoLowPower 1.2.2
- FlashStorage_SAMD 1.3.2
Notas:
– El Arduino Nano 33 IoT usa USB nativo. No requiere drivers en macOS ni Linux. En Windows 10/11 se instala como “USB Serial Device (COMx)”; no se necesitan drivers externos.
– Se usa Arduino CLI (no el IDE GUI) para todo el flujo: instalación del core, dependencias, compilación y subida.
Verificación del hardware y entorno
- Confirmar el puerto serie:
- Linux/macOS: típico /dev/ttyACM0 o /dev/tty.usbmodemXXXX
- Windows: COM3, COM4, etc. (ver en “Administrador de dispositivos”)
- Conexión a Internet para descargar cores y bibliotecas.
- Cable micro‑USB de datos (no solo carga).
Materiales
- 1x Arduino Nano 33 IoT (modelo exacto)
- 1x Módulo Waveshare 2.9″ e‑Paper monocromo con controlador SSD1680 (modelo exacto; versión b/w V2 con SSD1680)
- 1x Sensor ambiental BME680 (I2C)
- Cables Dupont macho‑hembra
- Protoboard (opcional, para ordenar cableado)
Observación sobre alimentación y niveles:
– El Nano 33 IoT funciona a 3.3 V lógicos, compatibles con la pantalla e‑Paper SSD1680 y con el BME680. No usar 5 V en señales.
Preparación y conexión
Disposición de pines y cableado
Para la pantalla Waveshare 2.9″ e‑Paper (SSD1680) se usará SPI. El módulo típico expone: VCC, GND, DIN (MOSI), CLK (SCK), CS, DC, RST, BUSY. No se usa MISO en el panel b/w.
Para el BME680 se usará I2C con alimentación a 3.3 V. La mayoría de breakout boards vienen con regulador y pull‑ups integradas; verificar el serigrafiado de su módulo.
Tabla de conexiones (Nano 33 IoT ↔ periféricos):
| Función | Nano 33 IoT | e‑Paper (SSD1680) | BME680 (I2C) |
|---|---|---|---|
| Alimentación | 3V3 | VCC | VIN/3V3 |
| Tierra | GND | GND | GND |
| SPI MOSI | D11 (MOSI) | DIN | — |
| SPI SCK | D13 (SCK) | CLK | — |
| SPI CS panel | D10 | CS | — |
| SPI DC (data/command) | D9 | DC | — |
| Reset panel | D8 | RST | — |
| Busy panel | D7 | BUSY | — |
| I2C SDA | SDA | — | SDA |
| I2C SCL | SCL | — | SCL |
Indicaciones:
– Conecte el BME680 a los pines etiquetados “SDA” y “SCL” del Nano 33 IoT (no confundir con A4/A5 propios de placas AVR).
– La pantalla e‑Paper debe alimentarse con 3.3 V. No usar 5 V en VCC ni en señales.
– Mantenga cortos los cables SPI de la e‑Paper para minimizar ruido y artefactos de actualización.
Preparación del entorno de compilación
1) Descargar e instalar Arduino CLI 0.35.3:
– Linux:
– curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
– Verifique versión: arduino-cli version (debe mostrar 0.35.3)
– macOS:
– brew update && brew install arduino-cli
– Verifique: arduino-cli version
– Windows:
– Descargue el binario .exe de Arduino CLI 0.35.3 y añádalo al PATH.
– Verifique: arduino-cli version
2) Instalar el core de la placa SAMD (exacto):
– arduino-cli core update-index
– arduino-cli core install arduino:samd@1.8.14
3) Instalar bibliotecas exactas:
– arduino-cli lib install «GxEPD2@1.5.9»
– arduino-cli lib install «Adafruit GFX Library@1.11.9»
– arduino-cli lib install «Adafruit BME680 Library@2.0.3»
– arduino-cli lib install «Adafruit Unified Sensor@1.1.14»
– arduino-cli lib install «Adafruit BusIO@1.14.1»
– arduino-cli lib install «ArduinoLowPower@1.2.2»
– arduino-cli lib install «FlashStorage_SAMD@1.3.2»
4) Verificar que el FQBN esté disponible:
– arduino-cli board listall | grep -i «Nano 33 IoT»
– Debe listar: arduino:samd:nano_33_iot
Código completo
A continuación se entrega el sketch “epaper-air-quality-logger.ino”. El objetivo:
– Leer cada minuto el BME680 (T, H, P, gas).
– Calibrar un valor de baseline de gas durante los primeros 5 minutos.
– Calcular un índice simple de calidad de aire (IAQ%) basado en gas y humedad.
– Mostrar en e‑Paper: valores actuales y una minigráfica histórica.
– Registrar datos en memoria flash del SAMD21 con un buffer circular persistente.
– Permitir volcado de registros por Serial en CSV cuando se envía el comando “DUMP”.
Notas importantes para el display:
– Para Waveshare 2.9″ b/w V2 (SSD1680) usar la clase GxEPD2_290_T5 (128×296).
– Configuramos pines CS/DC/RST/BUSY según la tabla de conexión.
// epaper-air-quality-logger.ino
// Dispositivo: Arduino Nano 33 IoT + Waveshare 2.9" e-Paper (SSD1680) + BME680
// Toolchain: Arduino CLI 0.35.3, Core SAMD 1.8.14
// Bibliotecas: ver versiones en la sección de prerrequisitos
#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME680.h>
#include <Adafruit_GFX.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeSans9pt7b.h>
#include <ArduinoLowPower.h>
#include <FlashStorage_SAMD.h>
// Pines e-Paper
#define EPD_CS 10
#define EPD_DC 9
#define EPD_RST 8
#define EPD_BUSY 7
// Instancia display: Waveshare 2.9" V2 (SSD1680) -> GxEPD2_290_T5 128x296
// Nota: GxEPD2 usa Adafruit_GFX como backend gráfico
#include <GxEPD2_3C.h> // no se usará color; se incluye por compatibilidad
#include <GxEPD2_290.h> // headers base
// Para SSD1680 (GDEW029T5 o equivalente):
class GxEPD2_290_T5; // forward decl. (incluido en librería)
GxEPD2_BW<GxEPD2_290_T5, GxEPD2_290_T5::HEIGHT> display(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY);
// BME680 por I2C
Adafruit_BME680 bme(&Wire);
// Configuración de logging y almacenamiento persistente
#define LOG_CAPACITY 288 // 288 muestras ~ 24 h si muestreamos cada 5 min (ajustable)
#define SAMPLE_PERIOD_MS (60UL * 1000UL) // 60 s
#define CALIBRATION_TIME_MS (5UL * 60UL * 1000UL) // 5 min de baseline
struct Measurement {
uint32_t t_ms; // tiempo desde arranque (ms)
float temp; // °C
float hum; // %RH
float pres; // hPa
float gas; // ohmios
float iaq; // 0..100 índice simple (no BSEC)
};
struct LogStore {
uint32_t magic; // firma para validar
uint16_t head; // próxima posición de escritura
uint16_t count; // nº de muestras válidas (<= LOG_CAPACITY)
float gas_baseline; // baseline persistente del gas
Measurement data[LOG_CAPACITY];
};
FlashStorage(log_store, LogStore);
static const uint32_t MAGIC = 0xA1Q1E0FF;
LogStore store;
uint32_t last_sample_ms = 0;
bool baseline_locked = false;
// Utilidades de mapeo/clamp
static inline float clampf(float v, float lo, float hi) {
if (v < lo) return lo;
if (v > hi) return hi;
return v;
}
// Cálculo de IAQ simple (0-100) no equivalente a BSEC
float compute_iaq_percent(float gas, float gas_baseline, float humidity) {
if (gas_baseline <= 0) return 0;
// Puntuación de gas: mayor resistencia -> mejor aire (menos VOC)
float gas_score = (gas / gas_baseline) * 100.0f;
gas_score = clampf(gas_score, 0.0f, 100.0f);
// Humedad ideal ~ 40% (penaliza desviaciones)
float hum_score = 100.0f - fabsf(humidity - 40.0f) * 2.5f; // ±40% -> 0
hum_score = clampf(hum_score, 0.0f, 100.0f);
// Fusión ponderada (más peso a gas)
float iaq = 0.75f * gas_score + 0.25f * hum_score;
return clampf(iaq, 0.0f, 100.0f);
}
void drawHeader() {
display.setFont(&FreeSans9pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(4, 16);
display.print("epaper-air-quality-logger");
display.setFont(); // volver a font por defecto para cuerpo
}
void drawReadings(const Measurement& m) {
char line[48];
snprintf(line, sizeof(line), "T: %.2f C H: %.1f %%", m.temp, m.hum);
display.setCursor(4, 36);
display.print(line);
snprintf(line, sizeof(line), "P: %.1f hPa Gas: %.0f ohm", m.pres, m.gas);
display.setCursor(4, 52);
display.print(line);
snprintf(line, sizeof(line), "IAQ*: %.1f /100 (baseline: %.0f)", m.iaq, store.gas_baseline);
display.setCursor(4, 68);
display.print(line);
display.setCursor(4, 84);
display.print("*Indice simplificado (no BSEC)");
}
void drawSparkline() {
// Área de la minigráfica: x=4..292, y=90..120 (altura ~30 px)
const int x0 = 4, y0 = 120, w = 288, h = 28;
display.drawRect(x0-1, y0-h-1, w+2, h+2, GxEPD_BLACK);
if (store.count == 0) {
display.setCursor(x0, y0 - 8);
display.print("Sin datos suficientes para graficar.");
return;
}
// Graficar IAQ en 0..100 mapeado a altura
int points = min((int)store.count, w);
// Recorremos el log desde el más reciente hacia atrás
int idx = (int)store.head - 1;
if (idx < 0) idx = LOG_CAPACITY - 1;
for (int i = 0; i < points; i++) {
const Measurement& m = store.data[idx];
float iaq = clampf(m.iaq, 0.0f, 100.0f);
int y = y0 - (int)((iaq / 100.0f) * (float)h);
int x = x0 + (w - 1 - i);
display.drawPixel(x, y, GxEPD_BLACK);
if (--idx < 0) idx = LOG_CAPACITY - 1;
}
}
void epaperFullRefresh(const Measurement& last) {
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
drawHeader();
drawReadings(last);
drawSparkline();
} while (display.nextPage());
}
void initStorage() {
store = log_store.read();
if (store.magic != MAGIC) {
memset(&store, 0, sizeof(store));
store.magic = MAGIC;
store.head = 0;
store.count = 0;
store.gas_baseline = 0.0f;
log_store.write(store);
}
}
void appendMeasurement(const Measurement& m) {
store.data[store.head] = m;
store.head = (store.head + 1) % LOG_CAPACITY;
if (store.count < LOG_CAPACITY) store.count++;
// Escribimos bloque completo (flash) de forma conservadora (1/min)
log_store.write(store);
}
bool setupBME680() {
if (!bme.begin(0x76)) { // la mayoría de BME680 usan 0x76
if (!bme.begin(0x77)) { // fallback si el jumper de su módulo selecciona 0x77
return false;
}
}
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320 C durante 150 ms
return true;
}
bool readBME680(Measurement& m) {
if (!bme.performReading()) return false;
m.t_ms = millis();
m.temp = bme.temperature; // °C
m.hum = bme.humidity; // %RH
m.pres = bme.pressure / 100.0f; // Pa -> hPa
m.gas = bme.gas_resistance; // ohmios
// Base line: primeras muestras (5 min) para calibrar gas
if (!baseline_locked && m.t_ms < CALIBRATION_TIME_MS) {
// Promedio incremental simple
if (store.gas_baseline <= 0.0f) {
store.gas_baseline = m.gas;
} else {
store.gas_baseline = (store.gas_baseline * 0.99f) + (m.gas * 0.01f);
}
} else if (!baseline_locked) {
baseline_locked = true;
// Persistir baseline tras calibración
log_store.write(store);
}
float base = (store.gas_baseline > 0.0f) ? store.gas_baseline : m.gas;
m.iaq = compute_iaq_percent(m.gas, base, m.hum);
return true;
}
void dumpCSV() {
Serial.println(F("#epaper-air-quality-logger CSV"));
Serial.println(F("#t_ms,temp_c,hum_pct,pres_hpa,gas_ohm,iaq_pct"));
int idx = store.head - store.count;
if (idx < 0) idx += LOG_CAPACITY;
for (int i = 0; i < store.count; i++) {
const Measurement& m = store.data[idx];
Serial.print(m.t_ms); Serial.print(',');
Serial.print(m.temp, 2); Serial.print(',');
Serial.print(m.hum, 1); Serial.print(',');
Serial.print(m.pres, 1); Serial.print(',');
Serial.print(m.gas, 0); Serial.print(',');
Serial.println(m.iaq, 1);
if (++idx >= LOG_CAPACITY) idx = 0;
}
Serial.println(F("#END"));
}
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 3000) { } // breve ventana para consola
initStorage();
Wire.begin();
if (!setupBME680()) {
Serial.println(F("Error: BME680 no encontrado en 0x76/0x77"));
// Seguimos para mostrar error en pantalla
}
// Inicialización del e-Paper
display.init(115200); // SPI a 115200kHz internamente optimiza; se usa para debug
display.setRotation(1); // apaisado (ancho 296, alto 128)
// Pantalla inicial
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
drawHeader();
display.setCursor(4, 40);
display.print("Inicializando sensores...");
} while (display.nextPage());
last_sample_ms = 0;
}
void loop() {
// Comandos por Serial
if (Serial.available()) {
int c = Serial.read();
if (c == 'D' || c == 'd') {
dumpCSV();
}
}
uint32_t now = millis();
if (now - last_sample_ms >= SAMPLE_PERIOD_MS || last_sample_ms == 0) {
last_sample_ms = now;
Measurement m{};
if (readBME680(m)) {
appendMeasurement(m);
epaperFullRefresh(m);
Serial.print(F("OK: T=")); Serial.print(m.temp, 2);
Serial.print(F("C H=")); Serial.print(m.hum, 1);
Serial.print(F("% P=")); Serial.print(m.pres, 1);
Serial.print(F("hPa GAS=")); Serial.print(m.gas, 0);
Serial.print(F("ohm IAQ=")); Serial.print(m.iaq, 1);
Serial.println(F("%"));
} else {
// Reporte de error y mostrar en e-Paper
Serial.println(F("Error: performReading() BME680"));
display.setFullWindow();
display.firstPage();
do {
display.fillScreen(GxEPD_WHITE);
drawHeader();
display.setCursor(4, 48);
display.print("Error lectura BME680");
} while (display.nextPage());
}
}
// Bajo consumo entre muestras
LowPower.sleep(1000); // Sleep ligero 1s, repite hasta llegar al minuto
}
Explicación breve de partes clave:
– Baseline del gas: se promedia durante 5 minutos al arranque para normalizar la resistencia del sensor (que varía entre unidades y con el entorno). Luego se “congela” y se persiste.
– IAQ simplificado: no usa BSEC (Bosch), pero ofrece una métrica cualitativa de 0 a 100 que combina gas y humedad.
– e‑Paper: se usa un refresco completo por ciclo para simplificar. En mejoras proponemos pasar a parciales.
– Persistencia: usamos FlashStorage_SAMD para almacenar un buffer circular. Es un ejemplo didáctico: escribir flash con frecuencia conlleva desgaste; más abajo damos recomendaciones para mitigar.
Compilación, flasheo y ejecución
Se asume que el sketch está en un directorio llamado “epaper-air-quality-logger”.
Estructura sugerida:
– epaper-air-quality-logger/
– epaper-air-quality-logger.ino
Pasos:
1) Preparar el core y libs (si no lo hizo antes):
arduino-cli core update-index
arduino-cli core install arduino:samd@1.8.14
arduino-cli lib install "GxEPD2@1.5.9"
arduino-cli lib install "Adafruit GFX Library@1.11.9"
arduino-cli lib install "Adafruit BME680 Library@2.0.3"
arduino-cli lib install "Adafruit Unified Sensor@1.1.14"
arduino-cli lib install "Adafruit BusIO@1.14.1"
arduino-cli lib install "ArduinoLowPower@1.2.2"
arduino-cli lib install "FlashStorage_SAMD@1.3.2"
2) Compilar para Nano 33 IoT:
arduino-cli compile --fqbn arduino:samd:nano_33_iot epaper-air-quality-logger
3) Identificar el puerto (ejemplos):
– Linux: ls /dev/ttyACM
– macOS: ls /dev/tty.usbmodem
– Windows: mode | findstr COM (o ver en el Administrador de dispositivos)
4) Subir el binario:
– Linux/macOS:
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:samd:nano_33_iot epaper-air-quality-logger
- Windows (ajuste COMx):
arduino-cli upload -p COM4 --fqbn arduino:samd:nano_33_iot epaper-air-quality-logger
5) Abrir monitor serie a 115200 baudios:
arduino-cli monitor -p /dev/ttyACM0 -c baudrate=115200
En Windows:
arduino-cli monitor -p COM4 -c baudrate=115200
Validación paso a paso
1) Validación de arranque:
– La pantalla e‑Paper debe mostrar “epaper-air-quality-logger” y el texto “Inicializando sensores…” durante el primer ciclo.
– En el monitor serie, verá un mensaje de estado o un error del BME680 si no está detectado.
2) Detección del BME680:
– Si todo va bien, al cabo de ~1 minuto, se imprimirá por serie una línea con T, H, P, GAS e IAQ.
– La pantalla hará un refresco completo mostrando:
– Temperatura, humedad, presión
– Resistencia de gas
– IAQ (%) y el valor de baseline
– Un recuadro en la parte inferior para la minigráfica (al principio con pocos puntos)
3) Calibración de baseline:
– Durante los primeros 5 minutos, el baseline de gas se ajusta (valor fluctúa hacia un promedio).
– El campo “baseline” en la pantalla se estabilizará.
– baseline_locked pasa a true internamente y el valor queda persistido.
4) Registro en flash:
– Tras varios minutos, envíe “d” o “D” por el monitor serie.
– Debe aparecer un CSV con encabezado y filas: t_ms,temp_c,hum_pct,pres_hpa,gas_ohm,iaq_pct.
– Reinicie la placa (botón reset) y vuelva a pedir “DUMP”: la data debe persistir (no se pierde tras reset).
5) Minigráfica:
– A partir de ~10 muestras, la línea en el recuadro inferior debe mostrar una tendencia de IAQ (0..100) desplazándose hacia la izquierda con el tiempo.
6) Comprobación visual y numérica:
– Compare temperatura y humedad con otro termohigrómetro para validar orden de magnitud.
– Alterar el ambiente:
– Exponer brevemente a alcohol isopropílico o aliento cerca del sensor (sin tocar): la resistencia de gas bajará y el IAQ tenderá a disminuir (peor “calidad”).
– Volver a aire limpio: el IAQ se recupera gradualmente.
Troubleshooting
1) La pantalla e‑Paper no muestra nada / se queda en blanco
– Causas probables:
– Pines mal conectados (DC/CS/RST/BUSY invertidos).
– Falta de 3.3 V o masa común.
– Clase de panel incorrecta en GxEPD2.
– Solución:
– Verifique la tabla de pines. Asegure EPD_CS=10, EPD_DC=9, EPD_RST=8, EPD_BUSY=7.
– Confirme que su Waveshare es 2.9″ b/w V2 (SSD1680). La clase GxEPD2_290_T5 es la adecuada para SSD1680.
– Pruebe display.setRotation(0/1/2/3) por si el mapeo afecta coordenadas visibles.
2) BME680 no detectado en 0x76/0x77
– Causas:
– Cableado SDA/SCL invertido o en pines incorrectos.
– Alimentación a 5 V en lugar de 3.3 V o GND suelta.
– Dirección I2C configurada por puente a otra distinta.
– Solución:
– Conectar a los pines etiquetados SDA/SCL del Nano 33 IoT (no A4/A5).
– Usar un escáner I2C para confirmar dirección.
– Revisar soldaduras o jumpers en el módulo BME680.
3) Ghosting o artefactos en e‑Paper
– Causas:
– Actualizaciones muy frecuentes sin refresco completo.
– Cables SPI largos o ruidosos.
– Solución:
– Mantener el refresco completo cada cierto número de ciclos.
– Reducir longitud de cables y retorcer MOSI/SCK con GND cercano para minimizar ruido.
4) Subida falla: “No device found on…”
– Causas:
– Puerto incorrecto.
– El bootloader solo está activo unos segundos tras reset.
– Solución:
– Identificar el puerto correcto con arduino-cli board list.
– Pulsar reset doblemente para entrar en “modo bootloader” y reintentar upload.
5) Mensajes “Error lectura BME680”
– Causas:
– Tiempos de calentamiento del gas no satisfechos.
– Interferencias I2C o alimentación inestable.
– Solución:
– Verificar alimentación 3.3 V estable.
– Aumentar delay entre lecturas o revisar setGasHeater(320, 150).
6) Desgaste de flash evidente / errores de escritura
– Causas:
– Frecuencia de escritura muy alta.
– Solución:
– Incrementar intervalo de muestreo (p. ej., 5 minutos).
– Escribir en flash cada N muestras en lugar de cada vez (buffer RAM + commit).
7) e‑Paper parpadea demasiado
– Causa:
– Refresco completo en cada ciclo.
– Solución:
– Migrar a actualizaciones parciales para datos pequeños (ver mejoras).
– Usar full refresh cada X ciclos para “limpiar”.
8) IAQ no parece realista
– Causa:
– El IAQ simplificado no es equivalente a BSEC.
– Solución:
– Aplique BSEC2 de Bosch para IAQ calibrado y métricas como eCO2/VOC Index (ver mejoras).
Mejoras y variantes
- IAQ profesional con BSEC2:
- Sustituir el cálculo simple por Bosch BSEC2 para obtener IAQ, eCO2, bVOC con calibración robusta.
-
Asegurarse de la compatibilidad de BSEC2 con SAMD21 y uso de licencias.
-
Reducción de desgaste de flash:
- Implementar un buffer en RAM para N muestras (p. ej., 12) y escribir en flash en bloques.
-
Reducir el muestreo a cada 5 minutos y LOG_CAPACITY = 288 para ~24 h.
-
Actualización parcial de e‑Paper:
- Usar “setPartialWindow” y dibujar solo las áreas cambiadas (número IAQ/última barra del sparkline).
-
Hacer un full-refresh cada 10 parciales para evitar ghosting.
-
Exportación de datos:
- Implementar un comando “DUMPJSON” con ArduinoJson para exportar en JSON.
-
Guardar en archivo CSV en microSD (si se añade un módulo microSD por SPI con su propio CS).
-
Integración IoT:
- Publicar mediciones por WiFi (Nano 33 IoT) a MQTT/InfluxDB y mantener la e‑Paper como tablero local.
-
Sincronizar hora por NTP y almacenar timestamps UNIX en el log.
-
Energía:
- Usar “LowPower.deepSleep” con alarma RTC para ciclos de muestreo largos.
-
Apagar periféricos entre muestras (p. ej., desalimentar BME680 con transistor p‑MOS si el diseño lo permite).
-
Visual:
- Cambiar la tipografía por fuentes GFX más grandes/bold para legibilidad.
- Añadir iconos según rangos de IAQ con bitmap monocromo.
Checklist de verificación
- [ ] He instalado Arduino CLI 0.35.3 y el core arduino:samd@1.8.14.
- [ ] He instalado las bibliotecas exactas: GxEPD2 1.5.9, Adafruit GFX 1.11.9, Adafruit BME680 2.0.3, Adafruit Unified Sensor 1.1.14, Adafruit BusIO 1.14.1, ArduinoLowPower 1.2.2, FlashStorage_SAMD 1.3.2.
- [ ] He cableado correctamente e‑Paper: CS=10, DC=9, RST=8, BUSY=7; MOSI=D11, SCK=D13; 3V3 y GND comunes.
- [ ] He cableado correctamente BME680 por I2C: SDA y SCL a los pines SDA/SCL del Nano 33 IoT; 3V3 y GND.
- [ ] El sketch compila con: arduino-cli compile –fqbn arduino:samd:nano_33_iot epaper-air-quality-logger.
- [ ] El sketch sube con: arduino-cli upload -p
–fqbn arduino:samd:nano_33_iot epaper-air-quality-logger. - [ ] Veo en la e‑Paper el título y, tras ~1 min, los valores de T/H/P/GAS/IAQ y la minigráfica.
- [ ] Tras 5 min, el baseline de gas se estabiliza y el IAQ varía con el ambiente.
- [ ] Al enviar “D” por el monitor serie, recibo el CSV con registros.
- [ ] Tras un reset, el log sigue presente (persistencia OK).
Apéndice: comandos de referencia compactos
- Instalar core SAMD y libs:
arduino-cli core update-index
arduino-cli core install arduino:samd@1.8.14
arduino-cli lib install "GxEPD2@1.5.9" "Adafruit GFX Library@1.11.9" "Adafruit BME680 Library@2.0.3" "Adafruit Unified Sensor@1.1.14" "Adafruit BusIO@1.14.1" "ArduinoLowPower@1.2.2" "FlashStorage_SAMD@1.3.2"
- Compilar y subir:
arduino-cli compile --fqbn arduino:samd:nano_33_iot epaper-air-quality-logger
arduino-cli upload -p /dev/ttyACM0 --fqbn arduino:samd:nano_33_iot epaper-air-quality-logger
arduino-cli monitor -p /dev/ttyACM0 -c baudrate=115200
Nota final
Este caso práctico está específicamente enfocado al modelo “Arduino Nano 33 IoT + Waveshare 2.9in e‑Paper (SSD1680) + BME680” con la toolchain y versiones indicadas. Todo el cableado, el código y los comandos han sido diseñados para esta combinación concreta a fin de lograr un “epaper-air-quality-logger” reproducible y validable.
Encuentra este producto y/o libros sobre este tema en Amazon
Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.



