Bare Metal: DS18B20
- 6 minutes read - 1179 wordsI’ve been working through Google’s Comprehensive Rust and, for the past couple of weeks, the Bare Metal Rust standalone course that uses the (excellent) micro:bit v2 that has a Nordic Semiconductor nRF52833 (an ARM Cortex-M4; interestingly its USB interface is also implemented using an ARM Cortex M0).
There’s a wealth of Rust tutorials for microcontrollers and I bought an ESP32-C3-DevKit-RUST-1 for another tutorial and spent some time with my favorite Pi Pico and a newly-acquired Debug Probe.
I’m going to blog in reverse order on these experiences starting with several unsuccessful attempts to interact with 5 DS18B20 (thermometer) sensors using Rust on an ESP32-C3 and then, trying to find a solution that worked, Python on a Pico.
DS18B20s use 1-Wire. 1-Wire is a very straightforward protocol (akin to 12C, SPI etc.). It is a serial communication protocol that, as its name hints, uses a single wire for data (the data wire can also be used for “parasitic” power).
Here are some diagrams:
Those of you who are familiar with it, will recognize this as the really excellent Wokwi simulator that includes several microcontrollers (including Arduinos but not micro:bits) and a slew of peripherals.
NOTE It is possible that the reason these examples don’t work is a 1-Wire timing issue running under Wokwi. I should buy some DS18B20 and try this in our simulation.
Wokwi is extensible, comprising multiple repos that implement the MCUs and peripherals including a C implementation of the DS18B20. This enables the DS18B20
device in Wokwi including permitting the following diagram.json
entry:
{
"type": "board-ds18b20",
"id": "temp1",
"top": -40,
"left": 90,
"attrs": {
"deviceID": "000000000001",
"familyCode": "40",
"tempWaveForm": "sine",
"tempWaveFreq": "100"
}
}
NOTE The values in the
attrs
object are documented here
This repo is this peripheral’s implementation and it functions somewhat like a server. Even though I haven’t been able to get the Rust and Python (clients) running on the microcontrollers doesn’t work, the peripheral appears (!?) to work correctly. Here’s the output from Wokwi’s “CHIP CONSOLE”:
*** DS18B20 chip initialising...
reading temp mode: sine
*** DS18B20 setting attributes:
genDebug: 0
owDebug: 0
temperature: 0.000000
familyCode: 28
minTemp: -55.000000
maxTemp: 125.000000
temp_freq: 100.000000
temp_mode: 2
deviceID: 2800000000000140
DS18B20 chip initialised
*** DS18B20 chip initialising...
reading temp mode: sine
*** DS18B20 setting attributes:
genDebug: 0
owDebug: 0
temperature: 0.000000
familyCode: 28
minTemp: -55.000000
maxTemp: 125.000000
temp_freq: 100.000000
temp_mode: 2
deviceID: 28000000000002a2
DS18B20 chip initialised
*** DS18B20 chip initialising...
reading temp mode: sine
*** DS18B20 setting attributes:
genDebug: 0
owDebug: 0
temperature: 0.000000
familyCode: 28
minTemp: -55.000000
maxTemp: 125.000000
temp_freq: 100.000000
temp_mode: 2
deviceID: 28000000000003fc
DS18B20 chip initialised
*** DS18B20 chip initialising...
reading temp mode: sine
*** DS18B20 setting attributes:
genDebug: 0
owDebug: 0
temperature: 0.000000
familyCode: 28
minTemp: -55.000000
maxTemp: 125.000000
temp_freq: 100.000000
temp_mode: 2
deviceID: 280000000000047f
DS18B20 chip initialised
*** DS18B20 chip initialising...
reading temp mode: sine
*** DS18B20 setting attributes:
genDebug: 0
owDebug: 0
temperature: 0.000000
familyCode: 28
minTemp: -55.000000
maxTemp: 125.000000
temp_freq: 100.000000
temp_mode: 2
deviceID: 2800000000000521
DS18B20 chip initialised
NOTE There are 5
DS18B20 chip initialised
. Each of which hasfamilyCode: 0x28
and, although thedeviceID
are presentfamily-code:address:CRC
(instead ofCRC:address:family-code
) and, the CRCs disagree with my calculations (see below), you can see e.g.000000000001
and on.
1-Wire devices have a hard-coded ROM 64-bit (8-byte) address comprising most-significant byte CRC, 6 bytes of “address” and a least-significant byte called the family code, uniquely identifying the type of sensor (in this case DS18B20 are 0x28
(40
)).
Interestingly if your printer rejects a cartridge or your Apple computer dislikes a power supply, it’s likely because these peripherals use 1-Wire devices and the host device is decoding the peripheral’s ROM to determine whether it’s a valid peripheral.
There are 2 CRCs used by 1-Wire. These are documented. My (Rust) code appears to be an accurate implementation of the 8-bit 1-Wire CRC but my CRC calculations don’t agree with those produced by the Wokwi device:
CRC | Serial Number | Family Code | Description |
---|---|---|---|
0xa2 |
0x00000001B81C |
0x02 |
Example given in documentation |
0x29 |
0x000000000001 |
0x28 |
Wokwi DS18B20 #1 |
0x70 |
0x000000000002 |
0x28 |
Wokwi DS18B20 #2 |
0x47 |
0x000000000003 |
0x28 |
Wokwi DS18B20 #3 |
0xc2 |
0x000000000004 |
0x28 |
Wokwi DS18B20 #4 |
0xf5 |
0x000000000005 |
0x28 |
Wokwi DS18B20 #5 |
NOTE My CRC calculator agrees with the documented example and validates with the CRC check (prepending the CRC to the ROM) and recalculating results in
0x00
.
The Rust example is implemented using the onewire
crate.
Unfortunately, it fails to read temperatures from any of the DS18B20s:
SP-ROM:esp32c3-api1-20210207
Build:Feb 7 2021
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd6100,len:0x420
load:0x403ce000,len:0x90c
load:0x403d0000,len:0x2370
entry 0x403ce000
INFO - Logger initialized
INFO - Embassy initialized
INFO - LED on
INFO - [ds18b20_task] Task started
INFO - Presence pulse detected
INFO - Adding temperature sensor ([40, 0, 0, 0, 0, 0, 4, 127])
INFO - Adding temperature sensor ([40, 0, 0, 0, 0, 0, 2, 162])
INFO - Adding temperature sensor ([40, 0, 0, 0, 0, 0, 1, 64])
INFO - Adding temperature sensor ([40, 0, 0, 0, 0, 0, 5, 33])
INFO - Adding temperature sensor ([40, 0, 0, 0, 0, 0, 3, 252])
INFO - Waiting for measurement to finish
INFO - Starting temperature measurement
INFO - Reading temperature
ERROR - Failed to read temperature: CrcMismatch { computed: 201, expected: 255 }
INFO - Starting temperature measurement
INFO - Reading temperature
ERROR - Failed to read temperature: CrcMismatch { computed: 79, expected: 0 }
INFO - Starting temperature measurement
INFO - Reading temperature
ERROR - Failed to read temperature: CrcMismatch { computed: 201, expected: 255 }
INFO - Starting temperature measurement
INFO - Reading temperature
ERROR - Failed to read temperature: CrcMismatch { computed: 201, expected: 255 }
INFO - Starting temperature measurement
INFO - Reading temperature
ERROR - Failed to read temperature: CrcMismatch { computed: 201, expected: 255 }
INFO - Cycle finished. Waiting 5 second
It finds the 5 DS18B20s (Adding temperature sensor
) and determines the correct family code (40
==0x28
) and serial numbers. It finds CRCs by serial number: 64
(0x40
),162
(0xa2
),252
(0xfc
) ,127
(0x7f
),33
(0x21
). The values correpond to the CRCs calculated by wokwi-ds1820-custom-chip
so I assume they are correct and my calculator is incorrect.
The MicroPython repo includes definitions of onewire
and ds18x20
. So these libraries are available by default to MicroPython code:
import ds18x20
import onewire
import time
from machine import Pin
try:
pin = machine.Pin(5, machine.Pin.OPEN_DRAIN)
except Exception as e:
pin = machine.Pin(5)
print(f"OPEN_DRAIN unavailable: {e}")
ow = onewire.OneWire(machine.Pin(5))
print(f"ow.reset() presence: {bool(ow.reset())}")
roms = ow.scan()
print(f"ow.scan() -> {[bytes(r).hex() for r in roms]}")
sensors = ds18x20.DS18X20(ow)
roms = sensors.scan()
print(f"sensors.scan(): {[r.hex() for r in roms]}")
while True:
sensors.convert_temp()
time.sleep_ms(750)
for rom in roms:
t = sensors.read_temp(rom)
print(f"{rom.hex()}: {t:.2f} °C")
time.sleep(1)
I won’t reproduce the CHIPS CONSOLE
output because it is essentially the same as when using the ESP32-C3.
Disappontingly, the Python code is unable to scan for devices
ow.reset() presence: True
ow.scan() -> []
sensors.scan(): []
The smart money (i.e. LLMs) thinks that the issue is the timing inaccuracies/specificity when using the emulator. The only way that I could prove this would be to reproduce the circuits in our simulation.
Watch this space!