Reading Maxim 1-Wire Temperature Sensors

Navigate to:

One of my favorite sensors is the Maxim DS18*20 series, a variety of inexpensive temperature sensors which communicate over a “single contact serial interface” called 1-Wire. I’ve been integrating them into a variety of projects for years now, and they’ve always been reliable, accurate, and extremely easy to use. That’s partly due to their use of a single contact for communication — valuable when you’re looking to save pins on an embedded computer or microcontroller — but also because of the built in w1_therm driver in the Linux kernel, which means they’ll work on any Linux machine with the appropriate physical interface.

For the sake of example, we’ll use a Raspberry Pi with a DS18B20 connected to the GPIO pins. You can find a wiring diagram in this blog post. We’ll load the w1_therm kernel module as well as the w1_gpio module, which uses the GPIO API to provide a 1-Wire interface:

modprobe w1_gpio
modprobe w1_therm

With the kernel modules enabled, the sensor is now accessible through the sysfs interface as individual directories. We can use the ls command with some wildcards to find information about our sensors:

$ ls -laH /sys/class/hwmon/hwmon*
total 0
drwxr-xr-x 3 root root    0 Apr 24 23:59 .
drwxr-xr-x 3 root root    0 Apr 24 23:59 ..
lrwxrwxrwx 1 root root    0 Apr 24 23:59 device -> ../../../28-000002e36be9
-r--r--r-- 1 root root 4096 Apr 24 23:59 name
drwxr-xr-x 2 root root    0 Apr 24 23:59 power
lrwxrwxrwx 1 root root    0 Apr 24 23:59 subsystem -> ../../../../../class/hwmon
-r--r--r-- 1 root root 4096 Apr 24 23:59 temp1_input
-rw-r--r-- 1 root root 4096 Apr 24 23:59 uevent

Within this directory you’ll find a variety of files, including a symlink to the device itself, the sensor name, and a file called temp1_input, which you can read to get the temperature of the sensor in units of Celsius*1000:

$ cat /sys/class/hwmon/hwmon0/temp1_input
27562

In addition, you can access more info about the sensor by reading the /sys/class/hwmon/hwmon0/device/w1_slave file, which looks something like this:

$ cat /sys/class/hwmon/hwmon0/device/w1_slave
bb 01 4b 46 7f ff 05 10 c6 : crc=c6 YES
bb 01 4b 46 7f ff 05 10 c6 t=27678

This shows the output of the device in hex on the left, with the results of the CRC error checking and temperature reading on the right.

Telegraf's Temp plugin

Telegraf’s temp input plugin reads data about temperature sensors from the filesystem during each collection. You can install Telegraf and InfluxDB on a Raspberry Pi using the official InfluxData repositories. Once installed, we can configure Telegraf by uncommenting the following section in /etc/telegraf/telegraf.conf:

# # Read metrics about temperature
[[inputs.temp]]
# # no configuration

Since there is no configuration beyond enabling the plugin, we can restart Telegraf and we should see data begin to appear in the temp measurement of the telegraf database (since we’re using the configuration defaults.)

Using multiple sensors

Unfortunately, there is one shortcoming with the current implementation of the temp plugin: it only supports a single sensor. This is because of an issue with the DS18*20 sensors and the library that Telegraf’s temp plugin uses to access the filesystem: gopsutil.

When reading temperatures using gopsutil, the library generates a SensorKey using data from the filesystem, including a label file. This is what the kernel documentation has to say about those files:

temp[1-*]_label	Suggested temperature channel label.
		Text string
		Should only be created if the driver has hints about what
		this temperature channel is being used for, and user-space
		doesn't. In all other cases, the label is provided by
		user-space.
		RO

Because the application of a DS18B20 and other similar sensors can vary, the driver is generic and does not have any information about what the temperature channel is being used for, so this file is not created. That means when Telegraf collects data from multiple sensors their tag values are identical, as they all have the same SensorKey. So even though we’re collecting data from each sensor, the data points have the same metadata and timestamp, and so will overwrite one another. We can verify this by running Telegraf using the --test argument, which will print the line protocol to the command line.

Multiple sensor workaround using Python

One way we can work around this issue is by using a Python script to read the data (you can find an example gist here), and Telegraf’s exec plugin to execute the script.

Download the script and copy it to a more permanent location; for now, let’s drop the file in /usr/local/bin. Because we installed Telegraf using the official InfluxData repository, Telegraf runs as the telegraf user, which means we’ll also want to change the permissions on the script so it can be executed by Telegraf, as follows:

sudo chown telegraf:telegraf /usr/local/bin/read_multiple_ds18b20.py
$ sudo chmod 755 /usr/local/bin/read_multiple_ds18b20.py

Then we can configure Telegraf’s exec plugin to execute the script by enabling the plugin and adding the file to the commands parameter:

# # Read metrics from one or more commands that can output to stdout
[[inputs.exec]]
#   ## Commands array
  commands = [
    "/usr/local/bin/read_multiple_ds18b20.py"
  ]

The full configuration file is here, and includes the comments that describe the default values. Since multiple DS18B20s can share a single data line, connecting an additional device is fairly straightforward: it will share the connections to ground, power, and data that the first sensor uses. Restart telegraf and it should start collecting data from each device.

Future improvements

I have a few applications in mind that use multiple temperature sensors per device, so it would be nice to iterate on the built-in temp plugin if possible, rather than relying on our Python script workaround.

One possibility would be to make a small change to the gopsutil library which would add an additional field to the SensorKey struct for the device name (unique for each DS18*20). Telegraf could then use that field to add an additional tag to the data and prevent the points from overwriting each other. I’m going to put together a Pull Request and get some feedback from the gopsutil maintainers. In the meantime, feel free to use my Python script, and reach out on our community site or to me directly at [email protected] or on Twitter @noahcrowley if you have any issues!