DS18B20 Sensor Failure Detection – How to Detect Degrading or Unstable Sensors Early

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

Please see the introductory topic:

Detect degrading sensors in long-term operation

Occasional read errors are normal when working with DS18B20 sensors, especially in electrically noisy environments or long cable installations.

Typical error values include:

  • −127 °C → communication error
  • +85 °C → sensor startup value

The usual approach is to sanitize such readings and keep the system running safely.
Many systems even simply ignore occasional invalid readings or retry the measurement.

However, for systems that operate months or years, another question becomes relevant:

Is this sensor still healthy, or is it slowly degrading?

The approach described here tries to answer exactly that question by continuously evaluating the reliability of each sensor during normal operation.

When is Sensor Health Monitoring Useful?

Not every DS18B20 setup requires sensor health monitoring.

If your sensor is connected with a short cable in a quiet electrical environment and you rarely see invalid readings, the basic temperature reading is usually sufficient.

However, this approach becomes very useful in systems where sensors run unattended for long periods of time, for example:

  • heating or solar thermal systems
  • aquariums and environmental monitoring
  • industrial or agricultural installations
  • remote or hard-to-access locations
  • long cable runs or electrically noisy environments

In these situations occasional read errors are normal and usually not a problem by themselves.
What becomes important is detecting whether a sensor is gradually becoming less reliable over time.

The health monitoring presented here focuses exactly on that:
tracking long-term reliability trends while the system continues to operate normally.

To my knowledge this is the first lightweight health monitoring approach specifically designed for DS18B20 long-term deployments.

Details

A single error means nothing. But patterns of errors over time can reveal a sensor that is beginning to fail.

This module continuously evaluates such patterns and produces a simple result:

Sensor Health = 0 … 100 %
Health Interpretation
90–100 % excellent
75–90 % normal operation
60–75 % monitor
40–60 % consider replacement
<40 % replacement recommended

The monitoring works with minimal integration effort.


Quick Integration

Typical integration requires only two additional lines.

tempC = sensors.getTempC...                // as usual

float sanitized = sanitizeTemp(tempC);     // normalize errors

feedSafeTempModel(sanitized);              // optional runtime safety
feedSensorHealthMonitoring(sanitized);     // health monitoring

Whenever needed:

currentSensorHealth = getSensorHealth();

API Overview

initSensorHealthMonitoring();

feedSensorHealthMonitoring(sanitizedValue, sensorID); // sensorID optional

feedSensorHealthMonitoringExtraPenalty(penalty);      // affects all sensors
feedSensorHealthMonitoringExtraPenalty(penalty, sensorID);

getSensorHealth(sensorID);

Sanitizing Sensor Values

The monitoring module expects NaN for invalid readings.

float sanitizeTemp(float t)
{
    // sensor error code (exactly -127.0)
    if (t == -127.0) return NAN;

    // sensor start value (exactly 85.0, almost never a real temperature)
    if (t == 85.0) return NAN;

    // physically no valid values 
    // DS18B20 nominally: -55 to +125 °C (use at least this as default!)
    // depending on your application choose your limits for a plausibility check
    if (t < 10.0 || t > 90.0) return NAN;	// as an example!

    // just a valid value
    return t;
}

Configuration Parameters

Users may adjust the sensitivity via a few parameters.

#define ERROR_ALPHA                0.98
#define HEALTH_CLUSTER_THRESHOLD   3
#define HEALTH_CLUSTER_PENALTY     3
#define HEALTH_MAX_AUTO_PENALTY    1

ERROR_ALPHA

Controls how quickly old errors are forgotten.

Value Behavior
0.90 reacts very quickly
0.95 moderate
0.98 stable (recommended)
0.995 very tolerant

HEALTH_CLUSTER_THRESHOLD

Number of consecutive errors required to trigger a cluster penalty.

#define HEALTH_CLUSTER_THRESHOLD 3   // default
Value Sensitivity
2 Aggressive - small number of errors triggers penalty quickly
3 Balanced - default, moderate sensitivity
5 Mild - only repeated errors trigger penalty, more tolerant

