The DS18B20 is a digital temperature sensor chip. It is produced by Maxim Integrated Circuits and the sensor comes in different variants, for example as SMD package (Surface Mount Device) or with three pins as TO-92 package; that is a compact type of casing often used for transistors and small ICs (Integrated Circuits). There are also water-proof metal casings with a longer wire attached to it, which makes sensing in harsher environments, for example under water, possible. As the sensor will be used to measure the water temperature close to the surface and at the bottom of the pond, the waterproof version was used in this case (Figure 1).
The sensor is not able to sense the temperature directly through the package, instead, it primarily uses the GND pin for measuring the temperature. Therefore, it is necessary that the GND pin has a sufficient thermal connection to the heat source to be measured. The better the connection, the more accurate the results and the faster can temperature changes be measured. In the case of the waterproof version, the GND pin is thermally connected to the metal casing of the probe.
Like the DHT-22, the DS18B20 features only 3 (connected) pins as it works using 1-Wire communication which will be explained in more detail in section 2. Unlike the DHT-22, the resolution of the DS18B20 can be configured to 9, 10, 11 or 12 bits, where 12 bits is the default resolution. The increments of temperature of the resolutions is 0.5°C, 0.25°C, 0.125°C and 0.0625°C, respectively. Even though a decrease in resolution impairs the accuracy of the measurement, it has the advantage of shortening the time necessary for taking a measurement and reducing power consumption.
It also features a variety of other possibilities such a powering the device through a pullup resistor connected to the Data line (parasite mode) or setting alarms for a high and a low temperature.
One of the biggest advantages of the DS18B20 in contrast to the DHT-22 is, that each sensor has a unique 64 serial code, which works as an address during communication. Due to the unique address many sensors can be connected to the same 1-Wire bus and can then be addressed by the microcontroller individually. Because of this, only a single GPIO pin of the MCU is needed to read data from all the DS18B20s connected to it.
The serial code contains different information:
The 64-bit address is stored in a 64-bit ROM (Read Only Memory), that means, the address is assigned during manufacture and cannot be changed afterwards; this prevents conflicts of two devices having the same address later on.
There are two different CRCs, one for the serial code, which does not change and one for the data contained in the memory of the module (scratchpad memory), which changes depending on the temperature readings. Both CRCs are a method for the microcontroller for validating that the transmission of the requested information, either from the ROM or the scratchpad, was done correctly (for more information check the datasheet). The DHT-22 used a similar approach for data validation with the check-sum byte.
The 64-bit ROM and the 1-Wire port are connected through the memory control logic to the scratchpad. The scratchpad is a high-speed internal memory that is used for storing small, often temporary, pieces of data which can be rapidly retrieved if needed. The scratchpad memory, which gets erased after powering down, is connected to the temperature sensor itself and stores its data in a 2-byte register. Furthermore, the module has a small EEPROM (Electronically Erasable Programmable Read Only Memory), that means a nonvolatile data storage, which retains the data even when the device is powered down. The Arduino UNO and the ESP32 also have such storages, but of larger size. The module can transfer the aforementioned alarm temperatures TH and TL, as well as the configuration register from the scratchpad into the EEPROM and reload them back to the scratchpad memory after powering up again (figure 2).
Figure 2 DS18B20 block diagram (Source: DS18B20 datasheet p. 5) |
Normally the sensor is in an idle state after powering up. When the MCU issues a Convert T command, the sensor measures the temperature and does an analog to digital conversion and stores the result in the scratchpad 2-byte temperature register (figure 3) and the sensor goes back into idle state.
Figure 3 Structure of the temperature register, LS = Least Significant, MS = Most Significant, S = Sign. |
In the register, the 5 leading bits are representing the sign of the temperature. If the temperature is below 0°C, S = 1 and if it is above 0°C, S = 0. The other 11 bits represent the temperature indicated in figure 3, where bit 0, 1 and 2 can be undefined depending on the resolution stored in the configuration register:
The maximum range of values that can be displayed is thus -127.9375 – 127.9375, which exceeds the range of the temperature sensor. Some examples of different temperatures displayed in 12-bit resolution can be seen in table 1.
Table 1 Different temperature values and how they are stored in the temperature register. | ||
---|---|---|
MS Byte | LS Byte | Temperature |
0000 0111 | 1101 0000 | + 125 °C |
0000 0001 | 0111 0001 | + 23.0625 °C |
0000 0000 | 1000 1100 | + 8.75 °C |
0000 0000 | 0000 0000 | 0 °C |
1111 1111 | 0011 1101 | - 12.125 °C |
1111 1100 | 1001 0000 | - 55°C |
The configuration register is stored in the scratchpad memory (and EEPROM) and contains 1 byte of data. The MSB (bit 7) and the 5 LSB (bit 0 – 4) are reserved for internal use and cannot be changed. Bit 6 (R1) and bit 5 (R0) can be changed to adjust the resolution (figure 4). As can be seen, the conversion time doubles each time the resolution is increased by 1. A 12-bit resolution measurement takes 8 times as long as a 9-bit conversion.
In the 1-Wire bus, the MCU, in this case the ESP32, acts as master and the DS1820 sensors act as slaves. It should be noted that all commands and data are transmitted with their least significant bit first in the bus.
Sending signals on the 1-Wire bus works in a similar way to the transmissions with the DHT-22. Normally, that is when no communication is happening and the data-bus is available, the potential of the data bus is high; with the ESP32 that means 3.3V. In this case, it is necessary to add an external pullup resistor of approximately 5kΩ. The external resistor and the resistor in the input of the ESP32 act as a voltage divider and because the input resistance is much higher, the potential in the data-bus is almost 3.3V. The DS18B20 (as well as the ESP32) communicate with each other by shorting the data-bus to GND. The DS18B20 is an open drain device. That means that its signal output is done using a transistor, which shorts the collector/drain (the data bus) to the emitter/source (GND).
Each communication between MCU and DS18B20 needs to follow a transaction sequence that consists of 3 steps:
The initialization consists of a reset pulse issued by the master (ESP32) followed by a presence pulse transmitted by the slaves which informs the master that there are 1-Wire devices on the bus which are ready to operate.
After having detected a presence pulse, the master can transmit a ROM command, which operates on the 64-bit ROM codes that each slave device has. There are different ROM commands, which allow the master to search and identify all the slaves on the bus and address a single device (or send a command for temperature measurement to all of them at once).
In the third step, a DS18B20 function command is issued by the master which allows to initiate a temperature measurement, read certain information like the power supply mode or temperature, or configure the temperature alarms or sensor resolution.
Similar to the DHT-22 protocol, the 1-Wire data bus uses a protocol with exactly defined time periods for the low and high voltage level pulses for sending or receiving a logical 1 or 0 and for pulses used for initialization and ROM commands. The exact data can be found in the datasheet of the DS18B20.
When connecting the pins (figure 1), pin 1 (red) is VDD, pin 2 (black) is GND and pin 3 (yellow) is the data pin. In this project two sensors, one measuring the temperature at the bottom of the pond and one at the surface, are connected to the ESP32. As each sensor can be addressed individually by its serial code, the data pins can be connected to the same GPIO pin of the ESP32 (here pin 14 was used). As mentioned before, the sensors need a pullup resistor connecting the data pin with VDD. As both sensors are connected in the same data bus, only a single 5kΩ resistor between their data pins and VDD needs to be added (see figure 5).
Like the DHT-22, VDD can be anywhere between 3.0 V to 5.5 V and can thus be powered by the ESP32 without a problem. Further information on the sensor’s specifications can be found in table 2 and in the datasheet.
Table 2 Specifications of the DS18B20 digital temperature sensor (local power supply with ESP32). | |||
---|---|---|---|
Sensor | DS18B20 | ||
Supply Voltage $V_{DD}$ | 3.3 V | ||
Pullup Supply Voltage $V_{PU}$ | 3.3 V | ||
Input Logic-Low $V_{IL}$ | -0.3 V – 0.8 V | ||
Input Logic-High $V_{IH}$ | 2.2 – 3.6 V | ||
Active Current $I_{DD}$ | 1 – 1.5 mA | ||
Standby Current $I_{DDS}$ | 0.75 – 1.0 uA | ||
Thermometer Range | -55 °C – 125°C | ||
Thermometer Error | -10 °C – 85 °C | ±0.5 °C | |
-30 °C – 100 °C | ±1 °C | ||
-55 °C – 125 °C | ±2 °C | ||
Resolution, Temperature Increments, Conversion Time | 9 bit | 0.5 °C | 750 ms |
10 bit | 0.25 °C | 375 ms | |
11 bit | 0.125 °C | 187.5 ms | |
12 bit | 0.0625 °C | 93.75 ms |
For the programming of the DS18B20, the OneWire.h library and the DallasTemperature.h library were used which can be found on github or in the Arduino library manager.
To distinguish the two sensors, it is advisable to first determine their individual serial codes which can be done using the OneWire.h library. The simple sketch searches for devices on the 1-Wire bus and prints their address in the serial monitor. To distinguish the two sensors, the one for the bottom of the pond was marked with tape (sensor 1), while the one for the surface was left as it was (sensor 2). The sketch was uploaded to the ESP32, sensor 1 was connected to the data bus and the ESP32 was restarted (EN – Enable on the ESP32 module) while connected to the computer. The address was obtained, and the process was repeated for the second sensor.
//1-Wire Device Address determination #include <OneWire.h> //1 #include <DallasTemperature.h> const uint8_t ONE_WIRE_BUS = 14; //2 OneWire oneWire(ONE_WIRE_BUS); //3 DeviceAddress SensorAddress; //4 void setup() { Serial.begin(115200); //5 Serial.println("Searching for devices on the bus..."); oneWire.search(SensorAddress); //6 Serial.print("Device Address: "); printAddress(SensorAddress); //7 Serial.println("==========================="); Serial.println(); } void loop() { } void printAddress(DeviceAddress SensorAddress){ //8 for(uint8_t i=0; i<8;i++){ //9 Serial.print("0x"); //10 if(SensorAddress[i]<16) Serial.print("0"); //11 Serial.print(SensorAddress[i],HEX); //12 if(i<7) Serial.print(","); //13 } Serial.println(); }
After the procedure described in section 4.1, the serial monitor will displays something like this:
Searching for devices on the bus... Device Address: 0x28,0xC4,0xA0,0x51,0x38,0x19,0x01,0xC2 //1 =========================== Searching for devices on the bus... Device Address: 0x28,0x0B,0xDB,0x60,0x38,0x19,0x01,0xA3 //2 =========================== Searching for devices on the bus... Device Address: 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 //3 ===========================
The following sketch activates the sensors, measures the temperatures and calculates the mean value for more accurate results which are then printed to the serial monitor. The function defined in the sketch will be later implemented into the complete code for the monitoring station. To use the sketch, both sensors need to be connected correctly to the ESP32 like explained in section 3.
//DS18B20 ESP32 Temperature Function //Definitions and Pins const int ONE_WIRE_BUS = 14; //Libraries and Objects #include <OneWire.h> #include <DallasTemperature.h> DeviceAddress bottomSensorAddress = {0x28,0xC4,0xA0,0x51,0x38,0x19,0x01,0xC2}; //1 DeviceAddress surfaceSensorAddress = {0x28,0x0B,0xDB,0x60,0x38,0x19,0x01,0xA3}; //2 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature DS18B20(&oneWire); //3 //Variables float bottomTem = 0.00; //4 String bottomTemperature = ""; //5 float surfaceTem = 0.00; String surfaceTemperature = ""; const uint8_t AveragingNumberDS18B20 = 5; //6 const uint8_t RESOLUTION = 12; //7 const uint8_t TCONV = 750; //8 uint8_t delayTime = (TCONV/pow(2, (12-RESOLUTION))) + 10; //9 void setup() { Serial.begin(115200); Serial.println("Measurement is starting ..."); } void loop() { measureDS18B20Tem(AveragingNumberDS18B20); //10 Serial.println("Sensor 1 (Bottom) Measurement: " + bottomTemperature + " °C");//11 Serial.println("Sensor 2 (Surface) Measurement: " + surfaceTemperature + " °C"); Serial.println("=============================================="); delay(2000); //12 } //DS18B20 Measurement function void measureDS18B20Tem (const uint8_t AveragingNumber){ //13 DS18B20.begin(); //14 bottomTem = 0; //15 surfaceTem = 0; bottomTemperature = ""; surfaceTemperature = ""; for(byte i = 0; i < AveragingNumber; i++) //16 { DS18B20.requestTemperatures(); //17 delay(delayTime); //18 bottomTem += DS18B20.getTempC(bottomSensorAddress); //19 surfaceTem += DS18B20.getTempC(surfaceSensorAddress); } bottomTem /= AveragingNumber; //20 surfaceTem /= AveragingNumber; if(bottomTem<10) //21 bottomTemperature = "0"; bottomTemperature += bottomTem; //22 if(surfaceTem<10) surfaceTemperature = "0"; surfaceTemperature += surfaceTem; }
When the sketch is uploaded and the sensors are correctly connected to the ESP32, the serial monitor reads something similar to this:
14:34:50.335 -> Measurement is starting ... 14:34:53.197 -> Sensor 1 (Bottom) Measurement: 25.56 °C //1 14:34:53.197 -> Sensor 2 (Surface) Measurement: 32.54 °C //2 14:34:53.197 -> ============================================== 14:35:00.187 -> Sensor 1 (Bottom) Measurement: 25.56 °C 14:35:00.187 -> Sensor 2 (Surface) Measurement: 32.66 °C 14:35:00.187 -> ============================================== 14:35:07.209 -> Sensor 1 (Bottom) Measurement: 25.56 °C 14:35:07.209 -> Sensor 2 (Surface) Measurement: 32.78 °C 14:35:07.209 -> ==============================================
The first sensor (1) was measuring the room temperature on a rather warm day. The second sensor (2) measured the temperature of my hand. Both measurements seem reasonable and when both sensors measure the room temperature, their results are almost equal. However, the temperature probes need some time to get to thermal equilibrium with their environment. When submerged in the water of the pond continuously, this should not be a problem whatsoever.