ESP32 y Arduino

ESP32 DS18B20 MQTT: varias sondas de temperatura en un mismo bus

Conecta varias sondas DS18B20 a un único GPIO del ESP32, descubre sus direcciones ROM, léelas sin bloquear y publica cada sonda en su propio topic en broker.connectmq.com.

Tres sondas DS18B20 sumergibles compartiendo un cable hasta un ESP32 en un montaje de monitorización esp32 ds18b20 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 montaje ESP32 DS18B20 MQTT permite que un solo pin GPIO lea muchas sondas de temperatura sumergibles y publique cada una en su propio topic. En este tutorial conectarás varios sensores DS18B20 en un único bus 1-Wire, los identificarás por su dirección ROM, los leerás sin bloquear el cliente MQTT y publicarás en broker.connectmq.com con un topic por sonda.

Por qué el DS18B20 funciona bien para monitorizar varios puntos

El DS18B20 es un sensor de temperatura digital con una dirección ROM única de 64 bits grabada de fábrica. Como cada sonda tiene su propia dirección, decenas de ellas pueden compartir los mismos tres hilos: alimentación, tierra y datos. Eso lo hace ideal para tareas como:

  • Temperaturas de impulsión y retorno en un sistema de calefacción.
  • Varias bandejas de un refrigerador o una cámara frigorífica.
  • Lecturas del acuario, el estanque y el aire ambiente desde un solo controlador.
  • Vigilancia de puntos calientes en un pack de baterías o una envolvente.

Las versiones sumergibles de acero inoxidable son baratas y robustas, con una precisión de aproximadamente medio grado en el rango habitual. La contrapartida es la velocidad: una conversión completa de 12 bits tarda hasta 750 ms, y por eso las lecturas sin bloqueo importan en cuanto interviene MQTT.

Componentes y librerías

  • Una placa de desarrollo ESP32.
  • Dos o más sensores DS18B20 (TO-92 o sonda sumergible).
  • Una resistencia de 4,7 kOhm como pull-up del bus.
  • Arduino IDE 2.x con el paquete de placas esp32.
  • Librerías: OneWire (de Paul Stoffregen, o el fork mantenido que se incluye para ESP32), DallasTemperature de Miles Burton y PubSubClient.

Conectar varias sondas a un pin del ESP32

Todas las sondas se conectan en paralelo:

Hilo de la sonda (colores típicos) Se conecta a Notas
Rojo (VDD) 3V3 del ESP32 Usa alimentación normal, no el modo parásito
Negro (GND) GND del ESP32
Amarillo (DATA) GPIO 4 del ESP32 Una resistencia de 4,7 kOhm de DATA a 3V3 para todo el bus

Usa una sola resistencia pull-up para todo el bus, colocada cerca del ESP32. Los colores de los hilos varían según el proveedor, así que consulta la hoja de datos de tus sondas. Para tiradas de más de unos pocos metros, usa cable de par trenzado, mantén un bus lineal en lugar de en estrella y considera un pull-up más fuerte, como 2,2 kOhm.

Estructura de topics por sonda

Cada sonda publica en un topic que contiene su dirección ROM, así que los topics se mantienen estables sea cual sea el orden en que se enumere el bus:

Topic Retenido Payload de ejemplo
connectmq/demo/dsb2q6h/ds18b20/28ff641e8316034f/temperature Sí 21.62
connectmq/demo/dsb2q6h/ds18b20/summary No {"28ff641e8316034f":21.62,"28aa1b2c3d4e5f60":4.12}
connectmq/demo/dsb2q6h/status Sí online / offline

Los topics por sonda son retenidos para que un dashboard abierto más tarde muestre de inmediato el último valor de cada sonda. El topic de resumen es práctico para los consumidores que quieren un mensaje por ciclo. Ambos respetan los ocho niveles de topic permitidos en el área pública.

El sketch ESP32 DS18B20 MQTT

#include <WiFi.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>

const char* WIFI_SSID     = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST     = "broker.connectmq.com";
const uint16_t MQTT_PORT  = 1883;
const char* PREFIX        = "connectmq/demo/dsb2q6h";   // usa tu propio id aleatorio
const uint8_t ONE_WIRE_PIN = 4;
const uint8_t MAX_PROBES   = 10;
const uint32_t INTERVAL_MS = 10000;
const uint32_t CONVERSION_MS = 750;                       // resolución de 12 bits

OneWire oneWire(ONE_WIRE_PIN);
DallasTemperature sensors(&oneWire);
WiFiClient   net;
PubSubClient mqtt(net);

DeviceAddress probes[MAX_PROBES];
char probeIds[MAX_PROBES][17];
uint8_t probeCount = 0;
String clientId, topicStatus;

