DS18B20 Conversion Time Problems – Visualize Sensor Timing with LED (Debug Method)

This is article 8 of 8 in a series about robust DS18B20 / 1-Wire system design.

Please see the introductory topic:

This is a short but very useful technique: a small trick that helps visualize the DS18B20 conversion time, which can take up to 750 ms at 12-bit resolution.

Many Arduino users are familiar with the classic heartbeat LED:
a small status blink that indicates the microcontroller is still running.

This is particularly helpful in systems that have:

  • no display

  • no serial monitor connected

  • no other visible output

A simple heartbeat reassures you that the firmware is alive.


The Classic Heartbeat LED

Typically the LED is toggled every few seconds.

Example (simplified):

pinMode(LED_BUILTIN, OUTPUT);
static unsigned long lastBlink = 0;

if (millis() - lastBlink >= 2000)
{
    digitalWrite(LED_BUILTIN, HIGH);
    delay(50);
    digitalWrite(LED_BUILTIN, LOW);
    lastBlink = millis();
}

This produces a short blink every two seconds.

It confirms that the main loop is still executing.

However, when working with a DS18B20 temperature sensor in blocking mode, the LED can provide even more useful information.


Using the DS18B20 Conversion Time as Optical Feedback

At 12-bit resolution, the DS18B20 conversion takes up to:

750 ms

During this time the processor is waiting for the sensor to complete the measurement.

Instead of letting this time pass silently, it can be used as a visual indicator.

The idea is simple:

  • turn the LED ON before starting the conversion

  • turn the LED OFF after reading the temperature

This means the LED stays on exactly during the conversion time.

Example sequence:

LED ON
requestTemperatures()
getTempCByIndex()
LED OFF

The LED therefore becomes a visual representation of sensor activity.


Why This Is Useful

This technique provides immediate visual feedback about sensor behavior.

Under normal conditions:

  • the LED stays on for roughly the conversion time

  • the blink pattern is stable and predictable

But if the sensor experiences problems, the LED pattern changes.

For example:

  • repeated reads

  • recovery attempts

  • additional conversion requests

All of these extend the LED ON time.

This makes it possible to see recovery behavior directly without using the serial monitor.

If an escalation strategy is implemented, the LED can visually reveal:

  • additional conversion attempts

  • retry loops

  • bus resets

  • longer recovery cycles

In other words:

The LED becomes a simple diagnostic instrument.


OFF-Time Behavior

The LED OFF period is determined by whatever else the loop is doing.

Typical contributors are:

  • other code inside the loop

  • communication routines

  • actuator control

  • or simply a delay() used to slow down the loop.

This creates a natural timing pattern:

LED ON  → sensor conversion
LED OFF → rest of the program

The result is a clear visual separation between measurement time and application logic.


Minimal Example

The following example demonstrates the concept with a DS18B20 running at 12-bit resolution in blocking mode.

It is intentionally kept minimal and fully compilable.

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

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);

    Serial.begin(115200);
    sensors.begin();
    sensors.setResolution(12);
}

void loop()
{
    digitalWrite(LED_BUILTIN, HIGH);     // LED ON: conversion starts

    sensors.requestTemperatures();       // DS18B20 blocking conversion (up to ~750 ms at 12-bit)
    float temp = sensors.getTempCByIndex(0);

    // https://forum.arduino.cc/t/ds18b20-recovery-and-escalation-strategy/1433190

    digitalWrite(LED_BUILTIN, LOW);      // LED OFF: conversion finished

    Serial.print("Temperature: ");
    Serial.println(temp);

    delay(1000);                         // slow down loop
}

What You Will Observe

With this setup:

  • the LED stays ON for ~750 ms

  • the LED stays OFF during the rest of the loop

If the sensor behaves normally, the blink pattern remains stable.

If problems occur (slow responses, retries, recovery attempts), the ON time becomes longer.

This provides a surprisingly effective visual debugging tool.


A Small Trick with Big Value

This technique costs:

  • one LED

  • two lines of code

Yet it can reveal a lot about the timing behavior of a DS18B20 system.

Especially during development, it offers an immediate visual indication of:

  • sensor timing

  • loop timing

  • recovery behavior

  • abnormal delays

This simple LED technique can help understand DS18B20 timing behavior and detect unusual conversion delays during debugging. => Sometimes the simplest diagnostics are the most effective ones.

So many words just for the sake of saying that we can light up the LED while measuring temperature? :)

And by the way, why even do you use DS18B20 in blocking mode?

Does this also fall under the "Use at your own risk" disclaimer?

Thanks for the comment.

Yes, lighting an LED during a measurement is technically a very simple idea.

The reason for explaining it in a bit more detail is that the post is aimed mainly at beginners who are still trying to understand what actually happens during the DS18B20 conversion phase and how sensor timing affects the main loop.

Using the LED simply turns the otherwise invisible conversion and recovery time into something directly observable, while still serving as a heartbeat indicator.

Regarding blocking mode: it is intentionally used here because many beginners start with it, and it makes the timing behavior very easy to demonstrate.

