Raspberry Pi

Raspberry Pi DHT22 MQTT: publicación fiable de temperatura y humedad con Python

Conecta un DHT22 a una Raspberry Pi, léelo con Python y publica JSON validado de temperatura y humedad por MQTT con reintentos, filtrado por mediana y contadores de errores que mantienen las lecturas erróneas fuera de tus dashboards.

Sensor de temperatura y humedad DHT22 conectado a una Raspberry Pi en una protoboard que envía lecturas a un broker en la nube en un proyecto Raspberry Pi DHT22 MQTT
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 sensor Raspberry Pi DHT22 MQTT es un clásico primer proyecto IoT y también una fuente clásica de frustración: el DHT22 es barato y lo bastante preciso para habitaciones e invernaderos, pero su protocolo sensible a los tiempos hace que algunas lecturas fallen en una placa Linux. En este tutorial conectarás el sensor, lo leerás desde Python y publicarás JSON limpio de temperatura y humedad en un broker MQTT, gestionando correctamente las lecturas fallidas, los valores inverosímiles y las caídas de red.

Por qué el DHT22 falla a veces

El DHT22 (también vendido como AM2302) no usa I2C ni 1-Wire. La Pi tiene que conmutar una línea GPIO y luego cronometrar una ráfaga de pulsos de decenas de microsegundos. Raspberry Pi OS no es un sistema operativo de tiempo real, así que cuando el kernel planifica otra tarea en el momento equivocado, se pierde un bit y el checksum falla. Es un comportamiento normal, no un sensor averiado. La solución está en el software:

  1. Espera al menos dos segundos entre lecturas; el sensor no puede muestrear más rápido.
  2. Captura el RuntimeError que lanza la biblioteca y reintenta.
  3. Descarta los valores fuera del rango físico del sensor.
  4. Toma varias lecturas válidas y publica la mediana.
  5. Cuenta los fallos y publícalos, para darte cuenta cuando un sensor muera de verdad.

Cableado

Pin del DHT22 Raspberry Pi
VCC (pin 1) 3,3 V (pin físico 1)
DATA (pin 2) GPIO4 (pin físico 7)
NC (pin 3) Sin conectar
GND (pin 4) GND (pin físico 9)

Un DHT22 suelto necesita una resistencia pull-up de 4,7 k a 10 k entre DATA y 3,3 V. Los módulos breakout de tres pines suelen incluirla. Al principio, mantén el cable corto; los tendidos largos funcionan, pero son más sensibles al ruido, y bajar el valor de la pull-up puede ayudar con cables más largos. Aliméntalo a 3,3 V, nunca a 5 V, porque la línea de datos va directa a un pin GPIO que no tolera 5 V.

Instalar las bibliotecas

sudo apt update
sudo apt install -y python3-venv libgpiod2
mkdir -p ~/dht22-mqtt && cd ~/dht22-mqtt
python3 -m venv .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" adafruit-circuitpython-dht

adafruit-circuitpython-dht instala Adafruit Blinka, que proporciona el módulo board. Prueba el sensor por separado antes de añadir MQTT:

python -c "import time, board, adafruit_dht; d = adafruit_dht.DHT22(board.D4); time.sleep(2); print(d.temperature, d.humidity); d.exit()"

Si ves un error como "A full buffer was not returned", vuelve a ejecutar el comando; un fallo aislado es esperable. Si todos los intentos fallan, revisa el cableado y la pull-up. En algunos modelos de Raspberry Pi y versiones del sistema operativo, el asistente de entrada de pulsos no funciona; en ese caso, crea el sensor con adafruit_dht.DHT22(board.D4, use_pulseio=False) para usar el método alternativo por bit-banging.

Payload y topics

El script publica un documento JSON por intervalo más un estado retenido:

{
  "temperature": 22.4,
  "humidity": 48.1,
  "samples": 3,
  "read_errors": 2,
  "ts": 1792152000
}
Topic Contenido Retenido
connectmq/demo/c3f19a/greenhouse/dht22 Lectura JSON cada 30 s No
connectmq/demo/c3f19a/greenhouse/status online / offline (last will) Sí

Sustituye c3f19a por una cadena aleatoria propia. Incluir read_errors en cada mensaje cuesta unos pocos bytes y convierte un problema de hardware silencioso en algo que puedes graficar y vigilar con alertas.

El script Raspberry Pi DHT22 MQTT

Guárdalo como dht22_mqtt.py:

#!/usr/bin/env python3
"""Read a DHT22 and publish validated readings to MQTT."""
import json
import os
import statistics
import time
import uuid

import adafruit_dht
import board
import paho.mqtt.client as mqtt

BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"dht22-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/c3f19a/greenhouse")
INTERVAL = int(os.getenv("PUBLISH_INTERVAL", "30"))
SAMPLES = 3
MAX_ATTEMPTS = 10

sensor = adafruit_dht.DHT22(board.D4)
total_errors = 0


def plausible(temp, hum):
    return (
        temp is not None and hum is not None
        and -40.0 <= temp <= 80.0
        and 0.0 <= hum <= 100.0
    )


def read_median():
    """Reúne SAMPLES lecturas válidas y devuelve sus medianas, o None."""
    global total_errors
    temps, hums = [], []
    attempts = 0
    while len(temps) < SAMPLES and attempts < MAX_ATTEMPTS:
        attempts += 1
        try:
            t, h = sensor.temperature, sensor.humidity
            if plausible(t, h):
                temps.append(t)
                hums.append(h)
            else:
                total_errors += 1
        except RuntimeError as err:
            # Los errores de checksum o de temporización son esperables de vez en cuando.
            total_errors += 1
            print(f"Read failed ({err}); retrying")
        time.sleep(2.2)
    if not temps:
        return None
    return statistics.median(temps), statistics.median(hums), len(temps)


def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"Connection refused: {reason_code}")
        return
    print(f"Connected as {CLIENT_ID}")
    client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
if USERNAME:
    client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
    client.tls_set()

client.connect(BROKER, PORT, keepalive=60)
client.loop_start()

try:
    while True:
        started = time.monotonic()
        result = read_median()
        if result is None:
            payload = {"error": "no valid reading", "read_errors": total_errors,
                       "ts": int(time.time())}
        else:
            temp, hum, n = result
            payload = {
                "temperature": round(temp, 1),
                "humidity": round(hum, 1),
                "samples": n,
                "read_errors": total_errors,
                "ts": int(time.time()),
            }
        info = client.publish(f"{PREFIX}/dht22", json.dumps(payload), qos=1)
        if info.rc != mqtt.MQTT_ERR_SUCCESS:
            print(f"Publish queued while offline (rc={info.rc})")
        print(payload)
        elapsed = time.monotonic() - started
        time.sleep(max(0, INTERVAL - elapsed))
except KeyboardInterrupt:
    pass
finally:
    client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
    client.loop_stop()
    client.disconnect()
    sensor.exit()

Conviene destacar algunos detalles. La espera de algo más de dos segundos entre lecturas respeta el límite de muestreo del sensor. La mediana de tres lecturas elimina el pico aislado ocasional que pasa el checksum pero sigue siendo erróneo. Cuando todos los intentos fallan, el script publica igualmente un mensaje con un campo error, para que los suscriptores puedan distinguir "sensor averiado" de "Pi desconectada", caso que cubre el topic de estado retenido. Por último, sensor.exit() libera la línea GPIO; omitirlo puede dejar el pin ocupado para la siguiente ejecución.

Ver los datos

Suscríbete desde tu portátil para confirmar que llegan los mensajes:

mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-c3f19a \
  -t 'connectmq/demo/c3f19a/greenhouse/#' -v

Deberías ver status online de inmediato (es retenido) y una lectura JSON aproximadamente cada 30 segundos. Desconecta la red de la Pi durante un minuto y vuelve a conectarla: paho se reconecta automáticamente. Los mensajes QoS 1 publicados mientras la conexión está caída se encolan en memoria y se envían tras la reconexión, siempre que el proceso siga en marcha. Si necesitas que las lecturas sobrevivan a un reinicio durante un corte, consulta cómo almacenar mensajes MQTT sin conexión en Python con SQLite.

Elegir el intervalo de publicación

La temperatura y la humedad de una habitación o un invernadero cambian despacio. Publicar cada 30 a 60 segundos da gráficos suaves sin saturar el broker ni tu base de datos. Ir más rápido que cada 10 segundos rara vez aporta información con un DHT22, y cada ciclo de lectura con reintentos ya tarda varios segundos. Si solo te interesan los cambios, publica cuando el valor varíe más de 0,2 °C o un 1 % de humedad, más un heartbeat cada pocos minutos.

La ubicación del sensor importa tanto como el intervalo. Monta el DHT22 lejos de la propia Pi, sin sol directo y alejado de calefactores o rejillas de ventilación, con aire circulando libremente por las aberturas de su carcasa. Un sensor pegado con cinta a la caja de la Pi medirá sobre todo el procesador. En un invernadero, un pequeño protector de radiación hecho con platos blancos apilados evita que el sol infle la lectura de temperatura.

DHT22 frente a otros sensores

