Temp Sensor abfragen und per MQTT verschicken

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
carloss
User
Beiträge: 2
Registriert: Dienstag 11. Januar 2022, 17:36

Hallo, ich muss gleich vorausschicken, dass ich absolut noch keine Ahnung von Python habe und bis vor kurzem auch keinen Bedarf hatte das zu lernen.
Nun habe ich aber ein paar raspi Zeros liegen, die nur Temperatur und Feuchte messen und per MQTT verschicken sollen.
Ein script um die Werte auszulesen hab ich gefunden, eines um Daten per MQTT zu verschicken auch. Ich habe nun beides "zusammengefriemelt" und bin zumindest soweit gekommen, dass das script startet und die Werte einmal überträgt.
Danach scheine ich es in den Wald zu schicken, denn es kommt nicht mehr zur Kommandozeile zurück. Mit "Strg-C" lässt es sich abbrechen und auch wieder starten. Nur...sollte das script ohne mein Zutun Daten im Abstand von 10 Sekunden verschicken.
Kann mir hier bitte jemand aufs Pferd helfen?
Danke !

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------------------------------------------
# SHT31 abfragen
#------------------------------------------------------------------------
import os, sys, time
import smbus
# I2C Bus festlegen
bus = smbus.SMBus(1)

# SHT31 Adresse definieren, 0x44(68)
bus.write_i2c_block_data(0x44, 0x2C, [0x06])

time.sleep(0.5)

# Daten lesen aus 0x00(00), 6 Bytes
# Temp MSB, Temp LSB, Temp CRC, Humididty MSB, Humidity LSB, Humidity CRC
data = bus.read_i2c_block_data(0x44, 0x00, 6)

# Daten konvertieren
temp = data[0] * 256 + data[1]
cTemp = -45 + (175 * temp / 65535.0)
fTemp = -49 + (315 * temp / 65535.0)
humidity = 100 * (data[3] * 256 + data[4]) / 65535.0

# Daten zur Kontrolle anzeigen
print (("Temperatur in Celsius: %.2f C") %(cTemp))
print (("Temperatur in Fahrenheit: %.2f F") %(fTemp))
print (("Relative Feuchtigkeit: %.2f %%RH ") %(humidity))

#### MQTT Versand
import time

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print("Connected with result code " + str(rc))

client = mqtt.Client()
client.on_connect = on_connect

client.connect("localhost", 1883, 60)

client.loop_start()

while True:
    time.sleep(10)
    client.publish("data/temp", "%.2f" %cTemp)
    client.publish("data/humidity_rel", "%.2f" %humidity)
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@carloss: Was ist denn das Problem? Du hast da eine Endlosschleife die alle 10 Sekunden Daten verschickt. Natürlich kehrt das nicht zurück sondern läuft ”ewig”. Halt solange bis man es abbricht.

Du solltest Dir aber noch mal überlegen *was* Du da alle 10 Sekunden verschickst. Werte die man einmal an einen Namen gebunden hat, ändern sich nicht auf magische Weise oder wissen was sie bedeuten und das man da vielleicht alle 10 Sekunden *andere* Werte verschicken möchte.

Allgemein zum Quelltext: Sollte das tatsächlich noch mit Python 2 ausgeführt werden, solltest Du da auf Python 3 umsteigen. Python 2 hat sein Lebensende vor einem Jahr erreicht.

Importe stehen am Anfang vom Modul, damit man leicht sehen kann wovon das Modul abhängig ist. Und man sollte da nicht auf vorrat irgendwelche importe rein schreiben die dann gar nicht verwendet werden. Und auch keine doppelten Import, wie hier `time`.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten nicht kryptisch abgekürzt werden. `temp` ist eine übliche Abkürzung für `temporary`. Wenn man `temperature` meint, sollte man das auch so schreiben.

Der ``%``-Operator auf Zeichenketten war auch in Python 2 schon veraltet. In aktuellen Python-Versionen gibt es f-Zeichenkettenliterale.

Zwischenstand, immer noch mit dem Problem, dass sich die Werte die gesendet werden, natürlich nicht ändern:

Code: Alles auswählen

#!/usr/bin/env python3
import time

import paho.mqtt.client as mqtt
import smbus


def on_connect(_client, _userdata, _flags, return_code):
    print("Connected with result code", return_code)


def main():
    bus = smbus.SMBus(1)
    #
    # SHT31 Adresse definieren, 0x44.
    #
    bus.write_i2c_block_data(0x44, 0x2C, [0x06])
    time.sleep(0.5)
    #
    # Daten lesen aus 0x00, 6 Bytes.
    # Temp MSB, Temp LSB, Temp CRC, Humididty MSB, Humidity LSB, Humidity CRC
    #
    data = bus.read_i2c_block_data(0x44, 0x00, 6)

    raw_temperature = data[0] * 256 + data[1]
    temperature_in_celcius = -45 + (175 * raw_temperature / 0xffff)
    temperature_in_fahrenheit = -49 + (315 * raw_temperature / 0xffff)
    humidity = 100 * (data[3] * 256 + data[4]) / 0xffff

    print(f"Temperatur in Celsius: {temperature_in_celcius:.2f} C")
    print(f"Temperatur in Fahrenheit: {temperature_in_fahrenheit:.2f} F")
    print(f"Relative Feuchtigkeit: {humidity:.2f} %RH")

    client = mqtt.Client()
    client.on_connect = on_connect
    client.connect("localhost")
    client.loop_start()
    while True:
        time.sleep(10)
        client.publish("data/temp", f"{temperature_in_celcius:.2f}")
        client.publish("data/humidity_rel", f"{humidity:.2f}")