Sometimes simple visual tools help people understand system timing much faster than just reading about it …

Of course! :slight_smile:

a heartbeat can indicate a variety of states, ledMode depending on its timing

struct LedPeriod {
    unsigned long   period [2];
};
LedPeriod ledPeriod [] = {
    { 1900, 100 },
    {   50, 200 },
    {  100, 150 },
};

int ledMode = 0;

void
ledStatus ()
{
    static unsigned long msecLst;
    int state = digitalRead (PinLedGrn);
    if (msec - msecLst >= ledPeriod [ledMode].period [state])  {
        msecLst = msec;
        digitalWrite (PinLedGrn, ! state);
    }
}

Then why would anyone trust any of the code you have presented here to date. When I see “use at your own risk” it’s a good indication that the author has not actually tested or used the code they presented and cannot guarantee its correctness.

However, this does not indicate that the MCU is active during this conversion time. An alternative approach is to have the MCU toggle the onboard LED while the DS18B20 is still performing its temperature conversion. (codes work; but, the LED does not toggle visibly in that window!)

  ds.reset();
  while (ds.read() != 0xFF)  //bus value remains LOW until converson is done
  {
    while (millis() - lastMillis < 100)
    {
      lastState = !lastState;
      digitalWrite(13, lastState);
    }
    lastMillis = millis();
  }

Output:

Temperature = 30.50 Celsius, 86.90 Fahrenheit

Describing it as "useful" is a big stretch. It's just another delay where the rest of your program is doing nothing.

Right! Much better to use the sensor in non-blocking mode and allow your code to continue working while the DS18B20 takes the measurement and does the A2D conversion. During this time, one of the "useful" things your code could be doing is blinking a heartbeat LED using millis(), not delay().

Also, the "get by index" family of functions are very inefficient. Each one requires two (slow) transactions on the OneWire bus -- the first to find the OneWire address associated with the index and the second to access the particular sensor via that address. It's better to get the sensor address(es) once in the setup() function and use them directly.

Both of these problems with your code are solved here:

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

uint32_t waitTime;
uint32_t timer;
constexpr uint8_t busPin = 2;

OneWire oneWire(busPin);
DallasTemperature sensor(&oneWire);
DeviceAddress sensorAddress;

void setup() {
  Serial.begin(115200);
  sensor.setWaitForConversion(false);
  sensor.begin();
  if (!sensor.getAddress(sensorAddress, 0)) {
    Serial.println("Unable to get sensor address");
    while (true) {
      delay(10);
    }
  }
  if (!sensor.setResolution(sensorAddress, 12, true)) {
    Serial.println("Unable to set sensor resolution");
    while (true) {
      delay(10);
    }
  }
  waitTime = sensor.millisToWaitForConversion(sensor.getResolution());
  sensor.requestTemperatures();
  timer = millis();
}

void loop() {
  uint32_t currentTime = millis();
  if (currentTime - timer >= waitTime) {
    float sensorTemp = sensor.getTempC(sensorAddress);
    Serial.println(sensorTemp);
    sensor.requestTemperatures();
    timer = currentTime;
  }

  // Do useful stuff here while waiting for sensor to take reading

}

Thanks for the example — I really appreciate a factual discussion. Exchanges like this help everyone understand the topic a bit better.

Using the sensor in non-blocking mode is definitely a good approach when the goal is to keep the main loop responsive.

In this particular post the blocking call was used intentionally because it makes the conversion phase and any additional recovery time directly observable via the LED.
The underlying goal is simply to make it easier to understand how the sensor behaves when trying to obtain valid readings in an electrically noisy environment.
That makes it easier for beginners to understand what the sensor is actually doing during the measurement cycle.

For real applications both approaches have their place depending on whether the focus is on loop responsiveness or on analyzing sensor behavior and timing.

When would a user need a display AND and a blinking LED to indicate the display was being updated? Sounds like a brother-in-law has an LED business.

Exactly has does the LED solve the noise problem or even indicate a noise problem?

The LED is not meant to replace a display.
As mentioned at the beginning of the post, the idea is mainly for systems that have no display, no serial monitor, and no other visible output.
In those cases a simple LED can still act as a heartbeat indicator showing that the loop is running, and in this example it also makes the otherwise invisible conversion and recovery time of the sensor directly observable.

I would want the thermometer to be verified, not a loop.

Two examples, the temperature being measured :

  1. does not change for six months.
  2. changes every reading.

I do not care that the loop is running. I care that the thermometer is working.

How much attention does a "not blinking" LED get? Do fire trucks drive around with lights flashing, and turn the lights off in an emergency?

I use a similar approach for timing analysis. A GPIO pin (typically pin 7) is configured as a diagnostic “dead-dog” indicator. The pin is asserted when entering the function under test and deasserted on exit. An oscilloscope probe is connected to this pin so the execution duration and any timing variations can be observed directly on the scope. If the function stalls or exceeds the expected execution time, the dead-dog mechanism triggers a system reset, which is recovered by cycling power. Once debugging and timing verification are complete, the dead-dog functionality is re-enabled for normal operation.

