DHT11 humidity sensor kernel module

Started by jmogens, December 08, 2016, 08:33:43 PM

Previous topic - Next topic

jmogens

Hello, I'm having some trouble reading from a 1-wire DHT11 humidity sensor using the dht11 IIO kernel module.

Here's my setup:

1. Olinuxino Nano booting from micro SD with Arch Linux. Clock is set to 454 MHz.

2. Using GPIO 19 / CS_UEXT for DHT11 data pin. This is declared in my device tree. Relevant portion is printed below:

/* dht11 humidity sensor */
humidity_sensor {
compatible = "dht11";
/* pin 19 from bank 0 */
gpios = <0xf 19 0>;
};


3. Using mainline kernel 4.8.8 with this config. Notable changes are CONFIG_HZ_1000=y which I thought might improve kernel timer resolution and CONFIG_DYNAMIC_DEBUG=y to enable verbose prints from the dht11 kernel module.

Now, when I try to read from the DHT11 sensor I see the following prints:

[root@alarm ~]# cat /sys/bus/iio/devices/iio\:device0/in_temp_input
[  135.977000] dht11 humidity_sensor: current timeresolution: 31250ns
[  136.104000] dht11 humidity_sensor: 77 edges detected:
[  136.109000] dht11 humidity_sensor: 1: 62500 ns high
[  136.114000] dht11 humidity_sensor: 2: 62500 ns low
[  136.119000] dht11 humidity_sensor: 3: 31250 ns high
[  136.124000] dht11 humidity_sensor: 4: 93750 ns low
[  136.129000] dht11 humidity_sensor: 5: 62500 ns low
[  136.134000] dht11 humidity_sensor: 6: 62500 ns low
[  136.139000] dht11 humidity_sensor: 7: 62500 ns high
[  136.144000] dht11 humidity_sensor: 8: 31250 ns low
[  136.149000] dht11 humidity_sensor: 9: 31250 ns high
[  136.154000] dht11 humidity_sensor: 10: 62500 ns low
[  136.159000] dht11 humidity_sensor: 11: 62500 ns high
[  136.165000] dht11 humidity_sensor: 12: 62500 ns low
[  136.170000] dht11 humidity_sensor: 13: 62500 ns high
[  136.175000] dht11 humidity_sensor: 14: 93750 ns low
[  136.180000] dht11 humidity_sensor: 15: 31250 ns high
[  136.185000] dht11 humidity_sensor: 16: 62500 ns low
[  136.190000] dht11 humidity_sensor: 17: 31250 ns high
[  136.195000] dht11 humidity_sensor: 18: 31250 ns low
[  136.200000] dht11 humidity_sensor: 19: 62500 ns high
[  136.205000] dht11 humidity_sensor: 20: 31250 ns low
[  136.210000] dht11 humidity_sensor: 21: 31250 ns high
[  136.216000] dht11 humidity_sensor: 22: 62500 ns low
[  136.221000] dht11 humidity_sensor: 23: 0 ns high
[  136.225000] dht11 humidity_sensor: 24: 62500 ns low
[  136.230000] dht11 humidity_sensor: 25: 62500 ns high
[  136.236000] dht11 humidity_sensor: 26: 0 ns low
[  136.240000] dht11 humidity_sensor: 27: 31250 ns high
[  136.246000] dht11 humidity_sensor: 28: 62500 ns low
[  136.251000] dht11 humidity_sensor: 29: 31250 ns high
[  136.256000] dht11 humidity_sensor: 30: 31250 ns low
[  136.261000] dht11 humidity_sensor: 31: 31250 ns high
[  136.266000] dht11 humidity_sensor: 32: 62500 ns low
[  136.271000] dht11 humidity_sensor: 33: 31250 ns high
[  136.276000] dht11 humidity_sensor: 34: 31250 ns low
[  136.281000] dht11 humidity_sensor: 35: 31250 ns high
[  136.286000] dht11 humidity_sensor: 36: 62500 ns low
[  136.291000] dht11 humidity_sensor: 37: 31250 ns high
[  136.297000] dht11 humidity_sensor: 38: 31250 ns low
[  136.302000] dht11 humidity_sensor: 39: 93750 ns high
[  136.307000] dht11 humidity_sensor: 40: 31250 ns low
[  136.312000] dht11 humidity_sensor: 41: 31250 ns high
[  136.317000] dht11 humidity_sensor: 42: 62500 ns low
[  136.322000] dht11 humidity_sensor: 43: 62500 ns high
[  136.327000] dht11 humidity_sensor: 44: 62500 ns low
[  136.332000] dht11 humidity_sensor: 45: 62500 ns high
[  136.337000] dht11 humidity_sensor: 46: 62500 ns low
[  136.342000] dht11 humidity_sensor: 47: 0 ns high
[  136.347000] dht11 humidity_sensor: 48: 62500 ns low
[  136.352000] dht11 humidity_sensor: 49: 31250 ns high
[  136.357000] dht11 humidity_sensor: 50: 62500 ns low
[  136.362000] dht11 humidity_sensor: 51: 0 ns high
[  136.367000] dht11 humidity_sensor: 52: 62500 ns low
[  136.372000] dht11 humidity_sensor: 53: 31250 ns high
[  136.377000] dht11 humidity_sensor: 54: 62500 ns low
[  136.382000] dht11 humidity_sensor: 55: 0 ns high
[  136.387000] dht11 humidity_sensor: 56: 62500 ns low
[  136.392000] dht11 humidity_sensor: 57: 31250 ns high
[  136.397000] dht11 humidity_sensor: 58: 62500 ns low
[  136.402000] dht11 humidity_sensor: 59: 0 ns high
[  136.407000] dht11 humidity_sensor: 60: 62500 ns low
[  136.412000] dht11 humidity_sensor: 61: 62500 ns high
[  136.417000] dht11 humidity_sensor: 62: 0 ns low
[  136.422000] dht11 humidity_sensor: 63: 31250 ns high
[  136.427000] dht11 humidity_sensor: 64: 93750 ns low
[  136.432000] dht11 humidity_sensor: 65: 93750 ns low
[  136.437000] dht11 humidity_sensor: 66: 31250 ns low
[  136.442000] dht11 humidity_sensor: 67: 93750 ns high
[  136.448000] dht11 humidity_sensor: 68: 31250 ns low
[  136.453000] dht11 humidity_sensor: 69: 31250 ns high
[  136.458000] dht11 humidity_sensor: 70: 62500 ns low
[  136.463000] dht11 humidity_sensor: 71: 62500 ns high
[  136.468000] dht11 humidity_sensor: 72: 62500 ns low
[  136.473000] dht11 humidity_sensor: 73: 125000 ns high
[  136.478000] dht11 humidity_sensor: 74: 62500 ns low
[  136.483000] dht11 humidity_sensor: 75: 62500 ns high
[  136.488000] dht11 humidity_sensor: 76: 62500 ns low
[  136.493000] dht11 humidity_sensor: Only 77 signal edges detected
cat: '/sys/bus/iio/devices/iio:device0/in_temp_input': Connection timed out


The reported timer resolution is within the acceptable range for the dht11 kernel module ("34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks"). But we're missing some signal edges (a full read is 83 edges) and some of the measured pulse widths are 0 ns.

I scoped the data pin and the signal looks clean with square edges. Wires from Olinuxino to DHT11 sensor are less than 3 cm. There is a 47k pullup resistor from the data pin to 3.3V.

Is this a problem with kernel time resolution or GPIO interrupt latency?

All of the measured pulse widths are multiples of the 31250 ns timer resolution, which makes sense. But as long as the kernel module can make a guess at whether the real world pulse width was a 0-bit with a width of 26 us or a 1-bit with a width of 70 us, the data should still be readable.

How do I measure and characterize the GPIO interrupt latency from within the kernel? Are there steps I can take to tune and improve GPIO interrupt performance?

Thanks in advance for any advice or insight!

lambda

Hi jmogens!

As it happens, I'm the author of the dht11 kernel driver and it did
development on imx233-olinunxino boards - so this driver should work
really well for you ... ;)

As software I mostly use a custom built openwrt, which is available
here: http://friends.ccbib.org/lambda/mxs/ - this is a development
build which can change/break at any time. But it might be handy for
debugging/comparing results.

CONFIG_HZ_1000=y won't improve anything. (I don't know if it might
have adverse effects by keeping the CPU busy.)

