Connecting sensor to the RPi
DHT-11 is a very cheap temperature/humidity sensor which is commonly used in the IoT devices. It is not very accurate, so for the accurate measurement i would recommend to use DHT21 instead. Anyway, i had DHT-11 in my tool box, so decided to start with it. DHT-11 using very simple 1 wire protocol – host is turning on chip by sending 18ms low signal to the data output and then reading 40 bytes of data. Details about the protocol could be found in the specification. To read data from the chip it should be connected to the power (5v) and gpio pin. I used pin 2 as VCC, 6 as GND and 11 as GPIO (it is GPIO17, see pinout):

FreeBSD support
There is no support for this device out of the box. I found some sample code on the github, see lex/freebsd-gpio-dht11 repository. This code was a good starting point, but soon i found 2 issues with it:
- Results are very unreliable, probably due to gpio decoding algorithm.
- Checksum is not validated, so sometime values are bogus.
Initially i was thinking to fix this myself, but later found kernel module for this purpose, 1 wire over gpio. This module contains DHT11 kernel driver (gpio_sw) which implements DHT-11 protocol in the kernel space and exporting /dev/sw0 for the userland. Driver compiles on FreeBSD11/ARM without any changes. Use make install to install the driver.
Putting all the things together
- To specify GPIO pin with a sensor put
hint.gpio_sw.0.pin=17into /boot/loader.conf. - I found that gpio_sw.ko needs to be loaded after kernel initialization, or device is not created. So i have added
/sbin/kldload gpio_sw.koto the /etc/rc.local file. - To make /dev/sw0 available for the non-root users you should add
devfs_system_ruleset="localrules"to the /etc/rc.conf and add into /etc/devfs.rules this section:
[localrules=10] add path 'sw0' mode 0644
- Sample program (
test.c) shows non human-readable data, use something likeprintf("h:%d.%d %%, t:%d.%d C\n",Buf[0],Buf[1],Buf[2],Buf[3]);if you want to get humidity and temperature, e.g.
root@rpi-b:/home/freebsd/gpio_sw # ./test h:22.0 %, t:12.0 C Hum=5632 Tem=3072
That is it, after reboot you should have working /dev/sw0 device.
Solving problems with LUA
Final goal was to add this sensor to the domoticz software. It is using LUA scripting to extend it functionality, e.g. to obtain data from non-supported or non standard devices. So, i decided to read /dev/sw0 from the LUA. I wrote simple test script:
file = io.open ("/dev/sw0", "rb")
out = file:read (5)
print(string.byte(out).."."..string.byte(out,2).."%;"..string.byte(out,3).."."..string.byte(out,4).."C")
file:close()
However script was always returning nil and i had to use truss tool to understand the problem. This is part of the trace:
open("/dev/sw0",O_RDONLY,0666) = 3 (0x3)
fstat(3,{ mode=crw-r--r-- ,inode=96,size=0,blksize=4096 }) = 0 (0x0)
ioctl(3,TIOCGETA,0xbfbfe28c) = 0 (0x0)
read(3,0x2065b000,4096) ERR#22 'Invalid argument'
As you could see – LUA trying to read 4096 bytes, despite the fact that we specified 4. And driver checks this and returns the error. To fix this i did a small patch to avoid error and make LUA happy:
-- gpio_sw/gpio_sw.c 2014-05-12 11:26:51.000000000 +0000
+++ gpio_sw.mod/gpio_sw.c 2017-01-14 14:10:33.736813000 +0000
@@ -273,9 +273,10 @@
{
duprintf("read - start, uio_resid=%i\n", uio->uio_resid);
struct gpio_sw_softc *sc = cdev->si_drv1 ;
- if ( uio->uio_resid >= sc->BufSize) return EINVAL ;
+ if ( uio->uio_resid >= sc->BufSize) sc->Len=sc->BufSize-1 ; // to work with buffered IO
+ else sc->Len=uio->uio_resid;
+
if ( ! sc->GpioStatus) return ENXIO ;
- sc->Len = uio->uio_resid ;
int i = 0 ;
while ( i < sc->Len)
{
After modification script works as it should:
[root@rpi-b /home/freebsd]# lua52 test.lua 22.0%;12.0C
Now chip is connected to the domoticz and reporting actual temperature/humidity.
Hi,
I’ve written similar driver, but to expose it via sysctl (automatically updated each 5 seconds): https://github.com/Cka3o4Huk/freebsd/commit/2cff7953f44411a9076cdb3bcb0867f531ebe0a2
Taking into account recent prometheus_sysctl_exporter, it makes easy to monitor it via grafana/prometheus stack.
Great, thank you for comment! However for me its better to have one in non-automatic mode, because i also would like to play with it from userland and dont want to have any conflicts. I will later update article and add your link.
btw,
“`
+ sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
+ sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
“`
is valid only for the dht11, not for the dht21/22
Nice Tutorial. I had done using Arduino – https://techzeero.com/arduino-tutorials/dht11-with-arduino/
[…] time ago my DHT22 sensor, connected to the RPi1, died (humidity always was showing 99%), so I decided to replace it. I found a cheap AM2331 sensor […]