Raspberry Pi 3 und DHT20-Sensor Script

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
MetallSteinmetz
User
Beiträge: 1
Registriert: Montag 25. August 2025, 07:17

Servus miteineander,

Mein Name ist Tom und ich tüftel gerne.

Ich habe an meinen alten Raspi B3+ einen DHT 20 Sensor angeschlossen und habe Ihn auch dazu gebracht mir Werte anzuzeigen.
Nun bin ich aber in Python absoluter nichtskönner. Ich habe versucht aus zwei Scripten eines zu basteln.

Die Sensordaten sollen via MQTT an meinen IoBroker gehen. Ich bin auch schon soweit, dass ich die Datenpunkte in meinem Broker sehe.

Code: Alles auswählen

pi@Test:~/DFRobot_DHT20/python/raspberrypi/examples $ python3 dht20.py
Temperatur:  23.891448974609375 *C
Luftfeuchtigkeit:  39.478206634521484 RH
Allerdings wir mir das gesendet, was zwischen den " " hinter client.publish("{}/temperature".format(publish_topic)," " steht..

Nun mien Problem:
Ich weiß nicht was ich im Script eintragen muss, damit die Werte des Sensors überliefert werden.
Kann mir vielleicht jemand helfen und mir einen Tip geben, wie die Eintragung lauten muss?

Code: Alles auswählen

import time
import smbus2
import paho.mqtt.client as mqtt

address = 0x38 #Put your device's address here

i2cbus = smbus2.SMBus(1)
time.sleep(0.5)

data = i2cbus.read_i2c_block_data(address,0x71,1)
if (data[0] | 0x08) == 0:
  print('Initialization error')

broker='XXX.XXX.XXX.XXX'
port=1879
publish_topic="Testpi/pi-DHT20"
clientid='TestPi'
username='XXX'
password='XXXXXXXX'
insecure=True
qos=1
retain_message=True


i2cbus.write_i2c_block_data(address,0xac,[0x33,0x00])
time.sleep(0.1)

data = i2cbus.read_i2c_block_data(address,0x71,7)

Traw = ((data[3] & 0xf) << 16) + (data[4] << 8) + data[5]
temperature = 200*float(Traw)/2**20 - 50

Hraw = ((data[3] & 0xf0) >> 4) + (data[1] << 12) + (data[2] << 4)
humidity = 100*float(Hraw)/2**20

print("Temperatur: ", temperature, "*C")
print("Luftfeuchtigkeit: ", humidity, "RH")
print()
time.sleep(2)

#MQTT Connection
client=mqtt.Client(clientid)
client.username_pw_set(username, password)
#client.tls_set(cert_reqs=ssl.CERT_NONE) #no client certificate needed
#client.tls_insecure_set(insecure)
client.connect(broker, port)
client.loop_start()

client.publish("{}/temperature".format(publish_topic),"was muss hier rein, um den Temperaturwert zu senden")

client.disconnect()
client.loop_stop()
Danke schon mal im Voraus.

Mfg

Tom
Benutzeravatar
__blackjack__
User
Beiträge: 14093
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@MetallSteinmetz: Anmerkungen zum Quelltext: Konstanten werden per Konvention KOMPLETT_GROSS geschrieben, damit man sie als solche erkennen und von Variablen unterscheiden kann. Die werden üblicherweise auch am Anfang des Moduls nach den Importen definiert, damit man sie nicht zwischen anderem Code zusammensuchen muss. `address` würde also beispielsweie `ADDRESS` heissen. Wobei man hier dem Leser ruhig verraten darf, das es sich um die `SENSOR_ADDRESS` handelt.

Ausser Klassennamen (PascalCase) werden alle anderen Namen klein_mit_unterstrichen geschrieben. Also `t_raw` statt `Traw` aber besser noch `raw_temperature` damit man nicht raten muss wofür das `t` stehen mag.

`insecure`, `qos`, und `retain_message` werden definiert, aber nirgends verwendet.

Ressourcen wie Verbindungen auf Bussen, sollte man sauber wieder aufräumen. `SMBus`-Objekte sind netterweise Kontextmanager und können mit der ``with``-Anweisung verwendet werden, damit das `close()` am Ende sichergestellt ist.

Die ``if``-Abfrage ist kaputt weil diese Bedingung niemals wahr sein kann. Kein Wert mit 8 bitweise „oder“-verknüpft kann jemals 0 ergeben:

Code: Alles auswählen

>>> 0 in (byte_value | 0x08 for byte_value in range(256))
False
Sollte da eine funktionierende Bedingung stehen, dann es es unsinnig nach der Ausgabe eines Initialisierungsfehlers einfach so weiter zu machen, denn dann wird der Rest ja auch nicht funktionieren. Man sollte das ganze an der Stelle dann also auch abbrechen.

Die `float()`-Aufrufe sind überflüssig.

Statt dem Maskieren und Shiften von einzelnen Bytewerten könnte man auch einfach einen kompletten `int`-Wert aus den 5 Bytes machen und da dann Temperatur und Luftfeuchtigkeit extrahieren. Das ist dann nur jeweils einmal Maskieren und einmal Shiften für den jeweiligen Wert und ein bisschen übersichtlicher. Man sieht dann leichter was da passiert — dass das jeweils 20 Bit pro Wert sind und wie die in den Daten angeordnet sind.

Ich weiss nicht warum die Quelltexte für MQTT immer noch so funktionieren, denn `paho.mqtt.client.Client` erwartet seit einer gefühlten Ewigkeit schon die API-Version zwingend als erstes Argument‽

Da kommt man dann als Zwischenstand ungefähr hier heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import time

import paho.mqtt.client as mqtt
import smbus2

I2C_SENSOR_ADDRESS = 0x38

MQTT_BROKER = "XXX.XXX.XXX.XXX"
MQTT_PORT = 1879
MQTT_CLIENT_ID = "TestPi"
MQTT_USERNAME = "XXX"
MQTT_PASSWORD = "XXXXXXXX"
MQTT_PUBLISH_TOPIC = "Testpi/pi-DHT20"


def main():
    with smbus2.SMBus(1) as i2c_bus:
        time.sleep(0.5)

        data = i2c_bus.read_i2c_block_data(I2C_SENSOR_ADDRESS, 0x71, 1)
        if False:  # FIXME Eine funktionierende Bedingung an dieser Stelle.
            print("Initialization error")
            return

        i2c_bus.write_i2c_block_data(I2C_SENSOR_ADDRESS, 0xAC, [0x33, 0x00])
        time.sleep(0.1)
        data = i2c_bus.read_i2c_block_data(I2C_SENSOR_ADDRESS, 0x71, 7)
        raw_value = int.from_bytes(data[1:6], "big")
        temperature = 200 * (raw_value & (2**20 - 1)) / 2**20 - 50
        humidity = 100 * (raw_value >> 20) / 2**20

    print("Temperatur: ", temperature, "*C")
    print("Luftfeuchtigkeit: ", humidity, "RH")
    print()

    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, MQTT_CLIENT_ID)
    client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
    client.connect(MQTT_BROKER, MQTT_PORT)
    client.loop_start()
    client.publish(
        f"{MQTT_PUBLISH_TOPIC}/temperature",
        #
        # FIXME Hier muss der Temperaturwert in einer passenden Form übergeben
        #       werden.
        #
        "was muss hier rein, um den Temperaturwert zu senden",
    )
    client.disconnect()
    client.loop_stop()


if __name__ == "__main__":
    main()
Was da hin muss um den Temperaturwert zu senden? Die Temperatur. In der passenden Form/als passender Datentyp. Über die Grunddatentypen klärt das Grundlagentutorial in der Python-Dokumentation auf. Das sollte man mal durchgearbeitet haben.
“It is easier to change the specification to fit the program than vice versa.” — Alan J. Perlis
Antworten