Seite 1 von 1
Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 10:52
von johnny_b
Hallo zusammen,
ich möchte gerne kontinuierlich gemessene Temperaturwerte in eine sqlite-DB schreiben. Es gibt viele Beispiele im Netz mit möglichen Lösungswegen, leider ist es mir aber trotz etlichen Versuchen nicht gelungen auch nur eins davon umzusetzen.
Ein großes Problem für mich als Anfänger ist, dass ich keine Fehlermeldung bekomme. Wenn ich vorher etwas falsch gemacht hatte, konnte ich anhand der Fehlermeldung gezielt an der Behebung arbeiten und mich im Internet schlau machen. Das ist an dieser Stelle leider nicht der Fall.
Was funktioniert:
- Temperatursensor ist funktionsfähig und gibt im 10-s Intervall die gemessenen Werte aus
- SQlite-DB anlegen mit Tabelle und zwei Spalten
- Per Hand Testdaten in die beiden Spalten eingeben und auslesen
Was nicht funktioniert:
- Die Temperatursensorwerte permanent in die DB zu übernehmen
Funktioniert (Temperaturwerte messen):
Code: Alles auswählen
#!/usr/bin/python
# coding=utf-8
# messprogramm.py
# ----------------
import os, sys, time
def aktuelleTemperatur():
# 1-wire Slave Datei lesen
file = open('/sys/bus/w1/devices/28-011317a451b8/w1_slave')
filecontent = file.read()
file.close()
# Temperaturwerte auslesen und konvertieren
stringvalue = filecontent.split("\n")[1].split(" ")[9]
temperature = float(stringvalue[2:]) / 1000
# Temperatur ausgeben
rueckgabewert = '%6.2f' % temperature
return(rueckgabewert)
zeit = time.strftime('%H:%M:%S')
messdaten = aktuelleTemperatur()
try:
while True:
# Mit einem Timestamp versehe ich meine Messung und lasse mir diese in der Console ausgeben.
print ("Temperatur um " + time.strftime('%H:%M:%S') +" beträgt:" + messdaten + " °C")
# Nach 10 Sekunden erfolgt die nächste Messung
time.sleep(10)
except KeyboardInterrupt:
# Programm wird beendet wenn CTRL+C gedrückt wird.
print('Temperaturmessung wird beendet')
except Exception as e:
print(str(e))
sys.exit(1)
finally:
# Das Programm wird hier beendet, sodass kein Fehler in die Console geschrieben wird.
print('Programm wird beendet.')
Funktioniert (DB anlegen):
Code: Alles auswählen
import os, sys, sqlite3
connection = sqlite3.connect("tempdata.db")
cursor = connection.cursor()
# Tabelle erzeugen
sql = """
CREATE TABLE IF NOT EXISTS tempWerte(
zeit TEXT(20),
temperatur FLOAT(20));"""
cursor.execute(sql)
connection.commit()
connection.close()
Funktioniert nicht (Wenn ich das Programm laufen lasse werden die Temperaturwerte wieder angezeigt. Wenn ich dann im Anschluss die DB aber auslese, ist sie leer):
Code: Alles auswählen
import time, sys, sqlite3, Temperaturskript_22
def userTempWerte_db_anlegen():
connection = sqlite3.connect("tempdata.db")
cursor = connection.cursor()
sql = """INSERT INTO tempWerte VALUES
(%s,%s)""", (zeit,messdaten)
cursor.executemany(sql)
connection.commit()
connection.close()
Ich bin für jeden Tipp dankbar.
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 10:57
von __deets__
Das du keine Fehlermeldungen bekommst mag daran liegen, dass du recht grosszuegig try-excepts verstreust, welche die dann wegschlucken, und nur minimale Informationen liefern. Lass die einfach weg.
Und das dein letztes Programm nicht tut ist kein Wunder. Du benutzt die falsche Methode (executemany), benutz execute stattdessen. Und du brauchst dafuer ZWEI Argumente, SQL und Werte, nicht EIN Argument das aus einem Tupel von zwei Werten besteht.
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 11:09
von Sirius3
Ich sehe nirgends, wo Du `userTempWerte_db_anlegen` aufrufst.
Das Programm auf mehrere Dateien aufzuteilen, macht im Moment auch noch keinen Sinn. Da verdeckst Du, dass das Hauptprogramm eigentlich im Untermodul Temperaturskript_22 läuft.
Aktuelle Programme sollte man nicht mehr mit Python2 anfangen, sondern gleich Python3 benutzen.
Strings stückelt man nicht mit + zusammen, sondern benutzt Stringformatierung.
Dateien öffnet man mit dem with-Statement. Variablen und Funktionsnamen schreibt man klein_mit_unterstrich. return ist keine Funktion, die Klammern also überflüssig.
Benutze keine Abkürzungen, auch nicht für SQL-Tabellen. Schreibe temperatur wenn Du Temperatur meinst, und nicht temp.
Der Funktion `userTempWerte_db_anlegen` fehlen Argumente, zumindest zeit und messdaten, wobei das wohl nur eine Temperatur ist.
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 14:55
von johnny_b
Ich danke euch für die Antworten. Leider klappt es auch mit den Hinweisen nicht.
Zum Verständnis: Brauche ich denn wirklich die Methode "userTempWerte_db_anlegen"? Ich habe in einigen Beispielen für den o.g. Anwendungsfall gesehen, dass dort im Grunde das gleiche wie bei mir steht, nur ohne die Deklaration als Methode.
Konkret: Kann die Zeile
nicht einfach weggelassen werden?
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 15:01
von __deets__
"klappt nicht" ist schade, aber ohne weiteren Code, was GENAU du probiert hast, kann man da dann auch nur mit den Schulter zucken.
Die Zeile kann weggelassen werden. Dann ist das Programm halt schlechter programmiert. Und bei nahezu allem, das nicht extrem trivial ist, braucht man Funktionen sowieso. Warum ist dir das also so wichtig, die wegzulassen?
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 15:53
von __blackjack__
@johnny_b: Nitpick: Das ist weder eine Deklaration noch eine Methode. Das ist eine Definition einer Funktion.
Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Demnach ”muss” das dann auch in einer Funktion stehen.
Re: Temperatursensorwerte in sqlite
Verfasst: Dienstag 21. April 2020, 15:57
von johnny_b
Ich muss mich korrigieren: Es funktioniert nun doch- zumindest zum Teil. Unterm Strich konnte ich mich an den Fehlermeldungen entlanghangeln, die aufkamen nachdem ich die try-excepts rausgeschmissen habe.
Warum es nur zum Teil klappt: In die Datenbank wird immer nur der erste ausgegebene Zeit- und Sensorwert hinterlegt. Damit bin ich schon ein großes Stück weiter und möchte ich mich nochmals für die Hilfe bedanken. Für heute raucht aber der Kopf etwas und bzgl. des neuen Problems werde ich dann erst einmal selber nach einer Lösung suchen. Vielen Dank nochmal!
Re: Temperatursensorwerte in sqlite
Verfasst: Mittwoch 22. April 2020, 10:41
von johnny_b
Guten Morgen,
ich bin ein Stück weiter, muss aber leider doch noch einmal nachfragen:
In der Ausgabe verändert sich der Zeit- und Temperaturwert, aber es wird nur das erste Wertepaar in die DB alle 5s übernommen. Die DB füllt sich also immerhin schon einmal kontinuierlich, aber nur mit den gleichen Zeiten und Temperaturen.
Der ursprüngliche Fehler von gestern war, dass ich die DB am Ende immer geschlossen habe. Die Logik ergibt sich mir nicht ganz, da ich in der Schleife die Verbindung ja immer wieder aufbaue. Aber so werden immerhin kontinuierlich Werte in die DB geschrieben. Jetzt dachte ich, dass es möglicherweise mit der Cursor-Verbindung ein Problem geben könnte oder aber mit den Variablen, die ich in dem sql-Ausdruck platziert habe. In vielen Beispielen sind das Angaben wie z.B. %s, allerdings führen die zu Syntax-Fehlern.
Code: Alles auswählen
#!/usr/bin/python
# coding=utf-8
# messprogramm.py
#----------------
import os, sys, time, sqlite3
def aktuelleTemperatur():
# 1-wire Slave Datei lesen
file = open('/sys/bus/w1/devices/28-011317a451b8/w1_slave')
filecontent = file.read()
file.close()
# Temperaturwerte auslesen und konvertieren
stringvalue = filecontent.split("\n")[1].split(" ")[9]
temperature = float(stringvalue[2:]) / 1000
# Temperatur ausgeben
rueckgabewert = '%6.2f' % temperature
return(rueckgabewert)
zeit = time.strftime('%H:%M:%S')
messdaten = aktuelleTemperatur()
while True :
# Mit einem Timestamp versehe ich meine Messung und lasse mir diese in der Console ausgeben.
print ("Temperatur um " + time.strftime('%H:%M:%S') +" beträgt:" + messdaten + " °C")
# Verbindung zur DB herstellen
verbindung = sqlite3.connect("temperatur.db")
zeiger = verbindung.cursor()
#Tabelle erzeugen falls nicht vorhanden
sql_anweisung = """
CREATE TABLE IF NOT EXISTS temperaturwerte (
zeit TEXT(30),
temperatur FLOAT(30));"""
#Messdaten einfügen
zeiger.execute(sql_anweisung)
sql = "INSERT INTO temperaturwerte VALUES(?,?);"
zeiger.execute(sql, (zeit ,messdaten))
verbindung.commit()
zeiger.close()
# Nach 5 Sekunden erfolgt die nächste Messung
time.sleep(5)
PS: Bitte habt etwas Nachsicht, dass ich die nettgemeinten und richtigen Hinweise wie ordentliche String-Formatierung gerne mitnehme und beherzige, aber ich gerade versuche mich stückweise vorzutasten um eine Lösung zu finden und nicht potentielle neue Fehlerquellen einzubauen. Ich bin wirklich absoluter Anfänger.
Re: Temperatursensorwerte in sqlite
Verfasst: Mittwoch 22. April 2020, 10:53
von __deets__
Das kommt eben davon, wenn man globale Variablen benutzt. Benutz keine globalen Variablen. Und ich bin immer so ein bisschen verwundert ueber dieses "ich mache das nicht, weil ich Anfaenger bin"-Argument. Das du es nicht besser weisst, ist ok. Dass du trotz Hinweis es nicht besser machst sorgt dafuer, dass du dir falsche Vorgehensweisen antrainierst. Woher soll es dann kommen, dass die ploetzlich nicht mehr verwandt werden? Hans, Haenschen, nimmermehr.
Zu deinem Programm:
- pack die while-schleife in eine main-Funktion
- ruf die main-Funktion ganz am Ende auf mit einem __main__-Guard:
- in der while-Schleife musst du sowohl den Zeitstempel, als auch den Temperaturwert, immer wieder abrufen.
- die aktuelleTemperatur (schlecht benannt, sollte aktuelle_temperatur heissen) sollte nur das float zurueck geben. Da schon wieder eine Formatierung vorzunehmen ist falsch, du willst in deinem Programm immer mit korrekten Datentypen und nicht irgendwelchen schon formatierten Strings arbeiten. Formatiert wird erst unmittelbar vor der Ausgabe. Und das ist auch ein Fehler, denn die Datenbank nimmt und soll ja auch ein Float bekommen.
Re: Temperatursensorwerte in sqlite
Verfasst: Mittwoch 22. April 2020, 11:01
von Sirius3
Tabellen werden ein mal beim Initialisieren einer Datenbank angelegt, das sollte nicht jedesmal! beim Eintragen einer Zeile passieren.
`zeit` ist immer die Zeit zu der das Programm gestartet wurde. `messdaten` enthält die Temperatur zum Start des Programms.
Statt des low-level-Aufrufs time.strftime solltest Du auch das datetime-Modul verwenden.
Re: Temperatursensorwerte in sqlite
Verfasst: Mittwoch 22. April 2020, 12:15
von johnny_b
Sirius3 hat geschrieben: ↑Mittwoch 22. April 2020, 11:01
Tabellen werden ein mal beim Initialisieren einer Datenbank angelegt, das sollte nicht jedesmal! beim Eintragen einer Zeile passieren.
Habe ich das nicht durch den "IF NOT EXISTS"-Ausdruck ausgehebelt? Ansonsten würde ja nur noch übrig bleiben, die Initialisierung auszulagern. Das hatte ich ursprünglich auch so geplant, habe das aber aufgrund eines Hinweises hier im Thread zusammengefügt.
__deets__ hat geschrieben: ↑Mittwoch 22. April 2020, 10:53
Das kommt eben davon, wenn man globale Variablen benutzt. Benutz keine globalen Variablen. Und ich bin immer so ein bisschen verwundert ueber dieses "ich mache das nicht, weil ich Anfaenger bin"-Argument. Das du es nicht besser weisst, ist ok. Dass du trotz Hinweis es nicht besser machst sorgt dafuer, dass du dir falsche Vorgehensweisen antrainierst. Woher soll es dann kommen, dass die ploetzlich nicht mehr verwandt werden? Hans, Haenschen, nimmermehr.
Bitte versteh mich nicht falsch, ich bin dir wirklich dankbar für die Hinweise, da sie mich bisher maßgeblich weitergebracht haben. Und ich weiß auch, was du mir sagen möchtest und ich habe auch in dem Post, auf das du dich beziehst, geschrieben, dass ich sie mitnehme und beherzigen möchte. Python und die dazugehörigen Strukturen sind aber wirklich nicht ohne, wenn man kaum Berührungspunkte bisher damit hatte. Ich habe gerade das Tutorial von python-lernen.de durchgeklickt und habe mir nun etwas halbwegs sinnvolles suchen wollen, um überhaupt mal ein Gefühl dafür zu bekommen, wie man mit Python arbeitet. Dann kommt es irgendwann zu der Situation, dass man nicht weiterkommt und nach Beispielen im Internet sucht und die dann auf seinen Anwendungsfall anzupassen. Das werden dann sehr schnell sehr viele Baustellen. Ich versuche es besser zu machen.
__deets__ hat geschrieben: ↑Mittwoch 22. April 2020, 10:53
- in der while-Schleife musst du sowohl den Zeitstempel, als auch den Temperaturwert, immer wieder abrufen.
Meinst du damit, dass ich deswegen die main()-Funktion einfügen soll, oder soll ich den Zeitstempel und Temperaturwert in der Schleife noch an zusätzlichen Stellen platzieren? Das wäre mir nicht klar.
__deets__ hat geschrieben: ↑Mittwoch 22. April 2020, 10:53
- die aktuelleTemperatur (schlecht benannt, sollte aktuelle_temperatur heissen) sollte nur das float zurueck geben. Da schon wieder eine Formatierung vorzunehmen ist falsch, du willst in deinem Programm immer mit korrekten Datentypen und nicht irgendwelchen schon formatierten Strings arbeiten. Formatiert wird erst unmittelbar vor der Ausgabe. Und das ist auch ein Fehler, denn die Datenbank nimmt und soll ja auch ein Float bekommen.
Du meinst dieses float
verwenden und den Teil
Code: Alles auswählen
# Temperatur ausgeben
rueckgabewert = '%6.2f' % temperature
auch in die main()-Funktion aufnehmen?
Re: Temperatursensorwerte in sqlite
Verfasst: Mittwoch 22. April 2020, 12:29
von __blackjack__
@johnny_b: `os` und `sys` werden importiert, aber nirgends verwendet.
Neben der Schreibweise von `aktuelleTemperatur`, die __deets__ ja schon angesprochen hat, ist der Name inhaltlich auch nicht gut, denn das wäre ein guter Name für die aktuelle Temperatur. Also den Wert, den die Funktion liefert. Aber kein guter Name für die Funktion selbst. Funktionen werden üblicherweise nach der Tätigkeit benannt die sie durchführen. Also hier beispielsweise `lese_temperatur()` oder `read_temperature()`.
Dateien sollte man wo es geht mit der ``with``-Anweisung öffnen. Und bei Textdateien immer eine Kodierung angeben. Wenn man den gesamten Dateiinhalt lesen will, braucht man sich mit `pathlib.Path` weder um das öffnen noch das schliessen der Datei kümmern, denn diese Objekte haben eine Methode die das alles erledigt.
`messdaten` ist falsch weil es sich nicht um Daten (Mehrzahl) handelt, sondern nur im ein Messdatum. Es wäre aber noch besser wenn man am Namen ablesen könnte *was* da gemessen wurde: die Temperatur.
Faustregel für Kommentare: Die beschreiben nicht *was* der Code macht, denn das steht da ja bereits als Code, sondern warum er das so macht. Sofern das nicht offensichtlich ist. Und offensichtlich sind auch Sachen die in der Dokumentation der Programmiersprache oder der verwendeten Bibliotheken stehen.
Die Verbinung würde man eher einmal vor der Schleife erstellen, statt immer wieder neue Verbinungen für jeden Messwert, die zudem nicht sauber geschlossen werden. `sqlite3.Connection`-Objekte sind auch Kontextmanager, können also mit der ``with``-Anweisung verwendet werden. Das Erstellen der Tabelle gehört, wie Sirius3 schrieb, auch vor die Schleife.
Der Zeitstempel ist kein TEXT(30) sondern ein TIMESTAMP und die (30) bei FLOAT(30) macht keinen Sinn.
Den Cursor `zeiger` zu nennen ist IMHO falsch. SQLs CURSOR könnte man so bezeichnen, aber die DB-API V2 `Cursor`-Objekte haben nicht wirklich etwas mit SQLs CURSOR zu tun. Und im Programm ”zeigt” das Objekt ja auch nie auf irgend etwas. Dieses Bild funktioniert ja eher nur bei SELECTs.
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python3
import sqlite3
import time
from contextlib import closing
from datetime import datetime as DateTime
from pathlib import Path
def read_temperature():
return (
float(
Path("/sys/bus/w1/devices/28-011317a451b8/w1_slave")
.read_text(encoding="ascii")
.split("\n")[1]
.split(" ")[9][2:]
)
/ 1000
)
def main():
with sqlite3.connect("temperatur.db") as verbindung:
with closing(verbindung.cursor()) as cursor:
cursor.execute(
"""CREATE TABLE IF NOT EXISTS temperaturwerte (
zeit TIMESTAMP,
temperatur FLOAT
)"""
)
while True:
zeit = DateTime.now()
temperature = read_temperature()
print(
f"Temperatur um {zeit:%H:%M:%S} beträgt {temperature:5.2.f} °C"
)
with closing(verbindung.cursor()) as cursor:
cursor.execute(
"INSERT INTO temperaturwerte VALUES(?,?)",
(zeit, temperature),
)
verbindung.commit()
time.sleep(5)
if __name__ == "__main__":
main()
Re: Temperatursensorwerte in sqlite
Verfasst: Donnerstag 23. April 2020, 12:28
von johnny_b
Vielen Dank für den Ansatz. Das hat enorm geholfen, um die vielen Hinweise nachzuvollziehen und ich weiß nun auch, was mit "schlechter programmiert" gemeint ist. Das sieht so in der Form deutlich eleganter aus, obwohl die Funktionalitäten im Großen und Ganzen ähnlich sind.
In der while-Schleife gab es an dieser Stelle einen attribute __exit__-Fehler:
den ich so lösen konnte:
Ich habe aber leider nicht nachvollziehen können, warum der cursor mit dem closing-Aufruf geladen wird. Wenn ich danach z.B. cursor.execute() ausführe, hängt doch das closing immer dadran. Ist das nicht ein Widerspruch, wenn man beispielsweise die Tabelle anlegen möchte? Oder bedeutet das, dass nach dem Befehl immer "closing" ausgeführt wird?
Re: Temperatursensorwerte in sqlite
Verfasst: Donnerstag 23. April 2020, 12:52
von __blackjack__
@johnny_b: Das darf kein `AttributeError` kommen. Das ist Bestandteil der Standardbibliothek und ist genau dafür da das es unter anderem die `__exit__()`-Methode gibt.
Der `Cursor` soll geschlossen werden, darum ``with`` und `closing()`.