unsigned long lastRequest = 0;
bool conversionPending = false;

void addressToHex(const DeviceAddress addr, char* out) {
  for (uint8_t i = 0; i < 8; i++) sprintf(out + i * 2, "%02x", addr[i]);
  out[16] = '\0';
}

void discoverProbes() {
  sensors.begin();
  probeCount = 0;
  uint8_t found = sensors.getDeviceCount();
  for (uint8_t i = 0; i < found && probeCount < MAX_PROBES; i++) {
    if (sensors.getAddress(probes[probeCount], i)) {
      sensors.setResolution(probes[probeCount], 12);
      addressToHex(probes[probeCount], probeIds[probeCount]);
      Serial.printf("Probe %u: %s\n", probeCount, probeIds[probeCount]);
      probeCount++;
    }
  }
  sensors.setWaitForConversion(false);   // esperamos nosotros, sin bloquear
  Serial.printf("%u probe(s) found\n", probeCount);
}

void connectWifi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) delay(400);
  Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
}

void connectMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect(clientId.c_str(), topicStatus.c_str(), 1, true, "offline")) {
      mqtt.publish(topicStatus.c_str(), "online", true);
      Serial.println("MQTT connected");
    } else {
      Serial.printf("MQTT failed, state %d\n", mqtt.state());
      delay(5000);
    }
  }
}

void publishReadings() {
  String summary = "{";
  for (uint8_t i = 0; i < probeCount; i++) {
    float c = sensors.getTempC(probes[i]);
    bool valid = (c != DEVICE_DISCONNECTED_C) && (c != 85.0f);   // 85.0 = valor de encendido

    String topic = String(PREFIX) + "/ds18b20/" + probeIds[i] + "/temperature";
    char value[16];
    if (valid) snprintf(value, sizeof(value), "%.2f", c);
    else       snprintf(value, sizeof(value), "error");
    mqtt.publish(topic.c_str(), value, true);

    if (i) summary += ",";
    summary += "\"" + String(probeIds[i]) + "\":" + (valid ? String(value) : String("null"));
  }
  summary += "}";
  String topic = String(PREFIX) + "/ds18b20/summary";
  mqtt.publish(topic.c_str(), summary.c_str());
  Serial.println(summary);
}

void setup() {
  Serial.begin(115200);
  discoverProbes();

  clientId    = "esp32-ds18b20-" + String((uint32_t)ESP.getEfuseMac(), HEX);
  topicStatus = String(PREFIX) + "/status";

  connectWifi();
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setBufferSize(1024);   // espacio para un resumen de hasta 10 sondas
  connectMqtt();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();                                // sigue ejecutándose durante las conversiones

  unsigned long now = millis();
  if (!conversionPending && now - lastRequest >= INTERVAL_MS) {
    sensors.requestTemperatures();            // inicia la conversión en todas las sondas a la vez
    lastRequest = now;
    conversionPending = true;
  }
  if (conversionPending && now - lastRequest >= CONVERSION_MS) {
    conversionPending = false;
    publishReadings();
  }
}

Probar las sondas

Suscríbete a todo el prefijo:

mosquitto_sub -h broker.connectmq.com -p 1883 -i ds-view-2q6h -t "connectmq/demo/dsb2q6h/#" -v

Sujeta una sonda con la mano y observa cómo sube su topic mientras los demás se mantienen estables. Es también la forma más rápida de averiguar qué sonda física corresponde a cada dirección ROM: etiqueta cada cable sobre la marcha. Recuerda que el área pública de pruebas es visible para cualquiera, así que sirve para experimentos más que para una monitorización real; consulta la página del servidor MQTT público gratuito para conocer sus límites.

Cómo funciona la lectura sin bloqueo

requestTemperatures() envía un único comando de "conversión" a todas las sondas a la vez, así que diez sondas tardan los mismos 750 ms que una. Con setWaitForConversion(false) la llamada retorna de inmediato y el bucle sigue llamando a mqtt.loop() mientras las sondas trabajan. Solo cuando ha pasado el tiempo de conversión el sketch lee cada sonda por su dirección, lo que lleva unos pocos milisegundos por sonda.

Si no necesitas pasos de 0,0625 grados, reduce la resolución. A 10 bits la conversión tarda unos 188 ms y el paso pasa a ser de 0,25 grados, más que suficiente para supervisar una habitación o un refrigerador.

Dar nombres descriptivos a las sondas

Las direcciones ROM son estables pero no legibles. Dos enfoques funcionan bien:

  1. Asignarlos en el firmware. Mantén una pequeña tabla de dirección a nombre, como flow, return o freezer, y usa el nombre en el topic. Es sencillo, pero cada cambio de sonda implica volver a grabar el firmware.
  2. Asignarlos aguas abajo. Mantén la dirección en el topic y deja que el consumidor, por ejemplo un flujo de Node-RED o el dashboard, traduzca las direcciones a nombres. Sustituir una sonda implica entonces editar una asignación, no el firmware.

