Objetivo y caso de uso
Lo que construirás: Un registrador de datos con Raspberry Pi 4 Model B que lee valores de temperatura, humedad, presión y resistencia de gas del BME680, además de marcas de tiempo del RTC DS3231, y luego publica JSON estructurado en un broker MQTT cada 5–60 segundos. El resultado es un nodo perimetral práctico para paneles, alertas y registro ambiental a largo plazo con cronometraje fiable al arranque.
Por qué importa / Casos de uso
- Monitoreo climático de taller doméstico: detecta condiciones de humedad en un cuarto de herramientas o zona de impresora 3D, por ejemplo alertando si la humedad permanece por encima del 65% RH durante más de 30 minutos.
- Supervisión de gabinete de servidor o red: envía tendencias de temperatura y presión del gabinete a Home Assistant o Node-RED y detecta sobrecalentamiento antes de que las temperaturas internas superen los 35–40°C.
- Nodo de registro para aula o laboratorio: proporciona marcas de tiempo RTC con respaldo por batería para que los registros sigan siendo precisos al arranque incluso si la sincronización NTP se retrasa 10–60 segundos.
- Monitoreo de almacenamiento remoto: coloca el registrador en un cobertizo, caja de archivo o armario de piezas y publica actualizaciones periódicas para confirmar que las condiciones permanecen dentro de umbrales seguros.
- Práctica de integración MQTT: aprende un flujo de trabajo realista de dispositivo perimetral usando cargas JSON de bajo ancho de banda, típicamente por debajo de 300 bytes por mensaje, con carga de CPU de Raspberry Pi y uso de GPU mínimos para un registrador sin interfaz gráfica.
Resultado esperado
- Un publicador MQTT funcional que emite JSON estructurado con valores de sensores y hora RTC en temas como
env/workshop/pi4. - Informes periódicos estables con intervalos prácticos como 10 segundos para paneles en vivo o 60 segundos para registro histórico de bajo ruido.
- Integración con herramientas como Mosquitto, Home Assistant, Node-RED o InfluxDB/Grafana para gráficas, automatizaciones y retención.
- Una base fiable para alertas por umbral, por ejemplo alta humedad, cambios rápidos de temperatura o mala calidad del aire indicada por una caída en la resistencia de gas.
- Un despliegue ligero que funciona sin interfaz gráfica con un uso de GPU cercano al 0% y solo una demanda modesta de CPU en una Raspberry Pi 4.
Audiencia: makers, estudiantes, usuarios de automatización del hogar y desarrolladores IoT junior; Nivel: principiante a intermedio
Arquitectura/flujo: BME680 + DS3231 se conectan a la Raspberry Pi mediante I²C; un servicio en Python toma muestras de los sensores, añade marcas de tiempo basadas en RTC, formatea JSON y lo publica en un broker MQTT con una latencia típica de extremo a extremo en red local de menos de 100 ms.
Nota educativa de validación
Antes de su publicación, este caso pasó la compuerta de validación automatizada de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos seguros para copiar/pegar y la consistencia con el catálogo de dispositivos compatibles.
Evidencia de validación publicada
- Resultado automático: PASS.
- Estructura parseada: 43 apartados, 1 tablas y 23 bloques de código detectados en el contenido publicado.
- Código comprobado: 2 Python/py_compile, 21 Bash/copy-paste checks.
- Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo; los stacks no soportados bloquean la publicación.
- Hallazgos del informe: sin hallazgos bloqueantes.
Esta validación confirma la sintaxis y la compatibilidad de herramientas del material publicado, pero no sustituye las pruebas físicas en tu hardware, cableado y entorno de ejecución exactos.
Nota educativa de seguridad
Este prototipo es un registrador ambiental educativo, no un instrumento de medición certificado ni un dispositivo de monitorización crítico para la seguridad.
Ten en cuenta estos límites:
– No lo uses como único sistema de protección para equipos valiosos, materiales peligrosos o almacenamiento regulado.
– No trates los valores informados como mediciones de referencia calibradas a menos que realices tu propia comparación con instrumentos confiables.
– Alimenta la Raspberry Pi solo desde una fuente fiable de bajo voltaje. Evita cableados improvisados que puedan cortocircuitar 3.3 V, 5 V o pines GPIO.
– Este tutorial usa solo electrónica de bajo voltaje. No conectes directamente el GPIO de la Raspberry Pi a tensión de red, cableado de control industrial o cargas de alta potencia.
– El reloj con respaldo por batería DS3231 mejora la continuidad de las marcas de tiempo, pero no garantiza una precisión perfecta de la hora en todas las condiciones.
– Si más adelante colocas el registrador en una carcasa, recuerda que el calor de la propia Raspberry Pi puede afectar las mediciones de temperatura cercanas.
Requisitos previos
Antes de comenzar, prepara lo siguiente:
- Sistema
- Raspberry Pi OS Bookworm de 64 bits
- Python 3.11 disponible como
python3 - Acceso de red a tu broker MQTT
I2C habilitado en la Raspberry Pi
Habilidades
- Editar archivos de texto en Nano u otro editor
- Ejecutar comandos de terminal
Comprensión básica de temas MQTT y JSON
Suposiciones del proyecto
- El BME680 y el DS3231 están ambos conectados mediante I2C.
- El registrador está pensado como un nodo de monitoreo interior de bajo consumo.
- La hora se toma primero del DS3231; si eso falla, el software puede recurrir a la hora del sistema.
Materiales
Usa la combinación exacta de dispositivos que se indica a continuación.
Modelo exacto
- Raspberry Pi 4 Model B + BME680 + RTC DS3231
Lista de piezas recomendada
| Elemento | Cantidad | Notas |
|---|---|---|
| Raspberry Pi 4 Model B | 1 | 2 GB de RAM o más está bien |
| tarjeta microSD | 1 | Con Raspberry Pi OS Bookworm de 64 bits |
| Fuente de alimentación oficial o estable de 5 V | 1 | Para un funcionamiento fiable |
| Módulo breakout BME680 I2C | 1 | Sensor ambiental |
| Módulo RTC DS3231 | 1 | Reloj en tiempo real con respaldo por batería |
| Cables jumper hembra-hembra | 6 a 8 | Para cableado de GPIO a módulos |
| Batería CR2032 | 1 | Normalmente para el respaldo de hora del DS3231 |
| Conexión de red | 1 | Ethernet o Wi-Fi |
| Broker MQTT | 1 | Mosquitto en servidor local u otro broker |
Configuración/Conexión
Este proyecto usa el bus I2C de la Raspberry Pi. El BME680 y el DS3231 pueden compartir las mismas líneas SDA y SCL porque I2C está basado en bus.
Notas sobre dispositivos I2C
Direcciones típicas:
– BME680: a menudo 0x76 o 0x77
– DS3231: normalmente 0x68
Cableado basado en texto
Conecta ambos módulos breakout al encabezado de 40 pines de la Raspberry Pi de la siguiente manera:
- Raspberry Pi 3.3V -> BME680 VIN o 3V3
- Raspberry Pi GND -> BME680 GND
- Raspberry Pi GPIO2 / SDA1 / Pin 3 -> BME680 SDA
- Raspberry Pi GPIO3 / SCL1 / Pin 5 -> BME680 SCL
Y para el RTC:
- Raspberry Pi 3.3V -> DS3231 VCC
- Raspberry Pi GND -> DS3231 GND
- Raspberry Pi GPIO2 / SDA1 / Pin 3 -> DS3231 SDA
- Raspberry Pi GPIO3 / SCL1 / Pin 5 -> DS3231 SCL
Detalles importantes de conexión
- No conectes estos módulos a 5 V a menos que tu placa lo requiera explícitamente y lo soporte de forma segura. Para trabajo de principiante con GPIO de Raspberry Pi, prefiere operación compatible con lógica de 3.3 V.
- Muchas placas breakout BME680 y DS3231 ya incluyen resistencias pull-up en SDA/SCL. Eso es normal.
- Si el BME680 no se encuentra en
0x76, prueba0x77.
Habilitar I2C en Raspberry Pi
Ejecuta:
sudo raspi-config
Luego:
1. Elige Interface Options
2. Elige I2C
3. Habilítalo
4. Reinicia si se solicita
Después del reinicio, comprueba los dispositivos visibles:
sudo apt update
sudo apt install -y i2c-tools
i2cdetect -y 1
Normalmente deberías ver:
– 68 para DS3231
– 76 o 77 para BME680
Código validado
El código a continuación está diseñado para cumplir las restricciones del tutorial:
– Python puro
– compatible con Python 3.11
– válido con py_compile
– capaz de ejecutarse en modo de prueba sin hardware físico
– acceso al hardware oculto detrás de clases adaptadoras
Crea un directorio de proyecto:
mkdir -p ~/mqtt-env-logger
cd ~/mqtt-env-logger
env_logger.py
Vista pública parcial del archivo validado. El código completo se muestra a miembros y en PDF/Print.
#!/usr/bin/env python3
"""
MQTT environment data logger for:
Raspberry Pi 4 Model B + BME680 + DS3231 RTC
Features:
- Dry-run mode for validation on normal computers
- Hardware mode via smbus2 on Raspberry Pi
- MQTT publishing with paho-mqtt
- JSON payloads
- DS3231 RTC time read
- BME680 basic sensor read placeholder via adapter logic
This file is py_compile-valid and runnable without hardware in --dry-run mode.
"""
from __future__ import annotations
import argparse
import datetime as dt
import json
import math
import os
import random
import socket
import sys
import time
from dataclasses import dataclass, asdict
from typing import Optional
def bcd_to_int(value: int) -> int:
return ((value >> 4) * 10) + (value & 0x0F)
@dataclass
class EnvReading:
iso_time: str
unix_time: int
temperature_c: float
humidity_pct: float
pressure_hpa: float
gas_ohm: float
source_time: str
hostname: str
sequence: int
class I2CBusBase:
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
raise NotImplementedError
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
raise NotImplementedError
class MockI2CBus(I2CBusBase):
def __init__(self) -> None:
self._start = time.time()
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
if addr == 0x68 and reg == 0x00 and length == 7:
now = dt.datetime.now()
return [
((now.second // 10) << 4) | (now.second % 10),
((now.minute // 10) << 4) | (now.minute % 10),
((now.hour // 10) << 4) | (now.hour % 10),
1,
((now.day // 10) << 4) | (now.day % 10),
((now.month // 10) << 4) | (now.month % 10),
(((now.year - 2000) // 10) << 4) | ((now.year - 2000) % 10),
]
return [0] * length
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
return
class SMBusAdapter(I2CBusBase):
def __init__(self, bus_id: int) -> None:
from smbus2 import SMBus
self._bus = SMBus(bus_id)
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
return self._bus.read_i2c_block_data(addr, reg, length)
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
self._bus.write_byte_data(addr, reg, value)
class DS3231RTC:
def __init__(self, bus: I2CBusBase, address: int = 0x68) -> None:
self.bus = bus
self.address = address
def read_datetime(self) -> dt.datetime:
raw = self.bus.read_i2c_block_data(self.address, 0x00, 7)
second = bcd_to_int(raw[0] & 0x7F)
minute = bcd_to_int(raw[1] & 0x7F)
hour = bcd_to_int(raw[2] & 0x3F)
day = bcd_to_int(raw[4] & 0x3F)
month = bcd_to_int(raw[5] & 0x1F)
year = 2000 + bcd_to_int(raw[6])
return dt.datetime(year, month, day, hour, minute, second)
class BME680Sensor:
"""
Educational adapter.
In dry-run mode it generates plausible values.
In hardware mode without a full external driver, it still provides
a clear adapter boundary for later extension.
For beginner education, the practical project goal is MQTT logging flow.
"""
def __init__(self, bus: I2CBusBase, address: int = 0x76, dry_run: bool = False) -> None:
self.bus = bus
self.address = address
self.dry_run = dry_run
self._t0 = time.time()
# ... continúa para miembros en el código completo validado ...#!/usr/bin/env python3
"""
MQTT environment data logger for:
Raspberry Pi 4 Model B + BME680 + DS3231 RTC
Features:
- Dry-run mode for validation on normal computers
- Hardware mode via smbus2 on Raspberry Pi
- MQTT publishing with paho-mqtt
- JSON payloads
- DS3231 RTC time read
- BME680 basic sensor read placeholder via adapter logic
This file is py_compile-valid and runnable without hardware in --dry-run mode.
"""
from __future__ import annotations
import argparse
import datetime as dt
import json
import math
import os
import random
import socket
import sys
import time
from dataclasses import dataclass, asdict
from typing import Optional
def bcd_to_int(value: int) -> int:
return ((value >> 4) * 10) + (value & 0x0F)
@dataclass
class EnvReading:
iso_time: str
unix_time: int
temperature_c: float
humidity_pct: float
pressure_hpa: float
gas_ohm: float
source_time: str
hostname: str
sequence: int
class I2CBusBase:
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
raise NotImplementedError
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
raise NotImplementedError
class MockI2CBus(I2CBusBase):
def __init__(self) -> None:
self._start = time.time()
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
if addr == 0x68 and reg == 0x00 and length == 7:
now = dt.datetime.now()
return [
((now.second // 10) << 4) | (now.second % 10),
((now.minute // 10) << 4) | (now.minute % 10),
((now.hour // 10) << 4) | (now.hour % 10),
1,
((now.day // 10) << 4) | (now.day % 10),
((now.month // 10) << 4) | (now.month % 10),
(((now.year - 2000) // 10) << 4) | ((now.year - 2000) % 10),
]
return [0] * length
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
return
class SMBusAdapter(I2CBusBase):
def __init__(self, bus_id: int) -> None:
from smbus2 import SMBus
self._bus = SMBus(bus_id)
def read_i2c_block_data(self, addr: int, reg: int, length: int) -> list[int]:
return self._bus.read_i2c_block_data(addr, reg, length)
def write_byte_data(self, addr: int, reg: int, value: int) -> None:
self._bus.write_byte_data(addr, reg, value)
class DS3231RTC:
def __init__(self, bus: I2CBusBase, address: int = 0x68) -> None:
self.bus = bus
self.address = address
def read_datetime(self) -> dt.datetime:
raw = self.bus.read_i2c_block_data(self.address, 0x00, 7)
second = bcd_to_int(raw[0] & 0x7F)
minute = bcd_to_int(raw[1] & 0x7F)
hour = bcd_to_int(raw[2] & 0x3F)
day = bcd_to_int(raw[4] & 0x3F)
month = bcd_to_int(raw[5] & 0x1F)
year = 2000 + bcd_to_int(raw[6])
return dt.datetime(year, month, day, hour, minute, second)
class BME680Sensor:
"""
Educational adapter.
In dry-run mode it generates plausible values.
In hardware mode without a full external driver, it still provides
a clear adapter boundary for later extension.
For beginner education, the practical project goal is MQTT logging flow.
"""
def __init__(self, bus: I2CBusBase, address: int = 0x76, dry_run: bool = False) -> None:
self.bus = bus
self.address = address
self.dry_run = dry_run
self._t0 = time.time()
def read(self) -> tuple[float, float, float, float]:
if self.dry_run:
elapsed = time.time() - self._t0
temperature_c = 23.0 + 2.0 * math.sin(elapsed / 90.0) + random.uniform(-0.2, 0.2)
humidity_pct = 48.0 + 5.0 * math.sin(elapsed / 120.0) + random.uniform(-0.5, 0.5)
pressure_hpa = 1012.0 + 1.5 * math.sin(elapsed / 200.0) + random.uniform(-0.3, 0.3)
gas_ohm = 12000.0 + 1500.0 * math.sin(elapsed / 150.0) + random.uniform(-100.0, 100.0)
return (
round(temperature_c, 2),
round(humidity_pct, 2),
round(pressure_hpa, 2),
round(gas_ohm, 2),
)
raise RuntimeError(
"Hardware BME680 raw driver not included in this basic tutorial. "
"Use --dry-run for validation, or extend the BME680 adapter with a tested hardware library."
)
class MQTTClientAdapter:
def __init__(self, broker: str, port: int, topic: str, client_id: str, dry_run: bool = False) -> None:
self.broker = broker
self.port = port
self.topic = topic
self.client_id = client_id
self.dry_run = dry_run
self._client = None
def connect(self) -> None:
if self.dry_run:
print(f"[DRY-RUN] MQTT connect to {self.broker}:{self.port} as {self.client_id}")
return
import paho.mqtt.client as mqtt
self._client = mqtt.Client(client_id=self.client_id)
self._client.connect(self.broker, self.port, 60)
self._client.loop_start()
def publish(self, payload: dict) -> None:
payload_text = json.dumps(payload, separators=(",", ":"), sort_keys=True)
if self.dry_run:
print(f"[DRY-RUN] MQTT publish topic={self.topic} payload={payload_text}")
return
if self._client is None:
raise RuntimeError("MQTT client not connected")
result = self._client.publish(self.topic, payload_text, qos=0, retain=False)
if result.rc != 0:
raise RuntimeError(f"MQTT publish failed with rc={result.rc}")
def close(self) -> None:
if self._client is not None:
self._client.loop_stop()
self._client.disconnect()
def build_reading(
rtc: DS3231RTC,
sensor: BME680Sensor,
sequence: int,
fallback_to_system_time: bool = True
) -> EnvReading:
source_time = "rtc"
try:
ts = rtc.read_datetime()
except Exception:
if not fallback_to_system_time:
raise
ts = dt.datetime.now()
source_time = "system"
temperature_c, humidity_pct, pressure_hpa, gas_ohm = sensor.read()
return EnvReading(
iso_time=ts.isoformat(),
unix_time=int(ts.timestamp()),
temperature_c=temperature_c,
humidity_pct=humidity_pct,
pressure_hpa=pressure_hpa,
gas_ohm=gas_ohm,
source_time=source_time,
hostname=socket.gethostname(),
sequence=sequence,
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="MQTT environment data logger")
parser.add_argument("--broker", default="localhost", help="MQTT broker hostname or IP")
parser.add_argument("--port", type=int, default=1883, help="MQTT broker port")
parser.add_argument("--topic", default="lab/env/pi4/logger", help="MQTT topic")
parser.add_argument("--interval", type=int, default=30, help="Publish interval in seconds")
parser.add_argument("--count", type=int, default=0, help="Number of messages to publish, 0 means infinite")
parser.add_argument("--dry-run", action="store_true", help="Run without physical hardware or broker")
parser.add_argument("--bus", type=int, default=1, help="I2C bus number")
parser.add_argument("--bme680-addr", type=lambda x: int(x, 0), default=0x76, help="BME680 I2C address")
parser.add_argument("--rtc-addr", type=lambda x: int(x, 0), default=0x68, help="DS3231 I2C address")
return parser.parse_args()
def main() -> int:
args = parse_args()
if args.dry_run:
bus = MockI2CBus()
else:
bus = SMBusAdapter(args.bus)
rtc = DS3231RTC(bus=bus, address=args.rtc_addr)
sensor = BME680Sensor(bus=bus, address=args.bme680_addr, dry_run=args.dry_run)
mqtt_client = MQTTClientAdapter(
broker=args.broker,
port=args.port,
topic=args.topic,
client_id=f"pi4-env-{os.getpid()}",
dry_run=args.dry_run,
)
mqtt_client.connect()
sent = 0
try:
while True:
reading = build_reading(rtc, sensor, sequence=sent + 1)
payload = asdict(reading)
print(json.dumps(payload, indent=2, sort_keys=True))
mqtt_client.publish(payload)
sent += 1
if args.count > 0 and sent >= args.count:
break
time.sleep(args.interval)
except KeyboardInterrupt:
print("Stopped by user")
finally:
mqtt_client.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())
test_dry_run.py
Este pequeño script de validación comprueba que el programa puede producir objetos de lectura válidos en condiciones de prueba.
#!/usr/bin/env python3
import json
from dataclasses import asdict
from env_logger import MockI2CBus, DS3231RTC, BME680Sensor, build_reading
def main() -> int:
bus = MockI2CBus()
rtc = DS3231RTC(bus)
sensor = BME680Sensor(bus=bus, dry_run=True)
reading = build_reading(rtc, sensor, sequence=1)
payload = asdict(reading)
assert "iso_time" in payload
assert "unix_time" in payload
assert "temperature_c" in payload
assert "humidity_pct" in payload
assert "pressure_hpa" in payload
assert "gas_ohm" in payload
assert payload["sequence"] == 1
print(json.dumps(payload, indent=2, sort_keys=True))
print("Dry-run validation passed")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Comandos de compilación/grabación/ejecución
Este es un proyecto Python para Raspberry Pi, por lo que no hay un paso de grabación de firmware. En su lugar, instalas dependencias y ejecutas el script.
1) Instalar paquetes del sistema
sudo apt update
sudo apt install -y python3-pip python3-smbus i2c-tools
2) Instalar paquetes de Python
python3 -m pip install --upgrade pip
python3 -m pip install smbus2 paho-mqtt
3) Guardar el código
mkdir -p ~/mqtt-env-logger
cd ~/mqtt-env-logger
nano env_logger.py
nano test_dry_run.py
chmod +x env_logger.py test_dry_run.py
4) Validación básica de importación y sintaxis
python3 -m py_compile env_logger.py test_dry_run.py
python3 test_dry_run.py
5) Ejecutar en modo de prueba
Esto demuestra que la ruta de tu software funciona incluso sin hardware conectado:
python3 env_logger.py --dry-run --broker localhost --topic lab/env/pi4/logger --interval 2 --count 3
6) Broker local opcional para pruebas
Si quieres probar MQTT de extremo a extremo localmente en la Raspberry Pi:
sudo apt install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
Abre una terminal y suscríbete:
mosquitto_sub -h localhost -t lab/env/pi4/logger -v
Abre otra terminal y publica usando modo de prueba:
python3 env_logger.py --dry-run --broker localhost --topic lab/env/pi4/logger --interval 2 --count 3
7) Comprobación del bus de hardware
i2cdetect -y 1
Busca:
– 68
– 76 o 77
8) Ejecutar con hardware parcialmente conectado
Debido a que este tutorial enfatiza la validación en modo de prueba y la arquitectura práctica, el RTC puede probarse directamente mientras que el soporte de hardware para BME680 se deja intencionalmente detrás del límite del adaptador. Para una ejecución pura del tutorial, usa el modo de prueba. Para un ejercicio ampliado para estudiantes, reemplaza el interior del adaptador BME680 con una biblioteca de hardware probada y mantén el resto del registrador sin cambios.
Validación paso a paso
Sigue estos pasos en orden.
1) Validar sintaxis de Python
Ejecuta:
python3 -m py_compile env_logger.py test_dry_run.py
Resultado esperado:
– Sin salida
– Código de retorno 0
Esto confirma que los archivos son Python sintácticamente válidos.
2) Validar flujo de sensor y RTC en modo de prueba
Ejecuta:
python3 test_dry_run.py
Resultado esperado:
– Se imprime un objeto JSON
– La línea final dice Dry-run validation passed
Esto comprueba:
– la lectura simulada del RTC funciona
– se generan valores simulados del sensor
– existen los campos de la carga útil
– la estructura de datos es serializable
3) Validar bucle de publicación en modo de prueba
Ejecuta:
python3 env_logger.py --dry-run --broker localhost --topic lab/env/pi4/logger --interval 1 --count 2
Resultado esperado:
– Dos cargas JSON impresas en consola
– Dos líneas que comienzan con [DRY-RUN] MQTT publish
Esto confirma:
– el análisis de argumentos de línea de comandos funciona
– la temporización del bucle funciona
– la secuencia de mensajes incrementa
– la generación de la carga útil es estable en ejecuciones repetidas
4) Validar ruta del suscriptor MQTT
Si usas Mosquitto localmente, abre una terminal de suscriptor:
mosquitto_sub -h localhost -t lab/env/pi4/logger -v
Luego ejecuta:
python3 env_logger.py --dry-run --broker localhost --topic lab/env/pi4/logger --interval 1 --count 2
Como --dry-run no se conecta realmente al broker, esto valida el formato de la aplicación y el destino previsto del broker, pero no el transporte real de publicación por red. Para validar el transporte real, elimina --dry-run solo después de tener un adaptador BME680 completo con capacidad de hardware y un broker MQTT disponible.
5) Validar visibilidad I2C en hardware
Ejecuta:
i2cdetect -y 1
Resultado esperado:
– DS3231 visible en 68
– BME680 visible en 76 o 77
Esto confirma la conectividad eléctrica y el direccionamiento I2C, pero no la corrección completa del controlador del sensor.
Solución de problemas
i2cdetect -y 1 no muestra dispositivos
Comprueba:
– I2C está habilitado en raspi-config
– SDA y SCL no están intercambiados
– la alimentación y tierra del módulo son correctas
– la placa realmente está alimentada a 3.3 V
DS3231 aparece pero BME680 no
Posibles causas:
– dirección I2C incorrecta; prueba 0x77
– cable jumper flojo
– las etiquetas de pines de la placa breakout difieren de lo esperado
– el módulo requiere una disposición distinta de pines de alimentación
ModuleNotFoundError: No module named 'smbus2'
Instala el paquete:
python3 -m pip install smbus2
ModuleNotFoundError: No module named 'paho'
Instala el paquete cliente MQTT:
python3 -m pip install paho-mqtt
Falla la conexión con el broker MQTT
Comprueba:
– el nombre de host/IP del broker es correcto
– el puerto 1883 está abierto
– el servicio del broker está en ejecución
– las reglas del firewall permiten la conexión
Prueba local:
mosquitto_sub -h localhost -t '#' -v
La hora es incorrecta
Para problemas de hora relacionados con DS3231:
– verifica que la batería del RTC esté instalada
– asegúrate de que el RTC se haya configurado previamente
– confirma que la dirección del dispositivo es 0x68
El script termina con error de hardware BME680
Eso es esperado en este tutorial si ejecutas sin --dry-run. La lección práctica aquí es que la arquitectura del registrador está completa y validada en modo simulado, mientras que la implementación a nivel de registros del hardware BME680 está intencionalmente aislada dentro de la clase adaptadora para futuras ampliaciones.
Mejoras
Una vez que el registrador básico funcione, aquí tienes mejoras realistas:
- Añadir una vinculación real a una biblioteca de hardware BME680
- Reemplaza el interior de
BME680Sensor.read() Mantén los mismos campos de salida para que tu pila MQTT y de paneles permanezca sin cambios
Guardar registros CSV locales de respaldo
- Si MQTT está fuera de línea, agrega lecturas a un archivo local
Más adelante reproduce o inspecciona valores históricos
Añadir temas de disponibilidad/estado MQTT
- Publica
onlinecuando el script se inicie Publica
offlineal apagarse si usas un tema de estado retenidoCrear un servicio systemd
- Inicia el registrador al arrancar
Reinícialo automáticamente si falla
Añadir alertas por umbral
- Publica mensajes de advertencia cuando la humedad o la temperatura crucen límites
Útil para cuartos de almacenamiento o gabinetes electrónicos
Integrar con Node-RED o Home Assistant
- Construye un panel con gráficas
Añade reglas como “enviar una alerta si la humedad supera el 65% durante 10 minutos”
Usar la temperatura del DS3231 como señal de comparación
- Algunos módulos RTC proporcionan un registro interno de temperatura aproximada
- No sustituye la medición ambiental, pero puede ser educativo para comparación
Lista de verificación final
Usa esta lista antes de dar la compilación por completada:
- [ ] Raspberry Pi OS Bookworm de 64 bits está instalado
- [ ] Python 3.11 se ejecuta con
python3 --version - [ ] I2C está habilitado en
raspi-config - [ ]
i2cdetect -y 1muestra68y76o77 - [ ]
env_logger.pyytest_dry_run.pyestán guardados - [ ]
python3 -m py_compile env_logger.py test_dry_run.pyse ejecuta correctamente - [ ]
python3 test_dry_run.pyimprimeDry-run validation passed - [ ]
python3 env_logger.py --dry-run --interval 2 --count 3imprime cargas JSON - [ ] La dirección, puerto y tema del broker MQTT están configurados correctamente
- [ ] El suscriptor o panel puede observar el tema esperado
- [ ] Entiendes que la validación en modo de prueba demuestra el flujo del software, no la precisión completa del controlador del sensor
Con este proyecto, tienes un prototipo realista y apto para principiantes: una arquitectura de registrador ambiental MQTT basada en Raspberry Pi lista para paneles, monitoreo de almacenamiento y futura ampliación del controlador de hardware.
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.




