In this tutorial, we’ll be using a DS18B20 temperature sensor with Raspberry Pi Pico. The DS18B20 is a famous temperature sensor that uses a digital signal to output temperature data.
The DS18B20 is a digital temperature sensor that uses a 1-Wire bus to communicate with the Raspberry Pi Pico. the sensor provides 9 to 12 bits of resolution, completing it ideal for applications where temperature accuracy is important.
Required Material
For this project you will need:
- Raspberry pi pico
- Breadboard
- DS18B20 temp sensor
- 4.7K resistor
- Jumper wire
DS18B20 temperature sensor
A DS18B20 temperature sensor is a device that measures temperature. It is made up of a metal element that changes resistance with temperature, and an IC that amplifies the signal and converts it to a digital output. The temperature range is between -55C to 125C and it is accurate to +/- 0.5C between -10C and +85C. it can be used in a wide range of applications. including medical, automotive, and industrial.
Pinout of DS18B20 temperature sensor
Specs of DS18B20 Sensor
- Functional temperature range: -55 to 125°C (-67°F to +257°F)
- 9 to 12-bit selectable resolution
- Uses 1-Wire interface- requires only one digital pin for communication
- Unique 64-bit ID burned into the chip
- Multiple sensors can share one pin
- ±0.5°C Accuracy from -10°C to +85°C
- Query time is less than 750ms
- Usable with 3.0V to 5.5V power/data
Wiring Diagram – DS18B20 Temperature Sensor With Raspberry Pi Pico
To print the data from DS18B20 on the serial monitor of the Thonny IDE you have to build the circuit by following the schematic diagram.
The connections are straightforward, see the image above with the circuit schematic.
- VCC & GND –> Raspberry Pi Pico 3.3V & GND
- Signal (middle pin) –> Raspberry pi pico GP26 (ADC0)
- 4.7K Resistor between signal pin and 3.3V pin
Micropython Code & Libraries – DS18B20 With Raspberry Pi Pico
- Open the Thonny IDE on your computer.
- Copy the code & paste the code into the Thonny Editor.
- Save the file with the name “onewire.py” and “ds18x20.py“.
onewire.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
#!/usr/bin/env python3 # # The crc8 implementation is a Python port of the C code published here: # http://lentz.com.au/blog/tag/crc-table-generator # As far as suitable, the copyrigth notice and the disclaimer of the link apply # """ OneWire library for MicroPython """ import time import machine class OneWire: CMD_SEARCHROM = const(0xf0) CMD_READROM = const(0x33) CMD_MATCHROM = const(0x55) CMD_SKIPROM = const(0xcc) PULLUP_ON = const(1) def __init__(self, pin): self.pin = pin self.pin.init(pin.OPEN_DRAIN, pin.PULL_UP) self.disable_irq = machine.disable_irq self.enable_irq = machine.enable_irq self.crctab1 = (b"\x00\x5E\xBC\xE2\x61\x3F\xDD\x83" b"\xC2\x9C\x7E\x20\xA3\xFD\x1F\x41") self.crctab2 = (b"\x00\x9D\x23\xBE\x46\xDB\x65\xF8" b"\x8C\x11\xAF\x32\xCA\x57\xE9\x74") def reset(self, required=False): """ Perform the onewire reset function. Returns True if a device asserted a presence pulse, False otherwise. """ sleep_us = time.sleep_us pin = self.pin pin(0) sleep_us(480) i = self.disable_irq() pin(1) sleep_us(60) status = not pin() self.enable_irq(i) sleep_us(420) assert status is True or required is False, "Onewire device missing" return status def readbit(self): sleep_us = time.sleep_us pin = self.pin pin(1) # half of the devices don't match CRC without this line i = self.disable_irq() pin(0) # skip sleep_us(1) here, results in a 2 us pulse. pin(1) sleep_us(5) # 8 us delay in total value = pin() self.enable_irq(i) sleep_us(40) return value def readbyte(self): value = 0 for i in range(8): value |= self.readbit() << i return value def readbytes(self, count): buf = bytearray(count) for i in range(count): buf[i] = self.readbyte() return buf def readinto(self, buf): for i in range(len(buf)): buf[i] = self.readbyte() def writebit(self, value, powerpin=None): sleep_us = time.sleep_us pin = self.pin i = self.disable_irq() pin(0) # sleep_us(1) # dropped for shorter pulses pin(value) sleep_us(60) if powerpin: pin(1) powerpin(PULLUP_ON) else: pin(1) self.enable_irq(i) def writebyte(self, value, powerpin=None): for i in range(7): self.writebit(value & 1) value >>= 1 self.writebit(value & 1, powerpin) def write(self, buf): for b in buf: self.writebyte(b) def select_rom(self, rom): """ Select a specific device to talk to. Pass in rom as a bytearray (8 bytes). """ self.reset() self.writebyte(CMD_MATCHROM) self.write(rom) def crc8(self, data): """ Compute CRC, based on tables """ crc = 0 for i in range(len(data)): crc ^= data[i] ## just re-using crc as intermediate crc = (self.crctab1[crc & 0x0f] ^ self.crctab2[(crc >> 4) & 0x0f]) return crc def scan(self): """ Return a list of ROMs for all attached devices. Each ROM is returned as a bytes object of 8 bytes. """ devices = [] diff = 65 rom = False for i in range(0xff): rom, diff = self._search_rom(rom, diff) if rom: devices += [rom] if diff == 0: break return devices def _search_rom(self, l_rom, diff): if not self.reset(): return None, 0 self.writebyte(CMD_SEARCHROM) if not l_rom: l_rom = bytearray(8) rom = bytearray(8) next_diff = 0 i = 64 for byte in range(8): r_b = 0 for bit in range(8): b = self.readbit() if self.readbit(): if b: # there are no devices or there is an error on the bus return None, 0 else: if not b: # collision, two devices with different bit meaning if diff > i or ((l_rom[byte] & (1 << bit)) and diff != i): b = 1 next_diff = i self.writebit(b) if b: r_b |= 1 << bit i -= 1 rom[byte] = r_b return rom, next_diff |
ds18x20.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# DS18x20 temperature sensor driver for MicroPython. # MIT license; Copyright (c) 2016 Damien P. George from micropython import const from machine import Pin CMD_CONVERT = const(0x44) CMD_RDSCRATCH = const(0xbe) CMD_WRSCRATCH = const(0x4e) CMD_RDPOWER = const(0xb4) PULLUP_ON = const(1) PULLUP_OFF = const(0) class DS18X20: def __init__(self, onewire): self.ow = onewire self.buf = bytearray(9) self.config = bytearray(3) self.power = 1 # strong power supply by default self.powerpin = None def powermode(self, powerpin=None): if self.powerpin is not None: # deassert strong pull-up self.powerpin(PULLUP_OFF) self.ow.writebyte(self.ow.CMD_SKIPROM) self.ow.writebyte(CMD_RDPOWER) self.power = self.ow.readbit() if powerpin is not None: assert type(powerpin) is Pin, "Parameter must be a Pin object" self.powerpin = powerpin self.powerpin.init(mode=Pin.OUT, value=0) return self.power def scan(self): if self.powerpin is not None: # deassert strong pull-up self.powerpin(PULLUP_OFF) return [rom for rom in self.ow.scan() if rom[0] in (0x10, 0x22, 0x28)] def convert_temp(self, rom=None): if self.powerpin is not None: # deassert strong pull-up self.powerpin(PULLUP_OFF) self.ow.reset() if rom is None: self.ow.writebyte(self.ow.CMD_SKIPROM) else: self.ow.select_rom(rom) self.ow.writebyte(CMD_CONVERT, self.powerpin) def read_scratch(self, rom): if self.powerpin is not None: # deassert strong pull-up self.powerpin(PULLUP_OFF) self.ow.reset() self.ow.select_rom(rom) self.ow.writebyte(CMD_RDSCRATCH) self.ow.readinto(self.buf) assert self.ow.crc8(self.buf) == 0, 'CRC error' return self.buf def write_scratch(self, rom, buf): if self.powerpin is not None: # deassert strong pull-up self.powerpin(PULLUP_OFF) self.ow.reset() self.ow.select_rom(rom) self.ow.writebyte(CMD_WRSCRATCH) self.ow.write(buf) def read_temp(self, rom): try: buf = self.read_scratch(rom) if rom[0] == 0x10: if buf[1]: t = buf[0] >> 1 | 0x80 t = -((~t + 1) & 0xff) else: t = buf[0] >> 1 return t - 0.25 + (buf[7] - buf[6]) / buf[7] elif rom[0] in (0x22, 0x28): t = buf[1] << 8 | buf[0] if t & 0x8000: # sign bit set t = -((t ^ 0xffff) + 1) return t / 16 else: return None except AssertionError: return None def resolution(self, rom, bits=None): if bits is not None and 9 <= bits <= 12: self.config[2] = ((bits - 9) << 5) | 0x1f self.write_scratch(rom, self.config) return bits else: data = self.read_scratch(rom) return ((data[4] >> 5) & 0x03) + 9 def fahrenheit(self, celsius): return celsius * 1.8 + 32 if celsius is not None else None def kelvin(self, celsius): return celsius + 273.15 if celsius is not None else None |
The following micropython script code will read the temperature values from the DS18B20 sensor.
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import machine, onewire, ds18x20, time ds_pin = machine.Pin(26) ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin)) roms = ds_sensor.scan() print('Found DS18B20 : ', roms) while True: ds_sensor.convert_temp() time.sleep_ms(750) for rom in roms: print("Temp") print(ds_sensor.read_temp(rom)) time.sleep(2) |
Conclusion
To complete this, the DS18B20 temperature sensor is an excellent way to measure temperature with a Raspberry Pi Pico. It is easy to set up and use, and it is very accurate.
Multiple DS18B20 Temp sensors with Raspberry Pi Pico
We’ll be using the Raspberry Pi Pico and multiple DS18B20 Temperature sensors to create a temperature monitoring system. We’ll be using the 1-Wire protocol to communicate with the three DS18B20 sensors, which means we can use a single GPIO pin to control all of the sensors.
Circuit Diagram – Multiple DS18B20 Temp sensors with Raspberry Pi Pico
The following diagram is a simplified view of the connection.
Micropython Script
The following code will enable you to collect the DS18B20 temperature sensor finds and prints out the serial number of each sensor and the temperature that it is reading.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
import machine import onewire import ds18x20 import time import binascii gp_pin = machine.Pin(26) ds18b20_sensor = ds18x20.DS18X20(onewire.OneWire(gp_pin)) sensors = ds18b20_sensor.scan() print('Found DS18B20: ', sensors) while True: ds18b20_sensor.convert_temp() time.sleep_ms(750) for device in sensors: s = binascii.hexlify(device) readable_string = s.decode('ascii') print(readable_string) print(ds18b20_sensor.read_temp(device)) time.sleep(10) |
Conclusion
The result will look something like the following;
1 2 3 4 5 6 7 8 |
Found devices: [bytearray(b'(\xff\xef\x8d>\x04\x00\xa8'), bytearray(b'(\xffI\x90\ >\x04\x00\xa2'), bytearray(b'(\xff\xf8n;\x04\x00q')] 28ffef8d3e0400a8 19.8125 28ff49903e0400a2 20.4375 28fff86e3b040071 19.625 |
2 Comments
Running this code bricked my Pico
Really 🙁