El segundo enfoque escala mejor. También encaja con la forma en que una pasarela Modbus RS485 suele publicar las identidades brutas de los registros y deja que una capa superior les asigne significado.

Detectar una sonda averiada o ausente

Una sonda que deja de informar en silencio es peor que una que informa de un error, porque un topic retenido sigue mostrando para siempre el último valor válido. El sketch lo gestiona de dos formas. Primero, publica la cadena error en el topic retenido de la sonda cuando la lectura devuelve -127 u 85, de modo que el número obsoleto se sobrescribe. Segundo, el mensaje de resumen contiene null para esa sonda, que la mayoría de consumidores tratan como un hueco y no como un cero.

Puedes ir más allá publicando el número de sondas encontradas al arrancar, por ejemplo en un topic retenido ds18b20/count, y compararlo con el número esperado en tu dashboard. Si se corta un cable, el recuento baja en el siguiente redescubrimiento y la diferencia salta a la vista. Para usos críticos, vigila también la antigüedad de los mensajes: si el topic de una sonda no ha cambiado en tres intervalos, considera que el dispositivo no está sano aunque su estado siga diciendo online.

Lista de comprobación para resolver problemas

Síntoma Causa Solución
0 sondas encontradas Falta el pull-up o pin incorrecto Añade 4,7 kOhm de DATA a 3V3 y comprueba ONE_WIRE_PIN
Lectura -127 Sonda desconectada o bus con ruido Revisa las uniones, acorta el cable, reduce el valor del pull-up
Lectura 85.0 Lectura antes de terminar la conversión o fallo de alimentación Mantén la espera de 750 ms y usa alimentación normal
Faltan sondas en tiradas largas Cableado en estrella o demasiada capacitancia Usa un bus lineal con par trenzado
Falta el mensaje de resumen Payload mayor que el búfer Aumenta setBufferSize
Las sondas solo aparecen tras reiniciar Sonda conectada en caliente Llama a discoverProbes() periódicamente o bajo comando

Configuración para producción

Cuando las sondas vigilan algo importante, como un congelador, pasa a un broker autenticado. Crea uno en app.connectmq.com, cambia a WiFiClientSecure en el puerto 8883 con el certificado ISRG Root X1 y conéctate con YOUR_CLIENT_ID, YOUR_USERNAME y YOUR_PASSWORD. En el portal de ConnectMQ, añade una regla de alerta por umbral en el topic de la sonda del congelador y tendrás una alarma básica de sobretemperatura sin escribir código de backend. Si mides consumo eléctrico además de temperatura, el monitor de energía con SCT-013 publica con una estructura compatible, y el proyecto ESP32 con DHT22 añade humedad en un único punto.

Empieza con ConnectMQ

La monitorización con varias sondas necesita un broker que esté siempre disponible cuando una sonda cruza un umbral. ConnectMQ es un broker MQTT gestionado con TLS, retained messages, explorador de topics en vivo y reglas de alerta, y el plan gratuito Serverless no requiere tarjeta de crédito. Apunta este sketch a tu propio broker y recorre el tutorial de IoT con ESP32 para tu siguiente proyecto.

Preguntas frecuentes

¿Cuántos sensores DS18B20 puede manejar un pin del ESP32?

El protocolo admite muchos dispositivos por bus, y de diez a veinte funcionan de forma fiable con un buen cableado y alimentación normal. La longitud del cable y la topología te limitan mucho antes que el espacio de direcciones.

¿Por qué un DS18B20 marca a veces 85 grados?

85.0 es el valor de reinicio del scratchpad al encenderse. Aparece cuando la lectura se produce antes de que termine una conversión, a menudo tras un fallo de alimentación. Trátalo como un error.

¿Debe tener cada sonda su propio topic MQTT?

Los topics retenidos por sonda simplifican los dashboards y mantienen disponible el último valor. Un mensaje de resumen por ciclo es un complemento útil para los consumidores que quieren todos los valores juntos.

¿Puedo usar alimentación parásita para ahorrarme un hilo?

Puedes, pero es menos fiable en tiradas largas y con muchas sondas. Usa alimentación normal de tres hilos siempre que sea posible.

Artículos relacionados

Ejemplo de ESP-IDF MQTT: crea un cliente esp-mqtt desde cero

Construye un proyecto ESP-IDF desde una carpeta vacía con el componente esp-mqtt: configuración de Wi-Fi en modo estación, el manejador de eventos MQTT, publicación y suscripción, propiedades de MQTT 5 y TLS con el bundle de certificados.

9 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.