STATEMENT

As convenient as the DS18B20 is, the long-term reliability of these sensors is often a bit overestimated in real installations. It’s not “just a sensor,” as someone once commented somewhere — well, maybe in their world it is :slightly_smiling_face:. In many projects they work perfectly at the beginning, but after some years of continuous operation read errors tend to appear more frequently. That observation is actually one of the reasons why I started this article series: DS18B20 Wrong Readings (85°C / -127°C) – Reliable Fix and Robust 1-Wire Design

In my view this has less to do with the electronics itself and more with the real-world environment the sensors are exposed to. In this particular case an electrically noisy environment was excluded as a factor, but bus noise can still be a major source of intermittent problems in many installations — even with brand new sensors.

Depending on the installation, typical lifetimes seem to be roughly in this range:

  • TO-92 in free air: about 10–20 years (from literature)
  • Cheap stainless-steel probe versions: about 2–5 years (own experience)

Especially in humid environments (e.g. hot-water tanks, boiler rooms, outdoor installations), moisture can slowly penetrate the assembly or migrate along the cable. At least to me that explanation sounds plausible, and it would also explain why sporadic communication problems may occur long before a sensor finally fails completely.

Because of that, it can be useful to think about robustness in the sketch from the very beginning, for example by implementing:

At first this might look unnecessary — often one is just happy when the system finally runs at all. But with increasing runtime it can help to keep the system stable and gracefully handle aging sensors.

Of course anyone is free to ignore what I’ve posted so far. But in projects that are expected to run for years in the real, wet, dirty world — where nobody treats the hardware gently (think of equipment in a stable being sprayed with water, for example) — failures will eventually happen.

So no, this isn’t coming from an ivory tower perspective.

A typical application for me — for example a pipe heating system that prevents a water pipe from freezing with minimal energy — would look something like this:

A solid, properly sealed and grounded aluminium enclosure with exactly four well-sealed openings:

  • power in
  • switched power out
  • one sensor cable
  • one LED that tells me everything I need to know about the system state

I also don’t need overly complex code that I won’t even understand myself a month later.

The system simply needs to do its dedicated job with maximum reliability — and under no circumstances become a hazard!

@ gfvalvo

Some additional thoughts regarding the points you raised:

  1. “It’s just another delay where the rest of your program is doing nothing.”
    – If you mean the conversion time, you are correct — the loop is technically waiting. However, that was not the point here. The intent is to make the conversion and any recovery behavior directly observable.
  2. “Why even use DS18B20 in blocking mode?”
    – In many cases, the blocking call is sufficient and easier to demonstrate, especially when teaching sensor behavior or timing issues. Non-blocking code becomes essential mainly in multi-sensor systems or when bus timing and responsiveness are critical. In my applications, each sensor often has its own 1-Wire bus for reliability, and there non-blocking code provides a real advantage.
  3. Inefficiency of the “get by index” functions
    – I understand the technical point, but I prioritize flexibility. Using the sensor address directly in code is optimal, but "sensors.getTempCByIndex(0)" is most important for me because I can even ask someone to get a new sensor from the shelf and swap it whithout touching the code!
  4. Timing with “currentTime - timer >= waitTime”
    – This approach can be risky in multi-sensor setups. Not every sensor may be ready simultaneously, which could lead to sporadic failures. In critical applications, it’s safer to check whether the conversion has actually completed on each sensor before reading.

Overall, the goal of this post is educational: to visualize sensor timing and recovery behavior. For production code, of course, non-blocking approaches and proper multi-sensor handling should be applied.

No, it becomes essential when the processor needs to be getting other things done to support your application and you're letting the DS18B20 waste your processor's cycles.

But you have absolutely no guarantee that the new sensor will have the same index as the one being replaced. The indices are determined by the order of the OneWire addresses. The more likely outcome is that the indices of some or all of the existing sensors will be changed depending on where the new one is inserted in the sequence.

That's not correct. First, sensor.requestTemperatures() sends a global command to all sensor simultaneously (rather than addressing them individually). Thus, they all start their conversion at the same time (within timing tolerances). Second sensor.setResolution(sensorAddress, r) where "r" is the resolution in bits, causes the library to record a "global resolution" that is the maximum value across all sensors. So sensor.getResolution() will return the longest wait time required. Finally, checking "whether the conversion has actually completed" involves another slow OneWire transaction. Again, that's wasteful the when the processor can be doing other things. The datasheet specifies the maximum time required for the conversion. It will be done by then.

On contrary, if there is only one device on a one-wire bus it will always have the index(0).
My library - GitHub - RobTillaart/DS18B20_RT: Arduino library for the DS18B20 sensor - restricted to one sensor per pin. · GitHub
uses exact this argument for one device per pin.

My statement was correct:

Obviously, a single device will always be at Index 0. I was commenting on the general case of having multiple devices per OneWire bus. It's not much of a "bus" otherwise.