HEALTH_CLUSTER_PENALTY

Penalty applied when such a cluster occurs.

#define HEALTH_CLUSTER_PENALTY 3   // default
Value Effect
2 Mild - small deduction per cluster
3 Balanced - default, moderate deduction
5 Aggressive - strong deduction per cluster
10 Extreme - very strong penalty for each cluster

HEALTH_MAX_AUTO_PENALTY

Limits automatic penalty per cycle.

#define HEALTH_MAX_AUTO_PENALTY 1

This prevents the health value from collapsing too quickly.


Manual Sensor Priority

Some sensors may be more critical than others.

Example:

Sensor Importance
Solar collector low
Hot water medium
Safety shutdown very high

If a critical sensor misbehaves, the user may apply additional penalties.

feedSensorHealthMonitoringExtraPenalty(2, 3);

Meaning:

Sensor #3 loses 2 additional health points

Example:

if (crcError)
{
    feedSensorHealthMonitoringExtraPenalty(3, 3);
}

Penalty Recommendation Table

Event Recommended Penalty
Bus reset detected 3–5
temporary sensor missing 5–8
CRC error 2–4
manual sensor priority user defined
major communication fault 5–10

Example:

feedSensorHealthMonitoringExtraPenalty(5);

Persistent Storage in EEPROM

Sensor degradation is a long-term process.
Therefore the health values should survive MCU resets and power loss.

This module periodically stores the health values in EEPROM.

However, EEPROM lifetime must be considered.

Typical Arduino EEPROM endurance:

100,000 write cycles per cell

To ensure extremely long lifetime, writes are performed only every few hours.


EEPROM Configuration

#define HEALTH_EEPROM_INTERVAL     21600000UL   // 6 hours
#define HEALTH_EEPROM_START_ADDR   0
#define HEALTH_EEPROM_MAGIC_ADDR   20
#define HEALTH_EEPROM_MAGIC_VALUE  0xA5

HEALTH_EEPROM_INTERVAL

Interval between EEPROM updates.

21600000 ms = 6 hours

Worst-case write count over 20 years:

4 writes per day
≈ 1460 writes per year
≈ 29200 writes in 20 years

This is far below the 100k EEPROM limit, providing a very large safety margin.

Users therefore do not need to worry about EEPROM wear.


Magic Pattern

When the system starts, the EEPROM content must be validated.

A magic pattern is used to detect whether valid health data is present.

0xA5

If the magic value is missing, the system assumes:

EEPROM contains no valid health data

and initializes the health values to 100%.


Integration Steps for Existing Sketches

The monitoring module was designed so that integration into existing projects requires only a few small steps.


1. Add the Module Code

Copy the Sensor Health Monitoring section from this article into your sketch.


2. Initialize the Monitoring System

In setup() add:

initSensorHealthMonitoring();

Example:

void setup()
{
    Serial.begin(9600);

    sensors.begin();

    initSensorHealthMonitoring();
}

3. Feed the Monitoring System

After reading the sensor value:

float tempC = sensors.getTempCByIndex(0);

float sanitized = sanitizeTemp(tempC);

feedSensorHealthMonitoring(sanitized);

For multiple sensors:

feedSensorHealthMonitoring(sanitized_3, 3);

If the Safe Temperature Model is already integrated

If your project already uses the Safe Temperature Model from the previous article, the integration is even simpler.

In that case the sanitized value already exists and is used for the model, so the health monitoring can be fed immediately afterwards.

Example:

float tempC = sensors.getTempCByIndex(0);

float sanitized = sanitizeTemp(tempC);

feedSafeTempModel(sanitized);          // existing safety model
feedSensorHealthMonitoring(sanitized); // health monitoring

4. Optional Manual Penalties

Whenever the system detects additional faults:

feedSensorHealthMonitoringExtraPenalty(3, sensorID);

Example:

if(busResetDetected)
{
    feedSensorHealthMonitoringExtraPenalty(5);
}

5. Read the Health Value

