ESP32 and Arduino

ESP32 DS18B20 MQTT: Multiple Temperature Probes on One Bus

Wire several DS18B20 probes to a single ESP32 GPIO, discover their ROM addresses, read them without blocking and publish each probe to its own topic on broker.connectmq.com.

Three waterproof DS18B20 probes sharing one wire to an ESP32 in an esp32 ds18b20 mqtt temperature monitoring setup
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

An ESP32 DS18B20 MQTT setup lets one GPIO pin read many waterproof temperature probes and publish each to its own topic. In this tutorial you will wire several DS18B20 sensors on one 1-Wire bus, identify them by ROM address, read them without blocking the MQTT client, and publish to broker.connectmq.com with a topic per probe.

Why the DS18B20 Works Well for Multi-Point Monitoring

The DS18B20 is a digital temperature sensor with a unique 64-bit ROM address burned in at the factory. Because every probe has its own address, dozens can share the same three wires: power, ground and data. That makes it ideal for jobs like:

  • Flow and return temperatures on a heating system.
  • Several shelves in a fridge or cold room.
  • Aquarium, pond and ambient air readings from one controller.
  • Battery pack or enclosure hot-spot monitoring.

The stainless-steel waterproof versions are cheap and robust, with an accuracy around half a degree over the typical range. The trade-off is speed: a full 12-bit conversion takes up to 750 ms, which is why non-blocking reads matter once MQTT is involved.

Parts and Libraries

  • An ESP32 development board.
  • Two or more DS18B20 sensors (TO-92 or waterproof probe).
  • One 4.7 kOhm resistor for the bus pull-up.
  • Arduino IDE 2.x with the esp32 board package.
  • Libraries: OneWire (by Paul Stoffregen, or the maintained fork bundled for ESP32), DallasTemperature by Miles Burton, and PubSubClient.

Wiring Several Probes to One ESP32 Pin

All probes connect in parallel:

Probe wire (typical colours) Connects to Notes
Red (VDD) ESP32 3V3 Use normal power, not parasite mode
Black (GND) ESP32 GND
Yellow (DATA) ESP32 GPIO 4 One 4.7 kOhm resistor from DATA to 3V3 for the whole bus

Use only one pull-up resistor for the bus, placed near the ESP32. Wire colours vary between suppliers, so check the datasheet of your probes. For runs longer than a few metres, use twisted-pair cable, keep a linear bus rather than a star, and consider a stronger pull-up such as 2.2 kOhm.

Topic Layout per Probe

Each probe publishes to a topic containing its ROM address, so topics stay stable no matter in which order the bus is enumerated:

Topic Retained Example payload
connectmq/demo/dsb2q6h/ds18b20/28ff641e8316034f/temperature Yes 21.62
connectmq/demo/dsb2q6h/ds18b20/summary No {"28ff641e8316034f":21.62,"28aa1b2c3d4e5f60":4.12}
connectmq/demo/dsb2q6h/status Yes online / offline

The per-probe topics are retained so a dashboard opened later immediately shows the last value of every probe. The summary topic is convenient for consumers that want one message per cycle. Both stay within the eight topic levels allowed on the public area.

The ESP32 DS18B20 MQTT Sketch

#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";   // use your own random id
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;                       // 12-bit resolution

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);   // we wait ourselves, without blocking
  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 = power-on value

    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);   // room for a summary of up to 10 probes
  connectMqtt();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) connectWifi();
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();                                // keeps running during conversions

  unsigned long now = millis();
  if (!conversionPending && now - lastRequest >= INTERVAL_MS) {
    sensors.requestTemperatures();            // starts conversion on all probes at once
    lastRequest = now;
    conversionPending = true;
  }
  if (conversionPending && now - lastRequest >= CONVERSION_MS) {
    conversionPending = false;
    publishReadings();
  }
}

Testing the Probes

Subscribe to the whole prefix:

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

Hold one probe in your hand and watch its topic climb while the others stay flat. That is also the quickest way to figure out which physical probe owns which ROM address: label each cable as you go. Remember that the public test area is visible to anyone, so it suits experiments rather than real monitoring; see the free public MQTT broker page for its limits.

How the Non-Blocking Read Works

requestTemperatures() sends one "convert" command to every probe simultaneously, so ten probes take the same 750 ms as one. With setWaitForConversion(false) the call returns immediately, and the loop keeps calling mqtt.loop() while the probes work. Only after the conversion time has passed does the sketch read each probe by address, which takes a few milliseconds per probe.

