Temperaturwerte in MySQl-DB schreiben

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Antworten
dho
User
Beiträge: 3
Registriert: Montag 8. April 2013, 09:37

Hallo,

Ich habe mir vor kurzen ein Raaspberry Pi zugelegt und an diesen ein Temperatursensor (DS1820) angeshlossen. Nun möchte ich die gemessenen Temperaturwerte in meine MySQL-Datenbank schreiben. Diese liegt auf meinem Server in meinem Netzwerk. Das auslesen der Messwerte klappt, auch das herstellen der Verbindung zur Datenbank und das anlegen der Datenbank. Aber ich bekomme die Werte nicht in die Datenbank. Das Script sieht momentan so aus:

Code: Alles auswählen

import sys
import os
import time
import calendar
import MySQLdb
import time
from time import *

lt = localtime()

file = open('/sys/devices/w1_bus_master1/w1_master_slaves')

w1_slave = file.readlines()

file.close()

for line in w1_slave:

 w1_slave = line.split("\n")[0]
 file = open('/sys/bus/w1/devices/' + str(w1_slave) + '/w1_slave')
 filecontent = file.read()
 file.close()
 stringvalue = filecontent.split("\n")[1].split(" ")[9]
 temperatur_sensor = float(stringvalue[2:]) / 1000


 connection = MySQLdb.connect (host="server", user="user", passwd="psw", db="rasp_temp")

 cursor = connection.cursor()
 cursor.execute("""CREATE TABLE IF NOT EXISTS temperatur (temperatur FLOAT(5))""")
 cursor.execute("""'INSERT INTO temperatur VALUES ('%s')"""),(temperatur_sensor1)
 connection.commit()
 cursor.close();

sys.exit(0)
   
Das Problem liegt meines Erachtens in der Zeile "cursor.execute("""INSERT INTO temperatur Values ('%s')"""),(temperatur_sensor1)"
Hier wird der Wert der Variable temperatur_sensor1 nicht übergeben bzw. nicht richtig übergeben. Ich such also die richtige Syntax um den Wert dieser Variable an die DB zu übergeben.
Im Endeffekt will ich mehrere Sensoren anschließen, das Script per crontab ausführen lassen, die Werte + Zeit/Datum an die DB übertragen und diese auf einer Website darstellen.

Gruß dho
Zuletzt geändert von Anonymous am Montag 8. April 2013, 16:21, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@dho: In der Zeile ist in der Tat ein SQL-Syntax-Problem. Das sollte dementsprechend auch zu einer Ausnahme führen. Es wäre nett wenn Du in Zukunft solche Fehlermeldungen samt kompletten Traceback zeigst. Dann muss man weniger raten.

Neben dem SQL-Syntaxfehler — dem ersten ' in der Zeichenkette, ist da noch ein Problem mit den ' um den Platzhalter im SQL. Die gehören da nicht hin. Und zu guter letzt muss das zweite Argument von `execute()` eine *Sequenz* mit Werten sein, statt eines einzelnen Wertes, also zum Beispiel ein Tupel oder eine Liste. Einfach Klammern um einen Ausdruck zu setzen erzeugt kein Tupel, Kommata erzeugen Tupel. Einzige Ausnahme: das leere Tupel wird durch ``()`` erzeugt.
dho
User
Beiträge: 3
Registriert: Montag 8. April 2013, 09:37

Sry, mein Fehler und Danke für die Hilfe!
Hier nachgereicht die Fehlermeldung wenn ich das Script in der Shell ausführe:

Code: Alles auswählen

root@raspberrypi:/home/pi/Proggis# python Rasp_temp_mysql.py 
Traceback (most recent call last):
  File "Rasp_temp_mysql.py", line 31, in <module>
    cursor.execute("""'INSERT INTO temperatur VALUES ('%s')"""),(temperatur_sensor1)
  File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 174, in execute
    self.errorhandler(self, exc, value)
  File "/usr/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in defaulterrorhandler
    raise errorclass, errorvalue
_mysql_exceptions.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''INSERT INTO temperatur VALUES ('%s')' at line 1")
Desweiteren seh ich in phpMyAdmin das kein Index definiert wurde.
Ich habe jetzt die Hochkommata rausgenommen.
Zum Thema Tupel, versteh ich das richtig? Einzelne Werte kann ich nicht in die DB schreiben!? Aber den Temperaturwert + einer Zeit und/oder eines Index wäre möglich!? Oder ist das so zu verstehen, dass ich nur mehrere Werte der Variable temperatur_sensor1 in die Spalte temperatur schreiben kann!? Ich wollte ersteinmal klein anfangen und versuchen EINEN Wert in die DB zu bekommen.
Gruß dho
BlackJack

@dho: Doch Du kannst einzelne Werte in die DB schreiben aber dieser einzelne Wert muss als Sequenz mit eben diesem einzelnen Wert übergeben werden und nicht als der Wert selbst.

