MQTT Nachrichten in Postgresql-Datenbank speichern

  • Hallo an alle,

    auf meinem Pi läuft ein Postgresql-Server. Dieser soll von meinem Client (ESP 8266) über das MQTT-Protokoll Daten nicht nur empfangen sondern auch wegschreiben.

    Das Empfangen funktioniert nach einigen Anlaufproblemen Tadellos. Der Client verfügt über eine DHT22 und sendet somit die Werte Luftfeuchtigkeit und Temperatur.

    Nun zu meinem Problem.

    Das lesen der Daten über ein Pythonskript sieht bei mir so aus:


    Daten vom Client sehen so aus:

    Received message 'b' 25.40'' on topic '/esp8266/temperature'

    Received message 'b' 47.30'' on topic '/esp8266/humidity'


    Doch wie bekommt man das nun in eine DB? ich finde zwar viele Anleitungen aber irgendwie sind die alle veraltet da Paho nun auf Version 2 unterwegs ist macht es das für mich auch nicht leichter.

    Hat von euch vielleicht irgendjemand eine Anleitung die er empfehlen kann?

    Ich wäre sehr dankbar.

  • MQTT Nachrichten in Postgresql-Datenbank speichern? Schau mal ob du hier fündig wirst!

  • tutter Das Du da selbst Anführungszeichen um Werte bastelst, macht das ganze ein bisschen unübersichtlicher als es sein muss, insbesondere da die Zeichenkettendarstellung von bytes-Objekten selbst schon Anführungszeichen enthält. Zudem ist das zusammenbasteln mit str() und + eher BASIC denn Python. In Python gibt es Zeichenkettenformatierung mit der format()-Methode und/oder f-Zeichenketten um Werte in Zeichenketten zu formatieren.

    Mit einer aktuellen Version von paho.mqtt wird das nicht funktionieren weil der Client ein nicht-optionales Argument erwartet. Du schreibst es würde die API-Version 2 verwendet, dann passt aber die on_connect()-Signatur nicht, denn die bekommt dann ein Argument mehr übergeben. Das rc steht auch nicht für „result code“ sondern für „reason code“. Abkürzungen sind halt uncool. 😎

    Der Kommentar zum loop_start() ist korrekt und das ist auch der Grund warum das nicht funktionieren kann. Das startet einen Thread und danach ist das Programm zuende, was den Thread dann auch gleich mit beendet.

    Die Ausgabe mit „InfluxDB“ ist ein bisschen irreführend wenn PostgreSQL verwendet werden soll.

    Zwischenstand (ungetestet):

    Das gesamte Programm und auch welche Rückruf-API-Version verwendet wird, hat letztlich nichts mit der Frage zu tun wie man Daten in eine PostgreSQL-Datenbank bekommt. Das kannst Du also erst einmal ganz grundsätzlich in davon unabhängigen Code testen und bei Problemen dann auch damit nachfragen, weil man das dann auch nachvollziehen kann wenn man nur eine PostgreSQL-Datenbank hat und kein MQTT.

    Man braucht eine Datenbankanbindung an PostgreSQL für Python. Da ist psycopg2 ein verbreitetes Modul, beziehungsweise der Nachfolger psycopg 3: https://www.psycopg.org/psycopg3/docs/

    2 · 3 · 3 · 37 : The prime factorization of The Beast

  • Hi __blackjack__ danke schon mal für deine Hilfe.


    Ich habe mal etwas mit Postgresql und Python rumgespielt.

    Aber wie bekomme ich denn nun {message.payload!r} und {message.topic!r} in das INSERT Statement rein?


    Edited once, last by tutter (January 13, 2025 at 1:56 PM).

  • Du hast Temperatur und Luftfeuchte, hierfür würde ich zwei Spalten verwenden.

    Bei

    Code
    insert_script = 'INSERT INTO iot_event (Client, Message_Typ, Messagevalue) VALUES(%s,%s,%s)'

    bekommt du aber nur einen Wert in die Spalten "Client", "Message_Typ" und "Messagevalue", da du die Spaltennamen nicht mit Variablen besetzt hast.

    Zusätzlich würde ich an deiner Stelle das Daten er Erfassung mitführen, also eher

    Code
    insert_script = 'INSERT INTO iot_event (Datum, Temperatur, Luftfeuchte) VALUES(%s,%s,%s,%s,%s)'

    oder ähnlich die Zeile aufbauen, in den %s der values müssten dann (geht das so überhaupt so, habe bei Python wirklich keine Ahnung) die richtigen für Werte stehen.

    Dann bekommst du eine Zeile in die Datenbank, die du dann auch sauber auswerten kannst. (Ohne Datum geht das nicht wirklich. )

    Computer ..... grrrrrr

  • tutter Warum hast Du das denn da mitten in das andere Programm rein geschrieben und dann auch noch auf Modulebene? Das hat da nichts zu suchen.

    Die Werte aus der MQTT-Nachricht bekommst Du da rein in dem Du das einfügen in die Datenbank in der Funktion machst wo Du halt auch diese Daten zur Verfügung hast.

    Das mit dem Belegen von conn und cur (schlechte Namen!) mit None ist nicht schön. Um die Verbindung und den Cursor wieder zu schliessen würde man besser with und contextlib.closing() verwenden.

    Konstanten schreibt man in Python (und sehr vielen anderen Programmiersprachen) per Konvention KOMPLETT_GROSS. Und Variablen haben auf Modulebene nichts verloren.

    Die Ausnahmebehandlung ist schlecht. Da sollte nicht einfach nur das error-Objekt ausgegeben werden. Mindestens der Traceback sollte dabei sein, damit man eine bessere Chance hat Fehler zu finden. Dafür hat das logging-Modul etwas.

    2 · 3 · 3 · 37 : The prime factorization of The Beast

  • erstmal danke an alle!

    fred0815 das habe ich vergessen zu erwähnen, da die Tabelle setzt den Wert von alleine. Also genau so wie du auch vorgeschlagen hast.

    Rasp-Berlin meine Theorie war, wie auch in der Ausgabe zu sehen, pro Messpunkt auch eine Zeile in der Datenbank zu haben.

    __blackjack__ warum das so scheiße aussieht? ganz einfach - ich habe nicht wirklich Ahnung von dem Thema und war froh das er überhaupt etwas in die Datenbank geschrieben hat. Wenn ich dich richtig verstanden habe, ich soll eine Funktion schreiben die einen Wert übergeben bekommt (die MQTT-Messwerte) und dies soll dann dank der Funktion in die Datenbank geschrieben werden. Richtig? Kann man einfach dieses {message.topic!r} einfach irgendwie übergeben?

  • meine Theorie war, wie auch in der Ausgabe zu sehen, pro Messpunkt auch eine Zeile in der Datenbank zu haben.

    und was willst du damit machen?

    Due erfasst mit einem Abruf eines Sensors (erster Datensatz) zu einem Zeitpunkt (zweiter Datensatz) zwei werte (zwei weitere Datensätze), die beide einen Zusammenhang haben, nämlich die Zeit.

    Hier kannst du den Tabellennamen als ersten Datensatz (also den Sensornamen) verwenden, und die anderen beiden, neben der Zeit, mit dieser verknüpfen.


    so, wie du das speichern willst, kannst du mit den Ergebnissen rein gar nichts anfangen, da es Zahlen sind, die zu nichts und niemanden einen wirklichen Zusammenhang haben.

    Sind die Daten alle Minute, 5 Minuten, jede Stunde, jeden Tag, gelegentlich, wenn Sonnen- und Mondfinsternis gleichzeitig die Erdrückseite bescheinen, ....

    Computer ..... grrrrrr

  • Kann man einfach dieses {message.topic!r} einfach irgendwie übergeben?

    Ja, kannst du. Aber: dir fehlen scheinbar Python-Grundlagen und damit Verständnis von dem, was da passiert und du eigentlich tust.

    `{message.topic!r}`: die geschweiften Klammer brauchst du an der Stelle, wie von __blackjack__ gezeigt, wegen des f-Strings, also der String Formatierung. `!r` bedeutet, dass innerhalb des f-String die __repr__-Methode aufgerufen wird, um den String darzustellen `message` ist das Objekt, `topic` das Attribut, dass den Namen des Nachrichtenkanals enthält. `payload` ist das Attribut, welches die Daten enthält. Hast du schon mal das Python-Tutorial durchgearbeitet: deutsche Übersetzung oder alternative englisches Original?

    Bzgl. Datenbankdesign: du solltest dir _vorher_ darüber im klaren sein, was du wie wann wo abfragen möchtest. Klar kann man alles irgendwie in ein Feld in der DB packen - macht aber das Leben später ziemlich sicher unnötig kompliziert. Und beim Aufbau einer relationalen Datenbank (wie PotgreSQL es ist) muss man anders Denken als z.B. bei einer Excel-Tabelle oder einer Tabelle in einem Textdokument. Ich würde hier zwei Tabellen machen: eine mit den Topics und eine mit Daten. Die Datentabelle hat vier Felder: Primärschlüssel (einfach von der DB automatisch setzen lassen), Topic (als Fremdschlüssel 1:N Beziehung zur Topic-Tabelle), ein Feld für den Wert und ein Feld für den Zeitstempel, der zum Wert gehört.

    Gruß, noisefloor

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!