If you do not need 0.0625 degree steps, lower the resolution. At 10 bits the conversion takes about 188 ms and the step size becomes 0.25 degrees, which is plenty for room or fridge monitoring.

Giving Probes Friendly Names

ROM addresses are stable but not readable. Two approaches work well:

  1. Map in the firmware. Keep a small table of address to name, such as flow, return or freezer, and use the name in the topic. Simple, but every probe swap means a reflash.
  2. Map downstream. Keep the address in the topic and let the consumer, for example a Node-RED flow or the dashboard, translate addresses to names. Replacing a probe then means editing one mapping, not firmware.

The second approach scales better. It also matches how a Modbus RS485 gateway usually publishes raw register identities and lets a higher layer assign meaning.

Detecting a Failed or Missing Probe

A probe that silently stops reporting is worse than one that reports an error, because a retained topic keeps showing the last good value forever. The sketch handles this in two ways. First, it publishes the string error to the probe's retained topic when the read returns -127 or 85, so the stale number is overwritten. Second, the summary message contains null for that probe, which most consumers treat as a gap rather than a zero.

You can go further by publishing the number of probes found at boot, for example to a retained ds18b20/count topic, and comparing it with the expected number in your dashboard. If a cable is cut, the count drops at the next rediscovery and the difference is obvious. For critical uses, also watch message age: if a probe topic has not changed for three intervals, treat the device as unhealthy even if its status still says online.

Troubleshooting Checklist

Symptom Cause Fix
0 probes found Missing pull-up or wrong pin Add 4.7 kOhm DATA to 3V3, check ONE_WIRE_PIN
Reading -127 Probe disconnected or noisy bus Check joints, shorten cable, lower pull-up value
Reading 85.0 Read before conversion finished, or power glitch Keep the 750 ms wait, use normal power
Some probes missing on long runs Star wiring or too much capacitance Use a linear bus with twisted pair
Summary message missing Payload larger than buffer Increase setBufferSize
Probes found only after reboot Hot-plugged probe Call discoverProbes() periodically or on command

Production Setup

When the probes guard something that matters, such as a freezer, move to an authenticated broker. Create one at app.connectmq.com, switch to WiFiClientSecure on port 8883 with the ISRG Root X1 certificate, and connect with YOUR_CLIENT_ID, YOUR_USERNAME and YOUR_PASSWORD. In the ConnectMQ portal, add a threshold alert rule on the freezer probe topic and you have a basic over-temperature alarm without writing backend code. If you measure power alongside temperature, the SCT-013 energy monitor publishes in a compatible layout, and the ESP32 DHT22 project adds humidity at a single point.

Get Started with ConnectMQ

Multi-probe monitoring needs a broker that is always there when a probe crosses a threshold. ConnectMQ is a fully managed MQTT service with TLS, retained messages, a live topic explorer and alert rules, and the free Serverless plan needs no credit card. Point this sketch at your own broker and browse the ESP32 IoT tutorial hub for the next project.

Frequently Asked Questions

How many DS18B20 sensors can one ESP32 pin handle?

The protocol allows many devices per bus, and ten to twenty work reliably with good wiring and normal power. Cable length and topology limit you long before the address space does.

Why does a DS18B20 sometimes read 85 degrees?

85.0 is the power-on reset value of the scratchpad. It appears when the reading happens before a conversion completes, often after a power glitch. Treat it as an error.

Should each probe have its own MQTT topic?

Per-probe retained topics make dashboards simple and keep the last value available. A summary message per cycle is a useful addition for consumers that want all values together.

Can I use parasite power to save a wire?

You can, but it is less reliable on long runs and with many probes. Use three-wire normal power whenever possible.

Related articles

ESP-IDF MQTT Example: Build an esp-mqtt Client from Scratch

Build an ESP-IDF project from an empty folder with the esp-mqtt component: Wi-Fi station setup, the MQTT event handler, publish and subscribe, MQTT 5 properties and TLS with the certificate bundle.

8 min read

Using esp-mqtt MQTT 5 Features on ESP32 with ESP-IDF

A hands-on ESP-IDF project that enables MQTT 5.0 in esp-mqtt and uses user properties, message expiry, topic aliases, session expiry and response topics against a real MQTT 5 broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.