Ich sehe gerade das in der Zeile mit dem 'INSERT' noch mehr falsch ist. Schau Dir mal die Klammersetzung an. Und `temperatur_sensor1` wird verwendet, aber nirgends definiert.

Weitere Anmerkungen: Es werden Sachen importiert die überhaupt nicht verwendet werden. Das gilt auch für den *-Import, den man in Programmen vermeiden sollte. Wenn man sich alle Namen eines anderen Moduls in das aktuelle Modul holt, dann unterläuft man die Aufteilung auf Module. Das hat ja einen Sinn, dass es nicht einen grossen Namensraum gibt, wo sich alles drin tummelt.

Namen sollten verraten was der Wert dahinter im Kontext des Programms bedeutet. o etwas wie `lt` tut das nicht. Zumal der Namen zwar definiert, aber dann überhaupt nicht verwendet wird.

`file` ist der Name eines eingebauten Typs, den sollte man nicht an etwas anderes binden.

Dateien sollte man mit der ``with``-Anweisung öffnen/schliessen.

Einen Sequenztyp oder ein iterierbares Objekt bindet man in der Regel an einen Namen in der Mehrzahl, denn der Name repräsentiert ja eine Sammlung von Objekten. Und den gleichen Namen für den Container innerhalb einer Schleife dann noch einmal für die einzelnen Objekte zu verwenden geht gar nicht.

Eingerückt wird per Konvention mit vier Leerzeichen pro Ebene.

``line.split('\n')[0]`` ist eine ziemlich komische Art ``line.rstrip('\n')`` auszudrücken.

`w1_slave` ist bereits eine Zeichenkette, es ist also sinnfrei darauf `str()` anzuwenden. Das aufbauen einer Zeichenkette per ``+`` und `str()` ist auch nicht unbedingt „pythonisch” sondern eher BASIC. Python kennt für Zeichenkettenformatierung die `format()`-Methode auf Zeichenketten.

Ein Semikolon um Ausdrücke und Anweisungen abzuschliessen braucht man in Python nicht.

Für jeden Sensorwert eine Verbindung zur Datenbank auf- und wieder abzubauen ist ineffizient. Man sollte das ganze auf sinvollen Funktionen aufteilen, die das auslesen der Werte und das Eintragen in die Datenbank trennen. Dann kann man nicht nur eine Verbindung pro Wert vermeiden, sondern mit `executemany()` sogar mit einer Anfrage auskommen die gleich alle Daten überträgt.

Edit: Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
from itertools import imap
import MySQLdb


def read_sensor_value(name):
    with open('/sys/bus/w1/devices/{0}/w1_slave'.format(name)) as sensor_file:
        next(sensor_file)  # Skip first line.
        return float(next(sensor_file).split(' ')[9][2:]) / 1000


def iter_sensor_names():
    with open('/sys/devices/w1_bus_master1/w1_master_slaves') as lines:
        return (line.rstrip() for line in lines)


def main():
    connection = MySQLdb.connect(
        host='server', user='user', passwd='psw', db='rasp_temp'
    )
    cursor = connection.cursor()
    cursor.execute(
        'CREATE TABLE IF NOT EXISTS temperatur (temperatur FLOAT(5))'
    )
    cursor.executemany(
        'INSERT INTO temperatur VALUES (%s)',
        ((value,) for value in imap(read_sensor_value, iter_sensor_names))
    )
    connection.commit()
    cursor.close()


if __name__ == '__main__':
    main()
dho
User
Beiträge: 3
Registriert: Montag 8. April 2013, 09:37

@BlackJack:
1. Ich glaube ich muss nochmal von vorn beginnen.
2. Ich geb dir Recht, je Messung/ je Messwert eine Verbindung zur Datenbank ist alles andere als effizient. Mein Ziel war/ ist ja auch erstmal ein Script zu schreiben mit dem ich ein Wert in die Datenbank zu schreibe.
Gruss dho
Thommy09
User
Beiträge: 3
Registriert: Donnerstag 30. Mai 2013, 11:37

Hallo!
Mein Name ist Thomas und ich bin neu hier. Entsprechend rudimentär sind auch meine Programmierkenntnisse, besonders in Python.
Ich habe momentan das gleiche Problem wie dho, deshalb möchte ich mich gerne einmal hier dran hängen. Vielleicht ergeben sich ja Synergien...
Nun zu meinem Problem: Ich habe quasi ohne Python-Kenntnisse Skript-Schnippsel zusammen geklaut und auf meine Bedürfnisse angepasst. Da wäre zum einen der ersten Teil aus einem Adafruit-Tutorial: http://learn.adafruit.com/adafruits-ras ... g/software ,welcher den Sensor ausliest und der andere Teil, der mir das Ganze in meine Datenbank schreiben soll, stammt von ZetCode http://zetcode.com/db/mysqlpython/
Heraus gekommen ist folgendes:

