Raspberry Pi Pico: DS18x20 in MicroPython

Updated: Thanks to Ben, who noticed the Fritzing diagrams had the sensors the wrong way round. Fixed now…

Hidden away in the Pico MicroPython guide is a hint that there may be more modules installed in the system than they let on. In the section about picotool, the guide has a seemingly innocuous couple of lines:

frozen modules: _boot, rp2, ds18x20, onewire, uasyncio, uasyncio/core, uasyncio/event, uasyncio/funcs, uasyncio/lock, uasyncio/stream

The third and fourth ‘frozen modules’ are a giveaway: it shows that support for the popular Dallas/Maxim DS18x20 1-Wire temperature sensors is built in. Nowhere else in the guide are they mentioned. I guess someone needs to write them up.

DS18x20 digital temperature sensors – usually sold as DS18B20 by Maxim and the many clone/knock-off suppliers – are handy. They can report temperatures from -55 to 125 °C, although not every sensor will withstand that range. They come in a variety of packages, including immersible sealed units. They give a reliable result, free from ADC noise. They’re fairly cheap, the wiring’s absurdly simple, and you can chain long strings of them together from the same input pin and they’ll all work. What they aren’t, though, is fast: 1-Wire is a slow serial protocol that takes a while to query all of its attached devices and ferry the results back to the controller. But when we’re talking about environmental temperature, querying more often than a few times a minute is unnecessary.

So this is the most complex way you can wire up a DS18x20 sensor:

breadboard with raspberry Pi Pico, DS18x20 sensor with 47 k? pull-up resistor between 3V3 power and sensor data line
Raspberry Pi Pico connected to a single DS18x20 sensor

and this is how it’s wired:

   DS18X20    Pico
   =========  =========
   VDD      ? 3V3
              /
     --47 k?--
    /
   DQ       ? GP22
   GND      ? GND

 (47 k? resistor between DQ and 3V3 as pull-up)

Adding another sensor is no more complicated: connect it exactly as the first, chaining the sensors together –

breadboard with raspberry Pi Pico, two DS18x20 sensors with 47 k? pull-up resistor between 3V3 power and sensor data line
Two DS18x20 sensors, though quite why you’d want two temperature sensors less than 8 mm apart, I’ll never know. Imagine one is a fancy immersible one on a long cable

The code is not complex, either:

# Raspberry Pi Pico - MicroPython DS18X20 Sensor demo
# scruss - 2021-02
# -*- coding: utf-8 -*-

from machine import Pin
from onewire import OneWire
from ds18x20 import DS18X20
from time import sleep_ms
from ubinascii import hexlify    # for sensor ID nice display

ds = DS18X20(OneWire(Pin(22)))
sensors = ds.scan()

while True:
    ds.convert_temp()
    sleep_ms(750)     # mandatory pause to collect results
    for s in sensors:
        print(hexlify(s).decode(), ":", "%6.1f" % (ds.read_temp(s)))
        print()
    sleep_ms(2000)

This generic code will read any number of attached sensors and return their readings along with the sensor ID. The sensor ID is a big ugly hex string (the one I’m using right now has an ID of 284c907997070344, but its friends call it ThreeFourFour) that’s unique across all of the sensors that are out there.

If you’re reading a single sensor, the code can be much simpler:

# Raspberry Pi Pico - MicroPython 1x DS18X20 Sensor demo
# scruss - 2021-02
# -*- coding: utf-8 -*-

from machine import Pin
from onewire import OneWire
from ds18x20 import DS18X20
from time import sleep_ms

ds = DS18X20(OneWire(Pin(22)))
sensor_id = ds.scan()[0]  # the one and only sensor

while True:
    ds.convert_temp()
    sleep_ms(750)         # wait for results
    print(ds.read_temp(sensor_id), " °C")
    sleep_ms(2000)

The important bits of the program:

  1. Tell your Pico you have a DS18x20 on pin GP22:
    ds = DS18X20(OneWire(Pin(22)))
  2. Get the first (and only) sensor ID:
    sensor_id = ds.scan()[0]
  3. Every time you need a reading:
    1. Request a temperature reading:
      ds.convert_temp()
    2. Wait for results to come back:
      sleep_ms(750)
    3. Get the reading back as a floating-point value in °C:
      ds.read_temp(sensor_id)

That’s it. No faffing about with analogue conversion factors and mystery multipliers. No “will it feel like returning a result this time?” like the DHT sensors. While the 1-Wire protocol is immensely complicated (Trevor Woerner has a really clear summary: Device Enumeration on a 1-Wire Bus) it’s not something you need to understand to make them work.