El DHT22 está bien para monitoreo en interiores, pero no es la única opción. Una sonda sumergible DS18B20 es mucho más robusta si solo necesitas temperatura y admite muchas sondas en un solo cable; consulta la guía Raspberry Pi DS18B20 MQTT. Si prefieres usar el mismo sensor en un microcontrolador con batería, el tutorial ESP32 DHT22 MQTT usa un payload idéntico, así que ambos dispositivos pueden alimentar el mismo dashboard. Para una visión general de publicar, suscribirse y ejecutar scripts al arrancar, empieza con nuestro tutorial de MQTT con Raspberry Pi.

Pasar a un broker privado

El servidor MQTT público gratuito de broker.connectmq.com es perfecto para pruebas, pero todos sus topics son legibles por cualquiera. Para un invernadero o una sala de servidores reales, crea un broker privado en ConnectMQ, define las variables de entorno y reinicia:

export MQTT_PORT=8883
export MQTT_USERNAME=YOUR_USERNAME
export MQTT_PASSWORD=YOUR_PASSWORD
export MQTT_CLIENT_ID=YOUR_CLIENT_ID
export MQTT_PREFIX=site1/greenhouse/zone1
python dht22_mqtt.py

En el portal de ConnectMQ, que funciona como un broker MQTT gestionado, puedes ver las lecturas en el explorador de topics en vivo y crear una regla de alerta por umbral, por ejemplo humedad por encima del 85 %, sin escribir un suscriptor adicional.

Lista de comprobación para solucionar problemas

  1. Todas las lecturas fallan: comprueba que DATA está en GPIO4, que la pull-up está presente y que VCC es de 3,3 V.
  2. "Unable to set line to input" o GPIO ocupado: otro proceso tiene el pin; detén los scripts antiguos y asegúrate de que se ejecuta sensor.exit().
  3. Los valores parecen correctos pero saltan de vez en cuando: mantén el filtrado por mediana y sube SAMPLES a 5.
  4. Humedad atascada en 99,9: condensación en el sensor; a los DHT22 no les gusta la saturación constante.
  5. Las lecturas se detienen pero el estado sigue en online: el script se ha quedado bloqueado leyendo; añade un watchdog o reinícialo con systemd y Restart=always.
  6. Conexión rechazada, no autorizado: las credenciales o el client ID no coinciden con el broker.

Empieza con ConnectMQ

Lleva tu sensor del banco de pruebas a un despliegue real en un broker MQTT en la nube. Crea un broker Serverless gratuito, sin tarjeta de crédito, copia las credenciales generadas en tu entorno y conéctate por TLS en el puerto 8883. Tus lecturas del DHT22 estarán disponibles para dashboards, flujos de Node-RED y reglas de alerta en cuestión de minutos.

Preguntas frecuentes

¿Por qué mi DHT22 devuelve "Checksum did not validate" en una Raspberry Pi?

Linux interrumpe de vez en cuando la temporización precisa que necesita el DHT22, así que algunas lecturas fallan. Captura el error, espera dos segundos y reintenta; unos pocos fallos por hora son normales.

¿Con qué frecuencia puedo leer un DHT22?

No más de una vez cada dos segundos. Para publicar por MQTT, un intervalo de 30 a 60 segundos es más que suficiente para monitorear habitaciones e invernaderos.

¿Debo usar QoS 0 o QoS 1 para las lecturas de sensores?

QoS 1 es un buen valor por defecto porque garantiza la entrega con poca sobrecarga. QoS 0 es aceptable para lecturas muy frecuentes en las que perder una no importa.

¿Puedo conectar varios sensores DHT22 a una sola Raspberry Pi?

Sí: asigna a cada uno su propio pin GPIO y crea un objeto DHT22 por pin. Léelos uno tras otro en lugar de a la vez y publica cada uno en su propio topic.

Artículos relacionados

Fiabilidad de la Raspberry Pi: cómo usar una Pi 24/7 como gateway IoT

Una guía práctica para mantener un gateway MQTT con Raspberry Pi funcionando durante meses: desgaste del almacenamiento, sistemas de archivos de solo lectura, alimentación, refrigeración, watchdogs de hardware y software y monitorización remota de la salud.

9 min de lectura

Pico W vs Pico 2 W para MQTT: rendimiento, memoria y TLS de RP2040 frente a RP2350

La Pico 2 W mantiene el formato y el chip Wi-Fi de la Pico W, pero sustituye el RP2040 por el RP2350, más rápido. Esta comparativa analiza qué significa para los dispositivos MQTT: CPU, memoria, TLS, funciones de seguridad, soporte de software y qué placa comprar para cada proyecto.

9 min de lectura

Registro de accesos Raspberry Pi RFID MQTT con el lector RC522

Conecta un lector RFID RC522 a una Raspberry Pi, lee etiquetas en Python, compáralas con una lista blanca local y publica cada intento de acceso como un evento MQTT que pueden consumir registradores, dashboards y herramientas de alertas.

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.