DS18B20 Temperature Sensor

temperature modules

Want an easy-to-use digital temperature sensor? The DS18B20 is a popular alternative to analog temperature modules.

The DS18B20 Temperature Sensor

The DS18B20 is a digital temperature sensor from Dallas Semiconductors which is now part of Maxim. The sensor provides an accuracy of about ± 0.5 °C and can work in a temperature range from - 55 °C up to 125 °C. It uses the 1-Wire bus for data transmission. This bus was developed by Dallas Semiconductors too and as the name suggests it requires only a single data line next to the power supply lines. If one wants to take it to the extreme the supply voltage can also be supplied via the data line. This is also called parasite power mode. In this mode you only need two wires in total. We will not cover this mode in this tutorial however. The Arduino modules typically use three pins with a separate line for power supply.

Each DS18B20 has a unique 48-bit address which allows to use a shear unlimited amount of sensors on the same 1-Wire bus. The only downside is that the communication speed is pretty slow. For temperature sensing, however, this doesn't really matter. Temperature does usually not change that fast and the DS18B20 itself isn't fast either. A measurement at full 12-bit resolution takes around 750 ms. For faster measurements the resolution can be reduced in 1-bit steps down to 9-bit. At 9-bit resolution a measurement takes around 100 ms. With the library we use in this tutorial one can set the resolution by e.g. calling sensors.setResolution(9) for 9-bit resolution. In this tutorial we will stick to the default 12-bit resolution and not aim for speed. Be aware, that the function needs to be called again, if new sensors are added to the bus at runtime.

Different DS18B20 modules

There are multiple types of DS18B20 sensors available. First there are ready-made modules like the KY-001 shown on the left. These modules already contain the required components to drive the 1-Wire bus. They are ready for use with the Arduino. On the other hand, there are sole DS18B20 sensors in different packages, e.g. in a transistor like TO-92 package like the one that is used on the module or in waterproof enclosure like the one shown on the right.

If you want to measure the temperature of liquids, I strongly suggest you to use a waterproof version or else the sensor will be quickly damaged by corrosion. To use a DS18B20 that does not come on a ready-made module, you need an additional 4.7 kΩ resistor that is used as pullup for the 1-Wire bus data line. The picture below shows how to build the required circuit on a breadboard.

Wiring of a DS18B20 that does not come on a ready-made module

Using a single DS18B20 with the Arduino

Let's start by using a single module together with the Arduino. In case you have a separate DS18B20 sensor without module, use the circuit with external pullup resistor shown in the last section. If you have a ready-made module, you just need three jumper wires. I connected the data line to pin 8 on the Arduino. On the module it is marked with an S. It is not required to use pin 8, any pin will do the job. Of course, you need to adjust the code, if you want to use another pin for the data line. Next we need to connect the power lines. Connect GND to the left pin that is marked with - and 5V to the middle pin, just like it is shown in the picture.

Wiring the DS18B20 module to the Arduino

To be able to use the module with the Arduino, we will make use of the DallasTemperature library. If you have not already done so, you need to install it. Just open the library manager under Tools > Manage Libraries ... and search for the sensor. Then select the DallasTemperature library and install it. You will be asked to install the OneWire library alongside the DallasTemperature library. The OneWire library is required to use the DallasTemperature library. It provides the needed functions for using the 1-wire bus.

Installing the DallasTemperature Library

In this tutorial we will print out the temperature value to the serial monitor. Right at the top of our program we need to include the required libraries and create a 1-wire bus instance for pin 8 and pass it to the DallasTemperature library:

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

// Sensor at pin 8
OneWire bus(8);
DallasTemperature sensors(&bus);

In loop we need to do two things: start the measurement and then read the measured value from the sensor. We can start the measurement by calling the requestTemperatures method. In our case we only want to use the first sensor on the 1-wire bus. We can limit our request to the first sensor by calling requestTemperaturesByIndex with index 0. To read out the measurement value, we can call getTempCByIndex for the temperature in degree Celsius or getTempFByIndex for the temperature in degree Fahrenheit.

The following code prints out these values to the serial console roughly every 5 seconds (more like every 6 seconds, because of the time the sensors needs to perform the measurement and transfer the data via the 1-wire bus):

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

// Sensor at pin 8
OneWire bus(8);
DallasTemperature sensors(&bus);

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

