The Raspberry Pi, a temperature sensor and the device tree.

Introduction

I have a few LM75 temperature sensors lying around. These temperature sensors have an I2C-interface and because of that they should be easy to connect to the Raspberry Pi. A lot of guides exist on the internet about how to read the temperature from these sensors. But most of these guides use i2c-tools and use raw I2C read commands to get the data from the sensor. That while there is a kernel module available for the sensor. It makes your wonder why they use raw I2C and not the kernel module. So I went on and tried to get my room temperature with the kernel module instead of i2c-tools.

Setting up

LM75 modules I have are from an older temperature and fan speed controller gimmick. They are already soldered to a tiny piece of PCB with the wires sticking out. With some additional wires, they were easily attached to the Raspberry Pi. The Vcc was attached to a 3.3V output of the Pi, the GND of course to the GND, SDA to pin 2 of the Pi and SCL to pin 5. On the LM75, pin 5, 6 and 7 on the LM75 can be used to configure the last 3 bits of the address. By configuring each sensor with a different address, you can attach up to 8 temperature sensors to a single I2C bus. My modules have a preconfigured value for these inputs. If you have your own configurable modules, pick an address, but make sure that each module has a different configuration if you attach multiple sensors. The modules have an address between 0x48 and 0x4F

For your kernel make sure that you have the lm75 kernel module and preferably the i2c-dev module. The i2c-dev is by default contained in the default Raspberry Pi kernel. I'm not sure about the lm75 module.

Raw I2C

When the modules are attached, some debugging on whether the modules are correctly attached can be done with the i2c-tools package. This package has a number of utilities to send raw commands over an I2C bus. With i2cdetect, the whole bus is scanned for devices, and any device that responds is listed. It can be used to automatically detect the address of the LM75 on the I2C bus. In my case I got this on i2c-1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
root@raspberrypi:~# i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x03-0x77.
Continue? [Y/n] y
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- 4c -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

Make sure that you have loaded the i2c-dev kernel module if this command gives you trouble. If you're output stays empty, try the other I2C buses on your Pi. The Raspberry Pi has 3 I2C buses so if you don't detect your modules, give the other buses a try. As you can see, my Raspberry Pi has two modules attached, one on address 0x48 and one on address 0x4c.

device tree

Since I thought it would be nice to change the existing device tree file instead of taking a device tree file from the kernel again, I took the dtb file from the /boot of my Raspberry Pi and decompiled it to a dts:

1
root@raspberrypi:~# dtc -I dtb -O dts -o /root/bcm2835-zero.dts /boot/bcm2835-rpi-zero.dtb

With this I have a device tree file directly from the blob build. Although this is a complete and certainly human readable file, it's not as nice as the one that comes with the kernel. The binary doesn't contain the nicely labeled structure the kernel has, so the device tree file reulting from the binary also doesn't have these references. Still, most of the things can be found, including the I2C device bus specifications. I used the file from the binary this time mainly because I did have quick access to the binary blob, but not to the kernel dts source

For each sensor, I want to include the following snippet:

1
2
3
4
5
lm75@48 {
        compatible = "lm75";
        reg = <0x48>;
        status = "okay";
};

This snippets describes an LM75 sensor. This one specifically at address 0x48. It is compatible with the lm75 kernel driver and is at register 0x48. Finally specify the status = "okay" to enable it. Since I have two of these sensors, I also included almost the same snippet in my code, but with the address changed to 0x4C.

Now, this snippet should be placed under the I2C bus it belongs to. Linux simply enumerates all I2C buses and gives them a number, but the device tree specifies them by address. To match these, remember which bus you had to use in your i2cdetect command. In my case it was i2c-1. Now I could find under /sys/class/i2c-adapter/i2c-1/ a file that has addresses in it.

root@raspberrypi:~# cat /sys/class/i2c-adapter/i2c-1/uevent
OF_NAME=i2c
OF_FULLNAME=/soc/i2c@7e804000
OF_COMPATIBLE_0=brcm,bcm2835-i2c
OF_COMPATIBLE_N=1

The OF_FULLNAME matches the device tree node of I2C bus number 1. So my final device code snippet should be placed as following, omitting the rest of the file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
i2c@7e804000 {
    compatible = "brcm,bcm2835-i2c";
    reg = <0x7e804000 0x1000>;
    interrupts = <0x2 0x15>;
    clocks = <0x7 0x14>;
    #address-cells = <0x1>;
    #size-cells = <0x0>;
    status = "okay";
    clock-frequency = <0x186a0>;

    lm75@48 {
        compatible = "lm75";
        reg = <0x48>;
        status = "okay";
    };

    lm75@4c {
        compatible = "lm75";
        reg = <0x4c>;
        tatus = "okay";
    };
};

The full device tree file was compiled to the binary format and replaced the original file in my /boot. With that in place hit the reboot.

Result

After the reboot, the lm75 module should be loaded and aware of the temperature sensors. My dmesg said the following:

1
2
[    9.160610] lm75 1-0048: hwmon0: sensor 'lm75'
[    9.186741] lm75 1-004c: hwmon1: sensor 'lm75'

In /sys/class/hwmon, both temperature sensor have gotten an entry, and most importantly:

root@raspberrypi:/home/pi# sensors
lm75-i2c-1-48
Adapter: bcm2835 I2C adapter
temp1:        +22.5�°C  (high = +80.0�°C, hyst = +75.0�°C)

lm75-i2c-1-4c
Adapter: bcm2835 I2C adapter
temp1:        +23.5�°C  (high = +80.0�°C, hyst = +75.0�°C)

The lm-sensors package nicely picks up both sensors and displays the current temperature. Blame the encoding errors on my serial terminal to the Pi, I was too lazy to set up networking, and this was the most comfortable way to connect without having to attach my display and keyboard.

Conclusion

With this it should be possible to read out the sensors with the libsensors library. For example Python can use the PySensors package. SNMP tools such as cacti should also be able to pick up the sensors now. In my opinion, this is cleaner approach to reading the temperature from the sensors than by using raw I2C commands. Another improvement is that other packages that use libsensors can use the temperature sensors now, resulting in a neater integration between tools.

Pages

Categories

Tags