Looking at your debug output it seems that the first few edges are
missing. This usually happens if the interrupt routine is not in
the cache yet and looking it up takes too long. The driver tries
to be tolerant about this, but 77 edges are too few. IIRC we need
at least 80 edges for decoding the data. (In theory we could
reconstruct the missing edges from the checksum at the cost of
reliablity, but I'd rather avoid this if possible.)

Obvious first questions: Are you consistently at 77 edges, or does
this vary? Does it change if you try to read the sensor multiple
times in a row? What is the load of the system? Are there any other
gpio bit banging drivers active at the same time?

About your wiring: This clearly is ok, but note that some imx23
gpios have internal pullups, that can be enabled from device tree.
That's what I'm using, but don't have the .dts file at hand ATM.

HTH,
Harald

jmogens

Thanks for the reply and thanks for writing a driver for the popular DHT11 sensor.

I've set HZ back to CONFIG_HZ_100 and rebuilt. The only other enabled drivers are for the MXS LRADC and the MXS on-chip memory.

The detected edges are not always consistent. Reading the sensor at about 2 Hz gives between 77 and 81 edges detected. Every 10 or 20 tries, the driver will report that it has lost synchronization with the edges.

The load average remains under 0.35.

I tried with your openwrt-mxs-olinuxino-maxi-sdcard.img image. I decompiled the device tree blob and made the same humidity_sensor{} addition I quoted in my previous message (except I used a phandle value of 0x11, corresponding to gpio@0 in your device tree for GPIO 19).

Unfortunately, when I try to read the IIO device I get "cat: read error: I/O error". Looking at the scope shows that the DHT11 sensor is pulsing the data line.

The open-wrt image has a dynamic_debug entry in /sys/kernel/debug so I tried to enable debug prints from the dht11 module with the following:
echo -n "module dht11 +p" > /sys/kernel/debug/dynamic_debug/control
But, I don't see any prints from the dht11 module in either the console or dmesg. Should I be looking somewhere else?

lambda

Quote from: jmogens on December 09, 2016, 10:07:17 PM
I've set HZ back to CONFIG_HZ_100 and rebuilt. The only other enabled drivers are for the MXS LRADC and the MXS on-chip memory.

The detected edges are not always consistent. Reading the sensor at about 2 Hz gives between 77 and 81 edges detected. Every 10 or 20 tries, the driver will report that it has lost synchronization with the edges.

Hm, with 81 edges I think we should have some chance to successfully get some data.
Is this correct or do you always get failures?

About this synchronization error: This means that the interrupt handler really ran too late/slow to see
all edges. I see now that the log you provided in your first post actually shows such a case. (Several
low edges in a row.) The odd thing is: I never had this on an imx233 unless it was under quite heavy load.

So yes, looks like a real interrupt latency issue. Maybe some driver is doing something nasty like
disabling all interrupts for a bit too long or something. If the only driver running really is mxs-lradc,
then maybe try unloading that one?

Quote
I tried with your openwrt-mxs-olinuxino-maxi-sdcard.img image. I decompiled the device tree blob and made the same humidity_sensor{} addition I quoted in my previous message (except I used a phandle value of 0x11, corresponding to gpio@0 in your device tree for GPIO 19).

I use the ext4-image myself, but it shouldn't matter.

Quote
Unfortunately, when I try to read the IIO device I get "cat: read error: I/O error". Looking at the scope shows that the DHT11 sensor is pulsing the data line.

Indeed. I/O error means that enough edges where detected to decode the data, but checksum test failed.
This can happen, but it should be rare - unless there is lots of RF noise in the air, that makes the
gpio controller detect extra edges. (In theory something else might be driving the gpio, but I guess this
is quite unlikely if you are running a clean mainline kernel.)

Quote
The open-wrt image has a dynamic_debug entry in /sys/kernel/debug so I tried to enable debug prints from the dht11 module with the following:
echo -n "module dht11 +p" > /sys/kernel/debug/dynamic_debug/control
But, I don't see any prints from the dht11 module in either the console or dmesg. Should I be looking somewhere else?

Hm, probably my last build was just without dynamic debug support for the dht11 module.

Anyway, if you see the some problems with my image too, then it probably doesn't matter which one you
are using for debugging. If you see different issues with the two systems, then we have at least
two problems to debug ...

I should also note, that I did test the driver with both DHT11 and DHT22, but I only use DHT22 in
production, so I have much more experience with that one. It is unlikely, but possible, that I got
the decoding for the DHT11 data format wrong in some corner cases and that this causes the checksum
error you are seeing with my image.

HTH,
Harald


BTW: This is the .dts file I'm using:


#include "imx23-olinuxino.dts"

/ {
apb@80000000 {
apbh@80000000 {
pinctrl@80018000 {
dht22_pin1: dht22a@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0073 /* MX23_PAD_GPMI_D07__GPIO_0_7 */
>;
fsl,drive-strength = <0>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
dht22_pin2: dht22b@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0063 /* MX23_PAD_GPMI_D06__GPIO_0_6 */
>;
fsl,drive-strength = <0>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
dht22_pin3: dht22c@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0023 /* MX23_PAD_GPMI_D02__GPIO_0_2 */
>;
fsl,drive-strength = <0>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
button_pin1: buttonein@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0053 /* MX23_PAD_GPMI_D05__GPIO_0_5 */
>;
fsl,drive-strength = <0>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
button_pin2: buttonaus@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0043 /* MX23_PAD_GPMI_D05__GPIO_0_4 */
>;
fsl,drive-strength = <0>;
fsl,voltage = <1>;
fsl,pull-up = <1>;
};
supply_pin1: supplyhack@0 {
reg = <0>;
fsl,pinmux-ids = <
0x0103 /* MX23_PAD_GPMI_CLE__GPIO_0_16 */
>;
fsl,drive-strength = <2>; /* 12mA */
fsl,voltage = <1>;
fsl,pull-up = <0>;
};
};
};
};

humidity_sensor_in2 {
compatible = "dht22", "dht11";
pinctrl-names = "default";
pinctrl-0 = <&dht22_pin3>, <&supply_pin1>;
gpios = <&gpio0 2 0>;
};

humidity_sensor_in {
compatible = "dht22", "dht11";
pinctrl-names = "default";
pinctrl-0 = <&dht22_pin1>, <&supply_pin1>;
gpios = <&gpio0 7 0>;
};

humidity_sensor_out {
compatible = "dht22", "dht11";
pinctrl-names = "default";
pinctrl-0 = <&dht22_pin2>, <&supply_pin1>;
gpios = <&gpio0 6 0>;
};

gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&button_pin1>, <&button_pin2>;

buttonein {
label = "start"; /* heizwunsch od. fan */
gpios = <&gpio0 5 0>;
linux,code = <256>; /* BTN_0 */
};
buttonaus {
label = "nichtheizwunsch";
gpios = <&gpio0 4 0>;
linux,code = <257>; /* BTN_1 */
};
};
};