Whenever needed:

uint8_t health = getSensorHealth(0);

Complete Example Sketch (Ready to Compile)

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

#define ONE_WIRE_BUS 2

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

/* -------------------------------------------------------
   Configuration
------------------------------------------------------- */

#define MAX_SENSORS 4

#define ERROR_ALPHA                0.98
#define HEALTH_CLUSTER_THRESHOLD   3
#define HEALTH_CLUSTER_PENALTY     3
#define HEALTH_MAX_AUTO_PENALTY    1

#define HEALTH_EEPROM_INTERVAL     21600000UL
#define HEALTH_EEPROM_START_ADDR   0
#define HEALTH_EEPROM_MAGIC_ADDR   20
#define HEALTH_EEPROM_MAGIC_VALUE  0xA5

/* -------------------------------------------------------
   Global Variables
------------------------------------------------------- */

uint8_t sensorHealth[MAX_SENSORS];
uint8_t consecutiveErrors[MAX_SENSORS];
float errorRate[MAX_SENSORS];

unsigned long lastEEPROMWrite = 0;

/* -------------------------------------------------------
   sanitizeTemp()
------------------------------------------------------- */

float sanitizeTemp(float t)
{
    // sensor error code (exactly -127.0)
    if (t == -127.0) return NAN;

    // sensor start value (exactly 85.0, almost never a real temperature)
    if (t == 85.0) return NAN;

    // physically no valid values 
    // DS18B20 nominally: -55 to +125 °C (use at least this as default!)
    // depending on your application choose your limits for a plausibility check
    if (t < 10.0 || t > 90.0) return NAN;	// as an example!

    // just a valid value
    return t;
}

/* -------------------------------------------------------
   EEPROM Handling
------------------------------------------------------- */

void loadHealthFromEEPROM()
{
    if(EEPROM.read(HEALTH_EEPROM_MAGIC_ADDR) != HEALTH_EEPROM_MAGIC_VALUE)
    {
        for(int i=0;i<MAX_SENSORS;i++)
            sensorHealth[i] = 100;

        return;
    }

    for(int i=0;i<MAX_SENSORS;i++)
    {
        uint8_t v = EEPROM.read(HEALTH_EEPROM_START_ADDR + i);

        if(v <= 100)
            sensorHealth[i] = v;
        else
            sensorHealth[i] = 100;
    }
}

void saveHealthToEEPROM()
{
    for(int i=0;i<MAX_SENSORS;i++)
        EEPROM.update(HEALTH_EEPROM_START_ADDR + i, sensorHealth[i]);

    EEPROM.update(HEALTH_EEPROM_MAGIC_ADDR, HEALTH_EEPROM_MAGIC_VALUE);
}

/* -------------------------------------------------------
   Initialization
------------------------------------------------------- */

void initSensorHealthMonitoring()
{
    for(int i=0;i<MAX_SENSORS;i++)
    {
        sensorHealth[i] = 100;
        consecutiveErrors[i] = 0;
        errorRate[i] = 0;
    }

    loadHealthFromEEPROM();
}

/* -------------------------------------------------------
   Monitoring Input
------------------------------------------------------- */

void feedSensorHealthMonitoring(float value, uint8_t sensorID = 0)
{
    bool error = isnan(value);

    float newError = error ? 1.0 : 0.0;

    errorRate[sensorID] =
        errorRate[sensorID] * ERROR_ALPHA +
        newError * (1.0 - ERROR_ALPHA);

    if(error)
    {
        consecutiveErrors[sensorID]++;

        if(consecutiveErrors[sensorID] >= HEALTH_CLUSTER_THRESHOLD)
        {
            if(sensorHealth[sensorID] > HEALTH_CLUSTER_PENALTY)
                sensorHealth[sensorID] -= HEALTH_CLUSTER_PENALTY;
        }
    }
    else
    {
        consecutiveErrors[sensorID] = 0;
    }

    uint8_t penalty = (uint8_t)(errorRate[sensorID] * 20);

    if(penalty > HEALTH_MAX_AUTO_PENALTY)
        penalty = HEALTH_MAX_AUTO_PENALTY;

    if(sensorHealth[sensorID] > penalty)
        sensorHealth[sensorID] -= penalty;
}