Code: Alles auswählen

#!/usr/bin/python
# -*- coding: utf-8 -*-

import MySQLdb as mdb # eingefügt
import os
import glob
import time

os.system('modprobe w1-gpio') 
os.system('modprobe w1-therm')

base_dir = '/sys/bus/w1/devices/' 
device_folder = glob.glob(base_dir + '28*')[0]
device_file = device_folder + '/w1_slave'

def read_temp_raw():
    f = open(device_file, 'r')
    lines = f.readlines()
    f.close()
    return lines

def read_temp():
    lines = read_temp_raw()
    while lines[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines = read_temp_raw()
    equals_pos = lines[1].find('t=')
    if equals_pos != -1:
        temp_string = lines[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        #temp_f = temp_c * 9.0 / 5.0 + 32.0
        return temp_c, # temp_f
	
while True:
        con = mdb.connect('localhost', 'xxx', 'xxx', 'temp');

        with con:

            cur = con.cursor()
            cur.execute("INSERT INTO templog(temp) VALUES(read_temp())")
	# print(read_temp())
	time.sleep(60)


Klappt nur leider nicht und ich habe keinen Schimmer warum. Das Problem liegt aber wohl irgenwie an der Übergabe der Variable read_temp(). Als Fehlermeldung erscheint Folgendes:

Traceback (most recent call last):
File "thermometer2.py", line 40, in <module>
cur.execute("INSERT INTO templog(temp) VALUES(read_temp())")
File "/usr/lib/python2.7/dist-packages/MySQLdb/cursors.py", line 174, in execu te
self.errorhandler(self, exc, value)
File "/usr/lib/python2.7/dist-packages/MySQLdb/connections.py", line 36, in de faulterrorhandler
raise errorclass, errorvalue
_mysql_exceptions.OperationalError: (1305, 'FUNCTION temp.read_temp does not exi st')

Ich glaube, ich habe syntaktisch irgendwas verbockt. Ansonsten läuft das Skript nämlich. Wenn ich den "Datenbank"-Teil weg lasse, bekomme ich im Minuten-Takt Temperatur werte in der Shell angezeigt. Auch wenn ich den "Datenbank"-Teil mit dazu nehme und statt der Variable aus der Funktion read_temp() eine 'Zeichenkette' einsetze, werden diese in der Datenbank erstellt.

Vielleicht hat ja einer von Euch einen Tipp, da wäre ich sehr dankbar!

Gruß Thomas
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Thommy09 hat geschrieben:

Code: Alles auswählen

            cur.execute("INSERT INTO templog(temp) VALUES(read_temp())")

Klappt nur leider nicht und ich habe keinen Schimmer warum. Das Problem liegt aber wohl irgenwie an der Übergabe der Variable read_temp()
In der Tat. Du hast in deinem Code nämlich gar keine Variable namens read_temp. Du hast nur eine Funktion namens read_temp. Aber selbst die Funktion wird hier nicht angesprochen.

Du übergibst dem Datenbanksystem exakt den String "INSERT INTO templog(temp) VALUES(read_temp())". Dieses read_temp wird vorher nicht irgendwie auf magische Weise von Python als Funktion aufgerufen, sondern so wie es ist als Text weitergereicht. Um den Aufruf musst du dich selber kümmern und dann den von der Funktion zurückgelieferten Wert (bzw. den ersten im Tupel enthaltenen Wert) dort eintragen.
Thommy09
User
Beiträge: 3
Registriert: Donnerstag 30. Mai 2013, 11:37

Danke für die schnelle Antwort!
Ich hab scheinbar noch ein grundsätzliches Verständnisproblem. Ich bin davon ausgegangen, dass das so laufen müsste, da ja der Befehl print , der die Temperaturwerte in der Shell ausgibt, ja auch die "Variable" read_temp() verwendet.
Muss man also für MySQL die Variable "nochmal" definieren? :K
Oder ist das ein Problem der Reihenfolge? Muss der Skriptteil, der den Sensor anspricht in den Teil integriert werden, der die Datenbank anspricht?
Ich steh irgendwie ziemlich auf der Leitung :?
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Beim »execute« steht "read_temp" innerhalb eines Strings, das wäre so, wie wenn Du schreiben würdest:

Code: Alles auswählen

print("read_temp()")
also musst Du umgekehrt den Aufruf »read_temp()« außerhalb des Insert-Strings schreiben und statt dessen einen Platzhalter im String setzen (read_temp liefert ja schon ein passendes Tuple als Rückgabe):

Code: Alles auswählen

cur.execute("INSERT INTO templog(temp) VALUES(%s)", read_temp())
Thommy09
User
Beiträge: 3
Registriert: Donnerstag 30. Mai 2013, 11:37

Tausend Dank!!!
Es läuft und mir ist es gerade wie Schuppen von den Augen gefallen. Ich glaub ich hab´s gerafft!

Gruß Thomas
Antworten