if __name__ == "__main__":
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
carloss
User
Beiträge: 2
Registriert: Dienstag 11. Januar 2022, 17:36

Vielen Dank für die rasche Antwort und die Hilfe. Ich werde mich da heute Abend Schritt für Schritt einlesen.
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

Mir gefällt an der aktuellen Lösung nicht, dass sie einmal den Sensor abfragt und dann immer diesen einen Wert sendet. Außerdem werden die gesendeten CRC summen ignoriert und zu guter Letzt halte ich das Senden der Temperatur über MQTT alle 10 Sekunden für ein bisschen überambitioniert, wenn du nicht gerade in einen Prozess viel Energie reinsteckst, sind Temperaturänderungen sehr träge. Beim normalen Außentemperatur messen sollte eine Auflösung von alle 15 Minuten ausreichend sein. Wenn du ein Grill beim Warmwerden überwachst, sollte eine Minutenauflösung auch ausreichen, vermutlich tun es auch 5 Minuten.

hier ist mal meine Version für den CRC Check verwende ich das crc8 Module das extra installiert werden muss, ausserdem habe ich mich für struct.unpack entschieden zum die Bytes in werte zu Konvertieren. Da ich so einen Sensor aber nicht habe, kann es sein das es dort noch ein paar kleine fehler gibt. (Konvertierung oder CRC berechnung)

Code: Alles auswählen

!/usr/bin/env python3
import dataclasses
import struct
import sys
import time

# Nicht Standard Python. Muss selbst installiert werden.
import crc8
import paho.mqtt.client as mqtt
import smbus


@dataclasses.dataclass
class Measured_value:
    value: int
    crc8: int = None

    def __post_init__(self):
        '''
        Wenn eine CRC übergeben würde überprüfe, ob sie richtig ist
        wenn nicht werfe einen ValueError.
        Sollte keine CRC übergeben werden berechne sie!
        '''
        byteorder = 'big'

        checksum = crc8.crc8()
        checksum.update(self.value.to_bytes(2, byteorder))
        calcuate = int.from_bytes(checksum.digest(), byteorder)

        if self.crc8 and self.crc8 != calcuate:
            raise ValueError(
                f'Checksum for {self.value} is wrong calculated {calcuate} given {self.crc8}')
        self.crc8 = calcuate


@dataclasses.dataclass
class Sht31Data:
    _temperature: Measured_value
    _humidity: Measured_value

    @classmethod
    def from_raw(cls, data):
        '''
        Methode zum erzeugen einer Klassen Instanz mit den Output des Sensors
        '''
        #
        # data 6 Bytes
        # +---------+---------+---------+--------------+-------------+-------------+
        # | 0       | 1       | 2       | 3            | 4           | 5           |
        # +---------+---------+---------+--------------+-------------+-------------+
        # | Temp MSB| Temp LSB| Temp CRC| Humididty MSB| Humidity LSB| Humidity CRC|
        # +---------+---------+---------+--------------+-------------+-------------+
        # | tv                | tc      | hv                         | hc          |
        # +-------------------+---------+----------------------------+-------------+
        # | H                 | B       | H                          | B           |
        # +-------------------+---------+----------------------------+-------------+

        tv, tc, hv, hc = struct.unpack('>HBHB', bytearray(data))

        raw_temp = Measured_value(tv, tc)
        raw_humidity = Measured_value(hv, hc)
        return cls(raw_temp, raw_humidity)

    @property
    def ctemp(self):
        ''' Temperatur in Grad Celsius '''
        return -45 + (175 * self._temperature.value / 0xffff)

    @property
    def ftemp(self):
        ''' Tempeeatur in  Fahrenheit'''
        return -49 + (315 * self._temperature.value / 0xffff)

    @property
    def humidity(self):
        ''' die Relative Luftfeutigkeit '''
        return 100 * self._humidity.value / 0xffff


def read_sht31(bus):
    #
    # SHT31 Adresse definieren, 0x44.
    #
    bus.write_i2c_block_data(0x44, 0x2C, [0x06])
    time.sleep(0.5)
    #
    # Daten lesen aus 0x00, 6 Bytes.
    data = bus.read_i2c_block_data(0x44, 0x00, 6)
    return Sht31Data.from_raw(data)


def on_connect(_client, _userdata, _flags, return_code):
    print("Connected with result code", return_code)


def main():
    base_topic = 'data'  # ich finde data zu generisch, wie wäre es mit sht31?
    bus = smbus.SMBus(1)

    client = mqtt.Client()
    client.on_connect = on_connect
    client.connect("localhost")
    client.loop_start()

    while True:
        try:
            values = read_sht31(bus)
        except ValueError as err:
            print(err, file=sys.stderr)
        else:
            print(f"Temperatur in Celsius: {values.ctemp:.2f} C")
            print(f"Temperatur in Fahrenheit: {values.ftemp:.2f} F")
            print(f"Relative Feuchtigkeit: {values.humidity:.2f} %RH")

            client.publish(f"{base_topic}/temp", f"{values.ctemp: .2f}")
            client.publish(f"{base_topic}/humidity_rel",
                           f"{values.humidity:.2f}")
        time.sleep(1 * 60)


if __name__ == "__main__":
    main()
Antworten