jmogens

Quick update with some good news.

Using the OpenWrt SD card image and a DHT22 sensor I get successful reads.

Combinations that do not work:
* OpenWrt + DHT11
* Kernel 4.8.8/Arch Linux + DHT11
* Kernel 4.8.8/Arch Linux + DHT22 + device tree declaring humidity sensor 'compatible = "dht22", "dht11";'

I'm glad that the hardware and kernel module is working. I need to do more investigating to see what the differences are between OpenWrt at kernel 4.4.14 and Arch Linux at kernel 4.8.8.

lambda

Quote from: jmogens on December 12, 2016, 09:35:25 PM
Using the OpenWrt SD card image and a DHT22 sensor I get successful reads.

Combinations that do not work:
* OpenWrt + DHT11

Thanks for the report. I've put reproducing/debugging this on my TODO list.
Might take a while though ...

Thanks,
Harald

lambda

Hi,

it is probably not directly related to your problem, but
this report I just got suggestes that there have been
some changes regarding timing or scheduling introduced
with 4.8:

http://www.spinics.net/lists/linux-iio/msg29901.html

Maybe this helps you track down something.

HTH,
Harald

lambda

Quote from: jmogens on December 12, 2016, 09:35:25 PM
I need to do more investigating to see what the differences are between OpenWrt at kernel 4.4.14 and Arch Linux at kernel 4.8.8.

I finally did some tests myself (with mainline 4.10-rc7) and got an error rate of about 40%. While this is tolerable for my application, it is much worse then before.

I guess the only way to pin this down is to git bisect. I'm putting that on my TODO-List, though not sure when I will have the time. If somebody works an that, please send me a PM - would be quite a waste of effort to do that chore twice.

swahren

Only some hints from my side:

* also take care of the kernel config (mxs_defconfig contains a lot of debug which slows down)
* calling gpio_direction_input() takes time

lambda

Quote from: swahren on February 19, 2017, 08:21:11 PM
* calling gpio_direction_input() takes time

Do you have numbers? I see a number of indrect calls which will cause
a number of cache misses, but I didn't expect this to be a problem so
far.

I guess there is a problem if execution gets interrupted betwen calling
gpio_direction_input() and requesting the interrupt. But this should be
rare, so we can just live with it.

swahren

Quote from: lambda on February 28, 2017, 08:21:58 PM
Quote from: swahren on February 19, 2017, 08:21:11 PM
* calling gpio_direction_input() takes time

Do you have numbers? I see a number of indrect calls which will cause
a number of cache misses, but I didn't expect this to be a problem so
far.

No, i haven't. AFAIK the gpio operations must be send via the AHB. But this should be microseconds not milliseconds.

lambda

I think I finally got useful results. This was quite confusing because sometimes I saw errors and sometimes I didn't and it wasn't clear what makes the difference. Turns out the interrupt latency is caused by output on the serial console! In particular if the sensor is read without pause, the "Only %d signal edges detected" message of the previous read() can interfere with the data transmission fo the current read(). I believe this explains the extremely high error rates reported by @jmogens.

From my tests it seems all of the following statements are true:
* There is no significant difference between DHT11 and DHT22
* There is no significant difference between different (mainline) kernel versions
* traffic on the USB interface causes no problems unless system becomes very busy
* booting the kernel with commandline console=null reduces sensor errors dramatically

I also tried booting with console=ttyAPP0 (instead of ttyAMA0), which should use the mxs-uart driver instead of the pl011 driver, but the difference was not big enough to draw any conclusions. Maybe both drivers could be improved, especially the pl011 driver disables interrupts a lot - I don't know whether this can be improved. I probably won't look into this further.

HTH,
Harald