/* -------------------------------------------------------
   Manual Penalties
------------------------------------------------------- */

void feedSensorHealthMonitoringExtraPenalty(uint8_t penalty)
{
    for(int i=0;i<MAX_SENSORS;i++)
        if(sensorHealth[i] > penalty)
            sensorHealth[i] -= penalty;
}

void feedSensorHealthMonitoringExtraPenalty(uint8_t penalty, uint8_t sensorID)
{
    if(sensorHealth[sensorID] > penalty)
        sensorHealth[sensorID] -= penalty;
}

/* -------------------------------------------------------
   Query Health
------------------------------------------------------- */

uint8_t getSensorHealth(uint8_t sensorID = 0)
{
    return sensorHealth[sensorID];
}

/* -------------------------------------------------------
   Setup
------------------------------------------------------- */

void setup()
{
    Serial.begin(115000);

    sensors.begin();

    initSensorHealthMonitoring();
}

/* -------------------------------------------------------
   Loop
------------------------------------------------------- */

void loop()
{
    sensors.requestTemperatures();

    float tempC = sensors.getTempCByIndex(0);

    float sanitized = sanitizeTemp(tempC);

    feedSensorHealthMonitoring(sanitized);

    if(millis() - lastEEPROMWrite > HEALTH_EEPROM_INTERVAL)
    {
        saveHealthToEEPROM();
        lastEEPROMWrite = millis();
    }

    uint8_t health = getSensorHealth();

    Serial.print("Temp: ");
    Serial.print(tempC);

    Serial.print(" Health: ");
    Serial.print(health);
    Serial.println("%");

    delay(2000);
}

Result

Combined with the introduced Safe Temperature Model, this approach provides:

sanitized sensor readings
fail-safe temperature modeling
long-term sensor health monitoring
persistent degradation tracking

This significantly improves reliability for long-running installations and noisy sensor networks. +++ Use at you own risk! +++

Feedback and Long-Term Testing

This monitoring system is designed to evaluate sensor behavior over very long periods of time.
Because of that, changes in the reported Sensor Health value are intentionally slow. In normal operation it may take many hours, days, or even weeks before any noticeable degradation appears.

Please keep this in mind when testing the system. Short experiments usually will not show meaningful health changes, which is by design — the algorithm is meant to detect long-term reliability trends, not temporary glitches.

If you try this module in your own project, your feedback would be extremely valuable. In particular, it would help the community if you could share:

  • your application scenario (number of sensors, cable lengths, environment, etc.)
  • how often you typically see invalid readings
  • whether you are using extra penalties and under which conditions
  • how you tuned the health parameters
  • any interesting long-term behavior you observe

Since many installations run continuously for months or years, real-world feedback from different setups is very helpful for understanding how the algorithm behaves under diverse conditions.

Think of this as a community field test. Even small observations can help improve recommendations for parameter settings and usage patterns.

Thank you to everyone who takes the time to test this and report back!

My experience with the device is it never continues after an error! Only a power off/on will start it’s processor over again.

Seems like a rather presumptuous statement. "Usual" by what standard?

Thanks for the post, it will help a lot. Following is based on my experiences over the years.

The biggest problem I have found is in the wiring and its associated capacitance. I also never use a star configuration, as it creates problems. Long stubs are bad and can cause excessive reflections. I like a pull-up resistor in the 3.3K range for a 5V system. I have also come across cable that degrades over time, increasing its capacitance and causing scrambled transfers.

The expected life is in the range of 20-30+ years in normal environments if it does not experience moisture intrusion, corrosion, cable damage, ESD, over-voltage on the bus, or poor waterproof probes.

This post serves as the official Solution of DS18B20 Sensor Health Monitoring – Detect Degrading Sensors Early (former title). For the full content, see my original post at the very beginning of this thread.