Raspberry Pi Pico
DS18B20 Digital Thermometer

I picked up a digital temperature sensor breakout board for £2.50. It was a DS18B20 sensor that you communicate with using the OneWire protocol. Handily, MicroPython comes with a module for the protocol and for the family of sensors that this one is from. I thought I'd put together a quick project to show the temperature on one of Adafruit's 4x7 segment displays using the HT16K33 backpack.

Here is a photo of what I ended up with. The display is way brighter than my photo shows and is really easy to read.

Pico Circuit

Here is a Fritzing diagram showing the equivalent circuit. Notice that there is a pull-up resistor on the signal line. This is a 4/7K resistor as advised in the MicroPython documentation.

Pico Circuit

I started by doing a quick tidy of the library code I had previously written for the display. I wanted to make it so you passed the I2C connection to the class when you instantiate it.

class backpack:
    ADDRESS             = 0x70
    BLINK_CMD           = 0x80
    CMD_BRIGHTNESS      = 0xE0
    # Digits 0 - F
    NUMS = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D,
        0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71]
       
    def __init__(self, i2c):
        self.buffer = bytearray([0]*16)
        self.i2c= i2c
        self.i2c.writeto(self.ADDRESS,b'\x21')
        # 0 to 3
        self.blink_rate(0)
        # 0 to 15
        self.set_brightness(15)
        self.update_display()
    
    def set_brightness(self,b):
        self.i2c.writeto(self.ADDRESS,bytes([self.CMD_BRIGHTNESS | b]))       
    
    def blink_rate(self, b):
        self.i2c.writeto(self.ADDRESS,bytes([self.BLINK_CMD | 1 | (b << 1)]))
    
    def write_digit(self, position, digit, dot=False):
        # skip the colon
        offset = 0 if position < 2 else 1
        pos = offset + position
        self.buffer[pos*2] = self.NUMS[digit] & 0xFF
        if dot:
            self.buffer[pos*2] |= 0x80                    
    
    def update_display(self):
        data = bytearray([0]) + self.buffer
        self.i2c.writeto(self.ADDRESS,data)
    
    def print(self,value):
        if value<0 or value>9999:
            return
        sdig =  '{:04d}'.format(value)
        dts = [int(x) for x in sdig]
        for i,d in enumerate(dts):
            self.write_digit(i,d)
    
    def set_decimal(self, position, dot=True): 
        # skip the colon
        offset = 0 if position < 2 else 1
        pos = offset + position        
        if dot:
            self.buffer[pos*2] |= 0x80
        else:
            self.buffer[pos*2] &= 0x7F
        
    def clear(self):
        self.buffer = bytearray([0]*16)
        self.update_display()
    
    def set_colon(self, colon=True):
        if colon:
            self.buffer[4] |= 0x02
        else:
            self.buffer[4] &= 0xFD

The rest of the code is not too special. It shows how to interact with the sensor and how I prepared the reading to get it onto the display as easily as possible.

from machine import Pin, I2C
from time import sleep
from onewire import OneWire
from ds18x20 import DS18X20
from ht16k33 import backpack


# set up temperature sensor with onewire
ow = OneWire(Pin(22))
ds = DS18X20(ow)
r = ds.scan()[0]

# set up i2c and display
i2c = I2C(1, sda=Pin(14), scl=Pin(15))
display = backpack(i2c)

while True:
    ds.convert_temp()
    sleep(0.75)
    t = round(ds.read_temp(r), 2)
    print(t)    
    # do the whole number part
    w = int(t * 100)
    display.print(w)
    # set the decimal point
    display.set_decimal(1)
    # do the fractional part
    w = w % 100
    display.write_digit(2, w // 10)
    display.write_digit(3, w % 10)
    display.update_display()
    sleep(5)

I was really impressed with the temperature sensor and how easy it was to get a plausible reading with the built-in library. Given the price and the apparent accuracy, I would be reaching for this sensor again, perhaps to draw a nice thermometer on an LCD...