void loop() {
  // Start the temperature measurement and wait for it
  sensors.requestTemperaturesByIndex(0);

  // Read and show the result
  Serial.print("Temperature (°C): ");
  Serial.println(sensors.getTempCByIndex(0));
  Serial.print("Temperature (°F): ");
  Serial.println(sensors.getTempFByIndex(0));
  
  delay(5000);
}

Once the code is uploaded to the Arduino, you should be able to see the current temperature in the serial monitor:

Temperature (°C): 21.44
Temperature (°F): 70.59

Using Multiple Sensors

To finish off, let's look at how to use multiple sensors on the same bus. Unfortunately this requires you to use separate DS18B20 sensors. You need only one pullup resistor for the whole bus. Unfortunately, each KY-001 module incorporates its own pullup resistor. If you connect too many of them together the bus will stop working. So please use separate sensors without integrated pullups, to make sure that everything will work as intended. The picture below shows how to connect two waterproof DS18B20 sensors to the same bus. Just connect the matching pins together. Of course, you can also build this circuit with DS18B20 sensors in a TO-92 package or even mix them. Electrically there is no difference, just the packaging is different between these sensor types.

Wiring up two waterproof DS18B20 sensors to the Arduino

We already know how to trigger a measurement for all connected sensors: we just call requestTemperatures. To print out all measurement values, we need to iterate through all sensors and read out each of them. Luckily the library takes care of the details for us. In theory, we could just call getTempCByIndex or getTempFByIndex in a loop and increase the index by one each time. Once we reach the last sensor, a large negative number is returned to indicate an error. This tells us when we need to stop. The problem with this is, that it is hard to tell which sensor is which.

Remember that we said that each sensor as a unique 48-bit address? By calling getAddress with the sensor index we can query the sensors address. To store the address one needs to pass an DeviceAddress object to the function alongside the index. The address will be written into the DeviceAddress object which is basically an array of 8 bytes. If we reach the end and there is no further sensor for the requested index the function will return false, otherwise it returns true. To iterate over all devices, we can just increase the index and loop until getAddress returns false. To get the measurement values we can then call getTempC or getTempF with the sensors' address.

You can see the full code below. I also made a small utility function printAddress that prints out the sensors address.

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

// Sensor at pin 8
OneWire bus(8);
DallasTemperature sensors(&bus);

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

void printAddress(DeviceAddress addr) {
  for(int i = 0; i < 8; i++) {
    if(i != 0) Serial.print(':');
    if(addr[i] < 0x10) Serial.print('0');
    Serial.print(addr[i], HEX);  
  }
}

void loop() {
  // Start the temperature measurement on all sensors and wait for it
  sensors.requestTemperatures();

  int index = 0;
  DeviceAddress addr;

  while(sensors.getAddress(addr, index)) {
    // Sensor index and address
    Serial.print("Sensor ");
    Serial.println(index);
    Serial.print(" - Address: ");
    printAddress(addr);
    Serial.println("");
  
    // Read and show the result
    Serial.print(" - Temperature (°C): ");
    Serial.println(sensors.getTempC(addr));
    Serial.print(" - Temperature (°F): ");
    Serial.println(sensors.getTempF(addr));

    // Next index
    index++;
  }
  
  delay(5000);
}

Once you uploaded the code to the Arduino, you should see an output like this in the serial monitor:

Sensor 0
 - Address: 28:E6:0B:E2:20:19:01:2F
 - Temperature (°C): 20.50
 - Temperature (°F): 68.90
Sensor 1
 - Address: 28:8B:7D:2E:21:19:01:D7
 - Temperature (°C): 20.37
 - Temperature (°F): 68.68

The address and the measurement values for each attached sensor will be printed out. If you only connect a single sensor you can easily determine its address. Label the sensor with its address to later be able to identify which sensor measures what. By the way, you might have noticed that the address printed on the serial monitor is 64-bits (8 bytes) long and not 48-bit. The first byte is the family code that identifies the device as an DS18B20 sensor. The last byte is an 8-bit CRC checksum. Only the 48-bit in between are unique to each sensor.

The DS18B20 has additional features like alarms. Alarms allow to set a temperature threshold on the module and later only read out the sensors where this threshold was exceeded. This functionality is interesting for complex solutions with a lot of sensors. In our case we can just read out all sensors. I thus will not cover these advanced features in this tutorial.

References

Previous Post Next Post