DHT11 an Pi anschließen, seltsame Werte

Also wenn ich den DHT11-Sensor an Arduino anschließe (data, vcc, gnd) und die C Adafruit Lib einsetze (https://github.com/adafruit/DHT-sensor-library), bekomme ich genaue Werte - aber wenn ich den Sensor an den Pi anschließe (komplett gleiches Pinout) und pi4j (https://pi4j.com/1.2/) und dieses Snippet (https://stackoverflow.com/a/34976602) einsetze, oder wenn ich die „originale“ Python Lib (https://github.com/adafruit/Adafruit_Python_DHT und https://github.com/adafruit/Adafruit_Python_DHT/blob/master/source/Raspberry_Pi_2/pi_2_dht_read.c) einsetze, dann bekomme ich oft keine oder nur ungenaue Werte, zum Beispiel nur 23 oder 24 Grad. Weiß jemand, woran das liegen könnte? Ist der Sensor kaputt?

Also die Frage wäre ganz einfach: Wieso liefert derselbe Sensor ungenauere (und höhere) Werte (z. B. 24.0 Grad, wenn das Kontrollthermometer 22.9 zeigt - anstatt z. B. 22.8 Grad), wenn ich ihn an ein anderes Board anschließe und nicht die native C-Bibliothek verwende? Wird einfach i-wo nach int abgerundet, eine Nachkommastelle „vergessen“ oder ist das Board/der Sensor schuld?

Hab versucht, die adafruit/DHT-sensor-library so umzuschreiben, dass sie auf dem Pi lauffähig wäre, konkret heißt das, anstatt #include "Arduino.h" #include <wiringPi.h> verwenden (die ist bei Raspbian zur Steuerung der GPIOs bereits vorinstalliert), und ca. 500 Zeilen der Lib. ändern; leider ist mir dabei irgendwo ein Fehler unterlaufen, und ich verstehe auch „das Protokoll“ des DHTs nicht, wodurch der Sensor einfach immer 1 sendete (na, immerhin etwas)… Am einfachsten wird es wohl sein, einen Temperatursensor(-Kit) für den Pi mit einer hohen Auflösung zu bestellen.

Danke dennoch!

Ach ärgerlich, der Sensor ist nicht kaputt, der Pi ist nicht schuld und meine Wenigkeit auch nicht. Sowohl https://stackoverflow.com/a/34976602 wie auch adafruit/Adafruit_Python_DHT haben die vom Sensor gesendeten Daten falsch interpretiert… Die SO-Methode hab ich so geändert:
Vorher:

    public void getTemperature(final int pin) {
        int laststate = Gpio.HIGH;
        int j = 0;
        dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

        Gpio.pinMode(pin, Gpio.OUTPUT);
        Gpio.digitalWrite(pin, Gpio.LOW);
        Gpio.delay(18);

        Gpio.digitalWrite(pin, Gpio.HIGH);
        Gpio.pinMode(pin, Gpio.INPUT);

        for (int i = 0; i < MAXTIMINGS; i++) {
            int counter = 0;
            while (Gpio.digitalRead(pin) == laststate) {
                counter++;
                Gpio.delayMicroseconds(1);
                if (counter == 255) {
                    break;
                }
            }

            laststate = Gpio.digitalRead(pin);

            if (counter == 255) {
                break;
            }

            /* ignore first 3 transitions */
            if (i >= 4 && i % 2 == 0) {
                /* shove each bit into the storage bytes */
                dht11_dat[j / 8] <<= 1;
                if (counter > 16) {
                    dht11_dat[j / 8] |= 1;
                }
                j++;
            }
        }
        // check we read 40 bits (8bit x 5 ) + verify checksum in the last
        // byte
        if (j >= 40 && checkParity()) {
            float h = (float) ((dht11_dat[0] << 8) + dht11_dat[1]) / 10;
            if (h > 100) {
                h = dht11_dat[0]; // for DHT11
            }
            float c = (float) (((dht11_dat[2] & 0x7F) << 8) + dht11_dat[3]) / 10;
            if (c > 125) {
                c = dht11_dat[2]; // for DHT11
            }
            if ((dht11_dat[2] & 0x80) != 0) {
                c = -c;
            }
            final float f = c * 1.8f + 32;
            System.out.println("Humidity = " + h + " Temperature = " + c + "(" + f + "f)");
        } else {
            System.out.println("Data not good, skip");
        }

    }

Nachher:

	public Optional<float[]> getTemperature(final int pin) {
		int laststate = Gpio.HIGH;
		int j = 0;
		dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;

		Gpio.pinMode(pin, Gpio.OUTPUT);
		Gpio.digitalWrite(pin, Gpio.LOW);
		Gpio.delay(18);

		Gpio.digitalWrite(pin, Gpio.HIGH);
		Gpio.pinMode(pin, Gpio.INPUT);

		for (int i = 0; i < MAXTIMINGS; i++) {
			int counter = 0;
			while (Gpio.digitalRead(pin) == laststate) {
				counter++;
				Gpio.delayMicroseconds(1);
				if (counter == 255) {
					break;
				}
			}

			laststate = Gpio.digitalRead(pin);

			if (counter == 255) {
				break;
			}

			/* ignore first 3 transitions */
			if (i >= 4 && i % 2 == 0) {
				/* shove each bit into the storage bytes */
				dht11_dat[j / 8] <<= 1;
				if (counter > 16) {
					dht11_dat[j / 8] |= 1;
				}
				j++;
			}
		}

		for (int i = 0; i < 4; i++) {
			if (dht11_dat[i] == 255) {
				return Optional.empty();
			}
		}

		float h = (dht11_dat[0] * 10 + dht11_dat[1]) / 10f;
		float t = (dht11_dat[2] * 10 + dht11_dat[3]) / 10f;
		float f = Math.round(t * 18f + 320f) / 10f;
		if (h >= 0 && h <= 100 && t >= 0 && t <= 66) {
			return Optional.of(new float[] { h, t, f });
		}
		return Optional.empty();
	}

Test:

		final int pint = 4;
		final DHT11 dht = new DHT11();

        for (int i = 0; i < 1000; i++) {
        	Thread.sleep(10000);
            Optional<float[]> temperature = dht.getTemperature(pint);
            while (temperature.isEmpty()) {
            	Thread.sleep(10000);
            	temperature = dht.getTemperature(pint);
            }
            System.out.println(Arrays.toString(temperature.get()));
        }

        System.out.println("Done!!");

Der Sensor ist (sogar) auf 1/10 Grad genau.
Also entweder ein Fehler + Folgefehler in der Python Lib., oder aber ich hab anders als beschrieben ist gar keinen DHT11-Sensor. :wink:

cool :slight_smile:
Und was war jetzt genau der Fehler? Ich seh die Unterschiede ganz unten, aber das ist ja komplett anders als vorher :open_mouth:

Also der Unterschied ist folgender:
Der Sensor liefert 4 Byte. Das erste Byte ist die Luftfeuchtigkeit in Prozent, das zweite Byte ist immer 0, das dritte und vierte Byte ist die Temperatur, wobei drittes Byte 1er- und viertes Byte Zehntelstellen sind. Es gibt ggfs. ein 5. Byte, das eine Prüfsumme sein könnte, aber das weiß ich nicht genau.
Nun ist:
float h = (float) ((dht11_dat[0] << 8) + dht11_dat[1]) / 10;
etwas komplett anderes als:
float h = (dht11_dat[0] * 10 + dht11_dat[1]) / 10f;
. Ersteres entspricht einer Multiplikation mit 256.

Also, ich belebe das Thema noch mal. Soweit ich das verstanden hab (du wirst dich besser damit auskennen), muss man dem Sensor initial mindestens 10 Nanosekunden lang ein hohes Signal an data senden, daraufhin sendet der Sensor fortwährend abwechselnd ein hohes und niedriges Signal von data. Zuerst die rel. Luftfeuchtigkeit in Prozent, dann die Temperatur in Grad. Wenn der Sensor zum Beispiel 23*15=345 Nanosekunden lang ein hohes Signal sendet und anschließend 3*15=45 Nanosekunden lang ein niedriges Signal sendet (also kein Signal), ist ist die Temperatur 23,3 Grad. Die ersten vier Signalwechsel von hoch/tief oder tief/hoch werden allerdings verworfen. Da ich mir allerdings nicht sicher bin, welches Bausteinchen ich genau habe und auch kein datasheet gelesen habe, ist viel Spekulation dabei. Googelt man nach DHT11 sensor, so erhält man Bildchen von ganz unterschiedlichen Modellen.

Meiner hatte auch immer 2° Unterschied, deshalb find ich das interessant :slight_smile:
Soweit ich das sehe nutzt die Bibliothek das letzte Bit als Parity-Bit. Jetzt sagst du aber, er sendet 5 bytes - kann gut sein, dass das 5. byte die checksum ist.

Die ersten 4 bit werden verworfen? Sind die ersten 4 bit immer 1010?

raw: 00011001 11010101 00010101 01010101 01110111 11010101 01010111 01110111 01011110 01111111
raw: 01110101 11110101 10010101 01010101 00000101 01110111 11010101 01010011 11011100 01111111
raw: 01110101 11110101 10010101 01010101 01000101 01110111 11010101 01010111 11110110 01011111
raw: 01110101 11110101 10010101 01010101 01000101 11011111 01010101 01011111 11011100 01111111
raw: 00111101 01110101 00010101 01010101 01001101 11110001 01010111 11110111 01011110 11111111
raw: 01110101 11110101 10110101 01010101 01000101 01110111 11110101 01010101 00010100 11011101

Kann man daraus etwas erkennen? Die Lauffeuchtigkeit sollte zwischen 40 und 45 % liegen, die Temperatur etwa bei 23 Grad.

Da sind jeweils 10 byte, aber sollten es nicht immer nur 5 bytes sein? :smiley:

Es gibt genügend Datasheets im Internet für den DHT11 https://www.mouser.com/datasheet/2/758/DHT11-Technical-Data-Sheet-Translated-Version-1143054.pdf
Danach bedeutet ein 26-28µs HIGH „0“ und ein 70µs HIGH „1“. Und die einzelnen Bits (HIGH) werden immer durch ein 50µs LOW voneinander getrennt.

Jede Kommunikation sieht kurz gefasst also so aus:

µC an DHT11: 18µs LOW - 40µs HIGH
DHT11 an µC: 80µs LOW - 80µs HIGH
DHT11 an µC: 2 byte Luftfeuchte
DHT11 an µC: 2 byte Temperatur
DHT11 an µC: 1 byte Checksum (4 bytes addiert)

Wobei beim DHT11 das zweite byte von Temperatur und Luftfeuchte immer 0 ist. Was bei dir nicht der Fall ist. Und die Checksum stimmt auch nicht.

(Beitrag wurde vom Autor zurückgezogen und wird automatisch in 2400 Stunden gelöscht, sofern dieser Beitrag nicht gemeldet wird)

@TMII Was wäre hieran falsch? Der Sensor liefert immer 00000000 00000000 00000000 00000000 00000001.

	public DHT11() {

		// setup wiringPi
		if (Gpio.wiringPiSetup() == -1) {
			System.out.println(" ==>> GPIO SETUP FAILED");
			return;
		}

		// GpioUtil.export(3, GpioUtil.DIRECTION_OUT);
	}

	private String getRawData(int pin) {
		StringBuilder sb = new StringBuilder();

		Gpio.pinMode(pin, Gpio.OUTPUT);
		Gpio.digitalWrite(pin, Gpio.LOW);
		Gpio.delay(20);
		Gpio.digitalWrite(pin, Gpio.LOW);
		Gpio.delayMicroseconds(30);
		Gpio.pinMode(pin, Gpio.INPUT);
		readUntil(pin, Gpio.HIGH, 80);
		readUntil(pin, Gpio.LOW, 80);

		for (int i = 0; sb.length() < 8*5; i++) {
			readUntil(pin, Gpio.HIGH, 50);
			int c = readUntil(pin, Gpio.LOW, 80);

			if (c > 50) {
				sb.append('1');
			} else {
				sb.append('0');
			}
		}

		return sb.toString();
	}

	private int readUntil(int pin, int until, int micros) {
		int c = 0;
		while (Gpio.digitalRead(pin) != until) {
			c++;
			Gpio.delayMicroseconds(1);
			if (c == micros) {
				break;
			}
		}
		return c;
	}

Kurzes Update: @TMII hat mir gestern im Discord Chat den entscheidenden Tipp gegeben. Vielen Dank dafür! An dem Bausteinchen sollte immer etwas Strom anliegen, deshalb hab ich einen Ohm-Widerstand zwischen power und data gesteckt. Nun läuft der DHT genau spezifikationkonform (…das Wort wollte ich schon immer mal schreiben :wink: ). Der Java-Code für Pi4J mit Pi zum Ansteuern des DHT11 ist folgender:

import java.util.Optional;

import com.pi4j.wiringpi.Gpio;

public class DHT11 {
	public DHT11() {

		// setup wiringPi
		if (Gpio.wiringPiSetup() == -1) {
			System.out.println(" ==>> GPIO SETUP FAILED");
			return;
		}

	}

	private String getRawData(int pin) {
		StringBuilder sb = new StringBuilder();

		Gpio.pinMode(pin, Gpio.OUTPUT);
		Gpio.digitalWrite(pin, Gpio.LOW);
		Gpio.delay(20);
		Gpio.digitalWrite(pin, Gpio.HIGH);
		Gpio.delayMicroseconds(30);
		Gpio.pinMode(pin, Gpio.INPUT);
		readWhile(pin, Gpio.LOW, 80);
		readWhile(pin, Gpio.HIGH, 80);

		// read 6 bytes, instead of 5 bytes, just to go sure
		while (sb.length() < 8 * 6) {
			readWhile(pin, Gpio.LOW, 50);
			int c = readWhile(pin, Gpio.HIGH, 80);

			if (c > 50) {
				sb.append('1');
			} else {
				sb.append('0');
			}
		}

		return sb.toString();
	}

	private int readWhile(int pin, int val, int micros) {
		int c = 0;
		do {
			c++;
			if (c == micros) {
				break;
			}
			Gpio.delayMicroseconds(1);
		} while (Gpio.digitalRead(pin) == val);
		return c;
	}

	public Optional<float[]> getTemperature(final int pin) {
		for (int i = 0; i < 10; i++) {
			String raw = getRawData(pin);
			int h = Integer.parseInt(raw.substring(0, 8), 2);
			int c1 = Integer.parseInt(raw.substring(16, 24), 2);
			int c2 = Integer.parseInt(raw.substring(24, 32), 2);
			int sum = Integer.parseInt(raw.substring(32, 40), 2);
			if (sum != 0 && sum == (h + c1 + c2)) {
				return Optional.of(new float[] { h, (c1 * 10 + c2) / 10f });
			}
			Gpio.delay(2000);
		}
		return Optional.empty();
	}
}

Gar nicht so schwer verständlich oder?

Einen Pull-Up Widerstand :slight_smile: Und bitteschön. Freut mich das es funktioniert :slight_smile: