Bachelorarbeit Sensoren anbinden an RPi

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
ossihmz
User
Beiträge: 12
Registriert: Donnerstag 20. Dezember 2018, 06:44

Montag 24. Dezember 2018, 23:00

Guten Abend und fröhliche Weihnachten wünsche ich euch.

Ich lese schon lange mit wie auch in allen anderen Foren und Blogs die man so findet im world wide web.
Vorweg. Ich sitze an meiner Bachelorarbeit. Eine wahre Herausforderung für mich, da ich kaum programmieren konnte und dennoch eine riesen Aufgabe für mich selbst angenommen habe und als willkommene Herausforderung sehe.

Ich hoffe auf Hilfe. Selbstverständlich frage ich erst, wenn ich wirklich nicht weiter weiß. Oft kann ich es mir leider nicht erlauben, drei lange Tage an einer Kleinigkeit zu sitzen.

Aber worum genau geht es mir?
Ich darf eine Kalibrier"software" erstellen für geodätische Messgeräte. Im Kleinen vorerst versteht sich. Aber ich habe natürlich einen gewissen Anspruch an mich selbst.

Ich habe einen Raspberry Pi 3b+ sowie einen Arduino, 2 DS18B20 Sensoren und ein Inklinometer, sprich Neigungsmesser.
Ich habe derweil meinen RPi eingerichtet, die Temperatursensoren mit Adresse ausgelesen und kann eine Ausgabe in Python erzeugen nach folgendem Muster:

Code: Alles auswählen

Sensorbezeichnung und Temperaturwert:
28-0113161bd91d    24.19  °C 2018-12-24 22:42:36 DatapartsNr:  1
Sensorbezeichnung und Temperaturwert:
28-0113167ce9ad    24.25  °C 2018-12-24 22:42:36 DatapartsNr:  2
Sensorbezeichnung und Temperaturwert:
28-0113161bd91d    24.19  °C 2018-12-24 22:42:39 DatapartsNr:  3
Sensorbezeichnung und Temperaturwert:

Die Datenaufnahme wurde anhand der zuvor angebebenen Laufzeit beendet.
Die Daten befinden sich in der txt Datei 'Datenausgabe DS18B20.txt'


Die Weiterverarbeitung der Daten beginnt.
Ausgabe als Diagramm abhängig von der Zeit wird aufgerufen
So weit so gut. Nun möchte ich die Daten in eine Datei schreiben, bzw besser anhängen via append.

Hier einmal mein bisheriges Erzeugnis

Code: Alles auswählen

import os, sys, time
import matplotlib as mlp
import matplotlib.pyplot as plt
import datetime

# Global für vorhandene Temperatursensoren
tempSensorBezeichnung = [] #Liste mit den einzelnen  Sensor-Kennungen
tempSensorAnzahl = 0 #INT für die Anzahl der gelesenen Sensoren
tempSensorWert = [] #Liste mit den einzelnen Sensor-Werten

# Global für Programmstatus
programmStatus = 1

def ds18b20einlesen():
    global tempSensorBezeichnung, tempSensorAnzahl, programmStatus
    #Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx
    try:
        for x in os.listdir("/sys/bus/w1/devices"):
            if (x.split("-")[0] == "28") or (x.split("-")[0] == "10"):
                tempSensorBezeichnung.append(x)
                tempSensorAnzahl = tempSensorAnzahl + 1

    except:
        # Auslesefehler
        print ("Der Verzeichnisinhalt konnte nicht ausgelesen werden.")
        programmStatus = 0

def ds18b20auslesen():
     global tempSensorBezeichnung, tempSensorAnzahl, tempSensorWert, programmStatus
     x = 0
     try:
         # 1-Wire Slave Dateien gemäß der ermittelten Anzahl auslesen
         while x < tempSensorAnzahl:
             dateiName = "/sys/bus/w1/devices/" + tempSensorBezeichnung[x] + "/w1_slave"
             file = open(dateiName)
             filecontent = file.read()
             file.close()
             # Temperaturwerte auslesen und konvertieren
             stringvalue = filecontent.split("\n")[1].split(" ")[9]
             sensorwert = float(stringvalue[2:]) / 1000
             temperatur = '%6.2f' % sensorwert #Sensor- bzw Temperaturwert auf 2 Dezimalstellen formatiert
             tempSensorWert.insert(x,temperatur) #Wert in Liste aktualisieren
             x = x + 1
     except:
         #Fehler bei Auslesung der Sensoren
         print ("Die Auslesung der DS18B20 Sensoren war nicht möglich")
         programmStatus = 0


#Programminitialisierung
ds18b20einlesen() #Anzahl und Bezeichnungen der vorhandenen Temperatursensoren einlesen
counter = 0
#Temperaturausgabe in Schleife

while programmStatus == 1 and counter < 10:
     x = 0
     ds18b20auslesen()
     while x < tempSensorAnzahl and counter < 10:
         print ("Sensorbezeichnung und Temperaturwert:")
         dateYMD = datetime.date.today().isoformat()
         dateHMS = time.strftime("%H:%M:%S")
         counter = counter + 1
         print (tempSensorBezeichnung[x] , (" ") , tempSensorWert[x] , (" °C"), dateYMD, dateHMS, "DatapartsNr: ", counter)
         x = x + 1
         tempSensorBezeichnung = tempSensorBezeichnung
         # Schreiben in Datei der Daten
         file = open("Datenausgabe DS18B20.txt", "a")
         #testList = [str(tempSensorBezeichnung[x]), (" ") ,str(tempSensorWert[x]) , (" °C") , dateYMD, dateHMS]
         file.write(str(tempSensorBezeichnung[1]) + str(tempSensorWert[1]) + str(" °C") + '   ' + str(dateYMD) + str(dateHMS) + '\n')
         file.close()
 
     time.sleep(0.5)
     #print ("\n")
Nun lese ich die Sensordaten bzw schreibe diese nur von einem der Sensoren in die Datei:

Code: Alles auswählen

28-0113167ce9ad 24.25 °C   2018-12-2422:55:43
28-0113167ce9ad 24.25 °C   2018-12-2422:55:43
28-0113167ce9ad 24.25 °C   2018-12-2422:55:45
28-0113167ce9ad 24.25 °C   2018-12-2422:55:45
28-0113167ce9ad 24.25 °C   2018-12-2422:55:47
28-0113167ce9ad 24.25 °C   2018-12-2422:55:47
28-0113167ce9ad 24.25 °C   2018-12-2422:55:50
28-0113167ce9ad 24.25 °C   2018-12-2422:55:50
28-0113167ce9ad 24.31 °C   2018-12-2422:55:52
28-0113167ce9ad 24.31 °C   2018-12-2422:55:52
Der Zeitstempel zeigt eindeutig, dass es im Ansatz richtig ist. Nur wird der zweite Sensor hier nicht geschrieben. Ich nehme an, dass dies mit deḿ Thema der Listen zutun hat. Leider habe ich nicht das Background wissen, um hier den Fehler zu finden.

Viel Geschreibe um sicher eine Kleinigkeit. Aber damit schon mal klar ist, worum es hier geht, wenn weitere Fragen auftauchen.
Das viel größere Problem wird letztlich die Übertragung der "theoretischen" Programmierung in das GUI erstellt mit QtDesigner. Da war ich schnell am Ende und muss noch viel lesen und erarbeiten.

Ich danke im Voraus für alle nützlichen Tipps. :)
Benutzeravatar
pixewakb
User
Beiträge: 1225
Registriert: Sonntag 24. April 2011, 19:43

Mittwoch 26. Dezember 2018, 00:20

Du hast im Blick, dass Du Hilfe hier im Forum in Deiner Bachelorarbeit gesondert ausweisen musst und Dein Betreuer Dich mit den Angaben hier wahrscheinlich leicht identifizieren kann? Ich halte das hier für einen SuperGAU.

Mein Eindruck ist, dass Du mit Python schon zurecht kommst, weshalb sich aus meiner Sicht ein Hinweis auf das Tutorial nicht verbietet, ein Durcharbeiten momentan aber auch nicht mehr ganz zwingend erscheint.

Vermeide das Schlüsselwort global, das ist sehr schlechter Stil, weil Du dir damit side effects einkaufst, die Du nicht gut händeln kannst. Übergib Daten an die jeweilige Funktion (!), das ist sauber und der übliche Weg. Deine Funktionen sollten auch Rückgabewerte haben.

Statt ''x = x + 1'' würde ich ''x += 1'' schreiben; du solltest Dir mal '''with open(dateiname, "r") as f: ''' ansehen und das nutzen, statt das mit open und close; ich finde die Vorsilben '''temp''' überflüssig.

Hast Du mal die Zeile '''tempSensorBezeichnung = tempSensorBezeichnung''' angesehen? Ich sehe nicht, dass Du die Variable aktualisierst, was ich aber durch die vielen global-Sachen nicht genau sagen könnte. Jedenfalls könnte es sein, dass du möglicherweise deshalb den zweiten Sensor nicht bekommst?

Falls es das nicht ist, arbeite meine obigen Hinweise ein und setze sie um und dann kann man noch mal an die Fehlersuche gehen. Falls Du etwas Zeit hast, könnte ein Blick nach PEP8 nicht schaden (kein Leerzeichen zwischen print und ( und keine überflüssigen Klammern bei Bedingungen. Kommentare in Englisch wären toll. Die Liste lässt sich wahrscheinlich noch erweitern.
Sirius3
User
Beiträge: 9823
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 26. Dezember 2018, 10:03

@ossihmz: zu dem was pixewakb schon geschrieben hat: keine nackten `except`s. Die verdecken alle möglichen Fehler, auch simple Programmierfehler, und Du wirst es mit dem Debugging schwer haben. Exceptions nur dort wo es Sinn macht und so konkret wie möglich abfangen. Z.B. macht Deine ganze „Fehlerbehandlung“ keinen Sinn, weil das Programm doch irgendwie über programmStatus beendet wird, nur dass der eigentliche Fehlerort und der konkrete Fehler durch eine nichtsagende Meldung ersetzt wird. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 4 mal 5.

Du benutzt Funktionen bisher als Sprungmarken, was sie aber nicht sind, Funktionen haben Eingabeargumente und Rückgabewerte, und erledigen genau eine klar abgetrennte Aufgabe.
`ds18b20einlesen` und `ds18b20auslesen` suggerieren, dass da irgendwas ein- und ausgeht wobei die erste Funktion einfach nur Sensornamen ermittelt.

`tempSensorAnzahl` ist unnötig, weil das einfach die Länge der Liste `tempSensorBezeichnung` ist, und überall wo Du das verwendest, solltest Du statt dessen eine for-Schleife über die Bezeichner nehmen. `x` ist bei Dir alles mögliche, Du solltest aussagekräftigere Variablennamen benutzen.

Aus `ds18b20einlesen` wird damit:

Code: Alles auswählen

def ermittle_sensor_bezeichnungen():
    """ Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx """
    bezeichnungen = []
    for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10"):
            bezeichnungen.append(sensor_name)
    return bezeichnungen
oder kurz:

Code: Alles auswählen

def ermittle_sensor_bezeichnungen():
    """ Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx """
    return [sensor_name
        for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10")
    ]
In `ds18b20auslesen` schreibst Du was von "Wert in Liste aktualisieren", statt dessen fügst Du aber immer neue Werte zur Liste hinzu. Statt etwas zu aktualisieren erzeugt man in Python einfach eine neue Liste. Zahlen formatiert man erst bei der Ausgabe, nicht schon beim Einlesen, weil man weiß dort ja noch nicht, was man alles damit mal anfangen will:

Code: Alles auswählen

def ds18b20auslesen(sensor_bezeichnungen):
    temperaturen = []
    for sensor in sensor_bezeichnungen:
        with open(os.path.join("/sys/bus/w1/devices", sensor, "w1_slave")) as data:
            temperatur = float(data.read().split('t=')[-1])
        temperaturen.append(temperatur)
    return temperaturen
Zum Hauptprogramm: Du ermittelst ziemlich verquer auf zwei verschiedene Arten die aktuelle Zeit, und das für jeden Sensor erneut, obwohl alle Daten zum selben Zeitpunkt ermittelt wurden. Das führt zu Inkonsistenzen, z.B. dass Tag und Uhrzeit 24h auseinander liegen.
Dann öffnest Du die Datei für jeden Sensor erneut, obwohl für alle Sensoren die Daten schon vorliegen. Du erhöhst `x` an der falschen Stelle und schreibst fix immer nur den zweiten Sensor in die Datei, weil wahrscheinlich irgendwann ein IndexError gekommen ist, den Du dann irgendwie beseitigt hast.
Einfach man auf alles str anwenden, damit man keine Probleme beim Schreiben hat, ist nicht die Lösung, statt dessen solltest Du Dir überlegen, was Du schreiben willst, am besten mit Stringformatierung.

Code: Alles auswählen

def main():
    sensor_bezeichnungen = ermittle_sensor_bezeichnungen()
    for _ in range(5):
        temperaturen = ds18b20auslesen(sensor_bezeichnungen)
        jetzt = datetime.datetime.now()
        with open("Datenausgabe DS18B20.txt", "a") as output:
            for sensor, temperatur in zip(sensor_bezeichnungen, temperaturen):
                line = "{} {:6.2f} °C {:%Y-%m-%d %H:%M:%S}\n".format(sensor, temperatur, jetzt)
                print(line, end="")
                output.write(line)
        time.sleep(0.5)

if __name__ == '_main__':
    main()
Benutzeravatar
snafu
User
Beiträge: 5792
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mittwoch 26. Dezember 2018, 11:44

Das Ermitteln der Sensornamen geht auch als knackiger Einzeiler, wenn man die recht sinnfrei implementierte Fehlerbehandlung weglässt:

Code: Alles auswählen

def get_sensor_names(dirname='/sys/bus/w1/devices'):
    return [name for name in os.listdir(dirname) if name[:2] in ('28', '10')]
...und dann kann man schon überlegen, ob man das überhaupt noch als eigene Funktion ausgliedern möchte oder den Einzeiler direkt hinschreibt. Mit Python lassen sich - entsprechende Kenntnisse vorausgesetzt - viele Dinge relativ kurz ausdrücken. Heißt aber natürlich nicht, dass nun alles x genannt werden soll und unbedingt in eine Zeile müsste. ;P
ossihmz
User
Beiträge: 12
Registriert: Donnerstag 20. Dezember 2018, 06:44

Mittwoch 26. Dezember 2018, 12:20

Ich danke euch für eure Mühe am Weihnachtsfeiertag.

@pixewakb
Ich halte die Hilfesuche zur Programmierung in einem Forum nicht für problematisch. Ich studiere Geodäsie und Messtechnik mit Vertiefung der Industriemesstechnik. Programmieren ist kein überwiegender Anteil des Studiums und hier nur Mittel zu Zweck. Ein Quellenbezug zum Forum wird so oder so hergestellt.

Die ganze Sparte des adressierens der Sensoren habe ich mir ehrlicherweise aus dem Netz geholt. Bisher habe ich Sensoren nur über Arduinos ausgelesen, was kein weiteres Problem war. Auf dem Raspberry bin ich neu.
tempSensorBezeichnung = tempSensorBezeichnung muss von einem Versuch übrig gelieben sein. Das ist dort fehl am Platz. Das war jedoch nicht das Problem , welches mich nur einen Sensor auslesen lässt.
Für den Style habe ich mir PEP8 mal aufgeschrieben und schon eine schöne Auflistung gefunden. Ich schaue mal rein.

Ich danke dir! :)

ps. temp als Vorsatz nehme ich nur, da ich noch andere Sensoren anbauen muss. Hier geht es nun lediglich erstmal um die Temp Sensoren. Am Ende um die Übersicht zu behalten.

@Sirius3

Das Durcheinander mit den Einrückungen viel mir auch auf. Habe ich schon beseitigt.
Inwiefern meinst du das mit den Funktionen als Sprungmarken?
Einstellige Variablen nutze ich immer nur, wenn ich was teste und bessere es anschließend aus.

Was ich bisher hatte, hat im Groben soweit ja gut funktioniert, sah vielleicht nicht gut aus, aber es hat mir schon viel zurückgegeben.

Ich habe das was du mir geschrieben hast, versucht einzubauen, muss aber gestehen, dass ich hier durcheinander komme.

Code: Alles auswählen

def ermittle_sensor_bezeichnungen():
    """ Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx """
    bezeichnungen = []
    for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10"):
            bezeichnungen.append(sensor_name)
    return bezeichnungen
Hier ermittle ich nun die angehängten Sensoren, bzw dessen Bezeichnungen, das Ergebnis dessen wird als Liste in Bezeichnungen = [] gespeichert? Durch 'return' erhalte ich die Bezeichnungen hier als Rückgabewert und kann diese nach aufgerufener Funktion auslesen.
Selbiges mit 'ds18b20auslesen'.

Mit der Zeit kam ich absolut durcheinander. Da hat nichts so richtig funktioniert, wie ich es gerne gehabt hätte.
Am Ende muss ich alle Daten, die ich aufnehme durch den Zeitstempel gegeneinander plotten können.

Für mich ist die Einordnung deiner Codes nun das Problem. Ich lösche bei mir einmal die 'def einlesen' und 'def auslesen' durch deine Codes.
Ebenfalls ersetze ich

Code: Alles auswählen

while programmStatus == 1 and counter < 10:
     x = 0
     ds18b20auslesen()
     while x < tempSensorAnzahl and counter < 10:
         print ("Sensorbezeichnung und Temperaturwert:")
         dateYMD = datetime.date.today().isoformat()
         dateHMS = time.strftime("%H:%M:%S")
         counter = counter + 1
         print (tempSensorBezeichnung[x] , (" ") , tempSensorWert[x] , (" °C"), dateYMD, dateHMS, "DatapartsNr: ", counter)
         x = x + 1
         tempSensorBezeichnung = tempSensorBezeichnung
         # Schreiben in Datei der Daten
         file = open("Datenausgabe DS18B20.txt", "a")
         #testList = [str(tempSensorBezeichnung[x]), (" ") ,str(tempSensorWert[x]) , (" °C") , dateYMD, dateHMS]
         file.write(str(tempSensorBezeichnung[1]) + str(tempSensorWert[1]) + str(" °C") + '   ' + str(dateYMD) + str(dateHMS) + '\n')
         file.close()
 
     time.sleep(0.5)

Diesen ganzen Teil durch deinen Main() Code. Soweit korrekt?

In der Main Funktion öffne ich die Datei und sollte die Sensorbezeichnung, die Temperatur und anschließend Datum und Zeit ausgeben. Als sollte zeilenweise ausgelesen und abgespeichert werden.

Leider ist dies nun nicht der Fall. Ich will ja über eine gewisse Zeit die Daten aufnehmen, die ich vorher durch User Input bestimme. Was übersehe ich?

Code jetzt:

Code: Alles auswählen

def ermittle_sensor_bezeichnungen():
    """ Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx """
    bezeichnungen = []
    for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10"):
            bezeichnungen.append(sensor_name)
    return bezeichnungen

def ds18b20auslesen(sensor_bezeichnungen):
    temperaturen = []
    for sensor in sensor_bezeichnungen:
        with open(os.path.join("/sys/bus/w1/devices", sensor, "w1_slave")) as data:
            temperatur = float(data.read().split('t=')[-1])
        temperaturen.append(temperatur)
    return temperaturen

def main():
    sensor_bezeichnungen = ermittle_sensor_bezeichnungen()
    for _ in range(5):
        temperaturen = ds18b20auslesen(sensor_bezeichnungen)
        jetzt = datetime.datetime.now()
        with open("Datenausgabe DS18B20.txt", "a") as output:
            for sensor, temperatur in zip(sensor_bezeichnungen, temperaturen):
                line = "{} {:6.2f} °C {:%Y-%m-%d %H:%M:%S}\n".format(sensor, temperatur, jetzt)
                print(line, end="")
                output.write(line)
        time.sleep(0.5)

if __name__ == '_main__':
    main()


@snafu Ich habe schon gemerkt, dass sich viele Dinge sehr knackig schreiben lassen können. Leider bin ich davon noch weit entfernt und umso knackiger mir das gezeigt wird, desto weniger erschließt sich mir der Sinn. :oops:

Lg
Benutzeravatar
DeaD_EyE
User
Beiträge: 312
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Mittwoch 26. Dezember 2018, 13:25

Passend zu der Funktion, ist ja bereits eine überarbeitete Version von ds18b20auslesen gepostet worden.
Wenn man das mit pathlib zusammen nutzt, sieht es noch etwas eleganter aus.
Die iteration über alle Sensoren würde ich außerhalb der Funktion machen.
Die split Methode hab ich durch rpartition ausgetauscht. Sieht aber nicht schöner aus.

Code: Alles auswählen

from pathlib import Path


def ds18b20_read(sensor):
    sensor = Path("/sys/bus/w1/devices") / sensor / "w1_slave"
    with sensor.open() as data:
        *_, temperatur = data.read().rpartition("=")
        temperatur = float(temperatur) / 1000
    return temperatur

Code: Alles auswählen

def ds18b20_read_all():
    all_sensors = get_sensor_names() # Funktion von snafu
    for sensor in sorted(all_sensors):
        yield sensor, ds18b20_read(sensor)
Die Funktion ist ein Generator. Ruft man diese Funktion auf, wird ein Generator zurückgeben.
Es passiert erstmal gar nichts, bis über diesen Generator iteriert wird.

In der Hauptschleife kann man dann direkt den Generator nutzen.
Ein Nebeneffekt ist, dass jedes mal beim Aufrufen nach neuen Sensoren gescannt wird.
Ob die Sensoren sortiert werden, wie der Index erstellt wird und/oder ob der Name des Sensors verwendet werden soll, bleibt dir überlassen.

Die Hauptschleife könnte dann so aussehen:

Code: Alles auswählen

def read_loop():
    for _ in range(10):
        for sensor, temp in ds18b20_read_all():
            print(sensor, temp)
        time.sleep(1)
Code ist nicht getestet. Es soll lediglich verdeutlichen, dass man mit entsprechenden Techniken den Code weiter vereinfachen kann.

PS: Ich bemerke gerade selbst, dass Deutsch und Englisch im Quellcode gemischt ist. Das ist nicht gut.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 9823
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 26. Dezember 2018, 16:38

@ossihmz: was funktioniert konkret jetzt nicht so, wie es soll?
ossihmz
User
Beiträge: 12
Registriert: Donnerstag 20. Dezember 2018, 06:44

Mittwoch 26. Dezember 2018, 16:48

Ich setze das nochmal neu auf mit euren Ratschlägen.

Erstes Problem, hinter das ich jetzt seit ner guten Weile nicht hinter steige ist dieses:

Code: Alles auswählen

import os, sys, time

def check_sensors():
    """ Verzeichnisinhalt auslesen mit allen vorhandenen Sensorbezeichnungen 28-xxxx """
    sensor_adress = []
    for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10"):
            sensor_adress.append(sensor_name)
    print(sensor_adress[0],sensor_adress[1])
    sensor1 = sensor_adress[0]
    sensor2 = sensor_adress[1]
    file = open("temp_Datenausgabe DS18B20", "w")
    file.write("DS18B20_1: " + str(sensor1) + '\n' + "DS18B20_2: " + str(sensor2))
    file.close()
    return sensor_adress


check_sensors()


def check_data_temp(sensor_adress):
    temperature = []
    for sensor in sensor_adress:
        with open(os.path.join("/sys/bus/w1/devices", sensor, "w1_slave")) as data:
            temp = float(data.read().split('t=')[-1])
        temperature.append(temp)
    print(temperature)
    return temperature

check_data_temp()
Nun bekomme ich folgende Fehlermeldung:

Code: Alles auswählen

>>> %Run untitled.py
28-0113161bd91d 28-0113167ce9ad
Traceback (most recent call last):
  File "/home/pi/Schreibtisch/Neigungsmessung an Kreiselläufer mit MEMS/untitled.py", line 30, in <module>
    check_data_temp()
TypeError: check_data_temp() missing 1 required positional argument: 'sensor_adress'
Ich komme logisch nicht zum Ziel, was er hier jetzt erwartet von mir einzutragen. Bei der zweiten Funktion meines Erachtens nichts anders als in der ersten. Und in der ersten funktioniert es wunderbar. Weitere Frage.
In der ersten Funktion printe ich probehalber Sensor1. Außerhalb der Funktion und auch nach aufrufen, wird diese Variable Sensor1 nicht gespeichert. Wie ändere ich das?

PEP8 habe ich mir schon ein wenig zu Gemüte geführt. :)
Sirius3
User
Beiträge: 9823
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 26. Dezember 2018, 19:01

@ossihmz: jede Funktion hat ihren eigenen Namensraum, Variablen die innerhalb definiert werden, existieren außerhalb nicht, daher braucht man ja auch Rückgabewerte. Und mit denen sollte man auch was machen, z.B. einer anderen Funktion als Argumente übergeben, wie die Fehlermeldung ja deutlich sagt:

Code: Alles auswählen

sensor_addresses = check_sensors()
temperatures = check_data_temp(sensor_addresses)
Listen benennt man üblicherweise nach der Pluralform des Inhaltes, also addresses und temperatures.
Benutzeravatar
sparrow
User
Beiträge: 1141
Registriert: Freitag 17. April 2009, 10:28

Mittwoch 26. Dezember 2018, 19:03

ossihmz hat geschrieben:
Mittwoch 26. Dezember 2018, 16:48
Nun bekomme ich folgende Fehlermeldung:
Deine Funktion erwartet ein Argument. Dass musst du auch übergeben.

Code: Alles auswählen

>>> def test():
	pass

>>> test()
>>> test("hallo")
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    test("hallo")
TypeError: test() takes 0 positional arguments but 1 was given
>>> def test(text):
	print(text)

	
>>> test("Hallo")
Hallo
>>> test()
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    test()
TypeError: test() missing 1 required positional argument: 'text'
>>>
ossihmz hat geschrieben:
Mittwoch 26. Dezember 2018, 16:48
In der ersten Funktion printe ich probehalber Sensor1. Außerhalb der Funktion und auch nach aufrufen, wird diese Variable Sensor1 nicht gespeichert. Wie ändere ich das?
Wenn du eine Variable in einer Funktion zuweist, dann existiert sie auch nur in der Funktion.
Grundlegend gilt: Du übergibst einer Funktion Parameter und erhälst per return eine Rückgabe.
Du könntest eine Funktion haben, die dir eine Liste aller Sensoren liefert. Die musst du dann vorhalten. Zum Auslesen kannst du dann eine Funktion haben, die genau das tut. Und als Parameter übergibst du die Liste der Sensoren. Die werden dann nicht per global oder sonstiger Magie irgendwo her gelesen.
Benutzeravatar
snafu
User
Beiträge: 5792
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 27. Dezember 2018, 13:11

ossihmz hat geschrieben:
Mittwoch 26. Dezember 2018, 12:20
@snafu Ich habe schon gemerkt, dass sich viele Dinge sehr knackig schreiben lassen können. Leider bin ich davon noch weit entfernt und umso knackiger mir das gezeigt wird, desto weniger erschließt sich mir der Sinn. :oops:
Das stimmt natürlich. Nur weil etwas kürzer geschrieben werden könnte, muss es nicht zwingend sinnvoll sein, dies immer auch zu tun. Wichtig ist ja am Ende, dass du und die Person, die deine Arbeit abnimmt, noch den Durchblick haben.

Aber so ganz darauf ausruhen sollte man sich auch nicht. Also ein (im Extremfall) komplettes Vorbeiprogrammieren an Sprachfeatures führt sicherlich nicht zu gut lesbarem und verständlichem Code. Da solltest du irgendwo den Mittelweg für dich finden. Auch wenn du kein hauptberuflicher Programmierer bist. Und dazu gehören z.B. wirkliche Basics wie das Verständnis von Funktionen (Übergabe und Rückgabe von Parametern) und das Iterieren über Datenstrukturen ohne range(len(meine_daten)).
ossihmz
User
Beiträge: 12
Registriert: Donnerstag 20. Dezember 2018, 06:44

Freitag 28. Dezember 2018, 13:52

Code: Alles auswählen

import os, sys, time
import datetime
import matplotlib as mlp
import matplotlib.pyplot as plt
import numpy as np

def check_sensors():
    """ Reading Pathcontent with all available sensor designations 28-xxxx """
    sensor_adresses = []
    for sensor_name in os.listdir("/sys/bus/w1/devices"):
        if sensor_name.split("-")[0] in ("28", "10"):
            sensor_adresses.append(sensor_name)
    #print(sensor_adresses[0],sensor_adresses[1])
    sensor1 = sensor_adresses[0]
    sensor2 = sensor_adresses[1]
    file = open("temp_Datenausgabe DS18B20", "w")
    file.write("DS18B20_1: " + str(sensor1) + '\n' + "DS18B20_2: " + str(sensor2))
    file.close()
    return sensor_adresses

def check_data_temp(sensor_adresses):
    """ Reading Data of w1_slave files for every sensor in sensor_adresses[] """
    temperatures = []
    for sensor in sensor_adresses:
        with open(os.path.join("/sys/bus/w1/devices", sensor, "w1_slave")) as data:
            temp = (float(data.read().split('t=')[-1])/1000)
        temperatures.append(temp)
    #print(temperatures)
    return temperatures

temp_data_sets = input("How much Data Sets do you want to read? ",)

def temp_read_loop():
    """ Printing data sets as often as choosen by user """
    array_temperature = ([])
    array_timestamp = ([])
    exit = 0
    while exit < int(temp_data_sets):
        sensor_adresses = check_sensors()
        temperatures = check_data_temp(sensor_adresses)
        
        """ Printing first time the sensor adresses """
        if exit == 0:
            print(sensor_adresses)
            file = open("Datenausgabe DS18B20_komplett.txt", "w")
            file.write(str(sensor_adresses) + str( '\n'))
            file.close()
                    
        print(temperatures)
        
        """ Printing list of all recorded temperatures """
        if exit == int(temp_data_sets):
            print("Finished reading " + temp_data_sets + " data sets")
            #print(all_data_sets)

        """ Printing arrays of recorded temperatures and timestamps """
        array_timestamp.append(datetime.datetime.now())
        array_temperature.append(temperatures)
        exit += 1
        time.sleep(0.5)
        
    print(array_temperature)
    print(array_timestamp)
    return(array_temperature, array_timestamp)

array_temperature, array_timestamp = temp_read_loop()

file = open("Datenausgabe DS18B20_komplett.txt", "a")
file.write(str(array_temperature) + '\n' + str(array_timestamp) + '\n')
file.close()
So sieht das Ganze mittlerweile aus. An eure Tipps habe ich mich möglichst gehalten.
Rückgabewerte habe ich nochmal selbst erzeugt in der Funktion des Einlesens der Daten. Ich denke, das habe ich verstanden. Danke für die Tipps.

Die Ausgabe des Programms sieht wie folgt aus:

Code: Alles auswählen

['28-0113161bd91d', '28-0113167ce9ad']
[[23.687, 23.812], [23.687, 23.812], [23.687, 23.812], [23.687, 23.875]]
[datetime.datetime(2018, 12, 28, 13, 45, 3, 327409), datetime.datetime(20........
Damit bin ich nicht ganz zufrieden.
Zum Einen offensichtlich das mitprinten von datetime.datetime...Aber den Fehler finde ich nicht, warum er das mit rausschreibt.
Und zum Andern habe ich versucht die Arrays zu transponieren, was leider nicht klappt. Ich hätte theoretisch gerne erst das Array der Temperatur und unten drunter oder direkt daneben das Array der Zeitstempel. Sicher könnte man diese auch zusammenfassen... Darum bemühe ich mich gleich.
Die primäre winzige Frage nun also: Warum printet er datetime mit aus?
Sirius3
User
Beiträge: 9823
Registriert: Sonntag 21. Oktober 2012, 17:20

Freitag 28. Dezember 2018, 15:37

Du solltest Dir dringend Stringformatierung, das with-Statement und for-Schleifen anschauen.
sys, numpy und matplotlib werden importiert aber nicht benutzt.
Das was Du array nennest sind keine Arrays sondern Listen, statt zwei Listen, die parallel Daten speichern solltest Du eine Liste mit Tupeln benutzen. Die Stringrepräsention von Liste ist nicht zum Weiterverarbeiten gedacht, sondern nur zu Debuggingzwecken, daher auch die Klammern und das datetime. Ausgeben mußt Du die Daten per for-Schleife und Stringformatierung. Statt der while-Schleife in `temp_read_loop` nimm eine for-Schleife, `exit` ist ein komischer Name für einen Laufindex. Das was bei `if exit == 0` steht, gehört vor die Schleife, das was in `if exit == int(temp_data_sets)` steht, hinter die Schleife.
Benutzeravatar
DeaD_EyE
User
Beiträge: 312
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Samstag 29. Dezember 2018, 14:41

Zum Einen offensichtlich das mitprinten von datetime.datetime.
Aber den Fehler finde ich nicht, warum er das mit rausschreibt.
Das ist die Repräsentation des datetime Objekts.

Entweder du wandelst diesen mit der Methode strftime in das gewünschte Format.
Alternativ, was du eh noch machen solltest, siehst du dir die String-Formatierung an.
Ich poste hierzu immer gerne diese Seite: https://pyformat.info/
Da ist sogar ein passendes Beispiel für datetime.
Und zum Andern habe ich versucht die Arrays zu transponieren, was leider nicht klappt. Ich hätte theoretisch gerne erst das Array der Temperatur und unten drunter oder direkt daneben das Array der Zeitstempel.
Mit numpy geht es sehr einfach, mit Boardmitteln sieht das etwas komisch aus.

Code: Alles auswählen

matrix = [(1,2,3), (4,5,6), (7,8,9)]
transposed = list(zip(*matrix))
Ich würde übrigens folgende Struktur für die Ausgabe verwenden:

Code: Alles auswählen

DatumZeit, Sensor, Temperatur
2018-12-28T13:45:03, 28-0113161bd91d, 23.687
2018-12-28T13:45:03, 28-0113167ce9ad, 23.812
Das hat den Vorteil, dass auch im Nachhinein mehr Sensoren vorhanden sein dürfen, ohne das Format zu zerstören.
Würde man z.B. die Namen der Sensoren als Spalten eintragen, sähe das so aus:

Code: Alles auswählen

DatumZeit, 28-0113161bd91d, 28-0113167ce9ad
2018-12-28T13:45:03, 23.687, 23.812
Wenn sich die Anzahl der Sensoren ändert, ändert sich auch die Anzahl der Spalten.

Konstruiere in deinem Code die Datensätze zeilenweise. Dann brauchst du nur eine Sequenz (Tupel/Liste).

Code: Alles auswählen

results = [] # ergebnisse, liste, ist veränderbar, (mutable)
for _ in range(temp_data_sets):
    sensor_adresses = check_sensors()
    temperatures = check_data_temp(sensor_adresses)
    timestamp = datetime.datetime.now().isoformat() # wandelt in einen string um, entsprich iso8601 (den einzigen Standard, den ich mir mal gemerkt habe)
    for sensor, temp in zip(sensor_adresses, temperatures): # https://docs.python.org/3/library/functions.html#zip
        row = (timestamp, sensor, temp) # Tupel, nicht veränderbar, (imutable)
        results.append(row)
Wie du jetzt deinen Zeitstempel erstellst, ist Geschmackssache.
Anstatt einer While-Schleife, in der du manuell prüfst, ist eine for-schleife mit range() Funktion besser geeignet.
In der verschachtelten Schleife, wird über die Sensoren und Temperaturen iteriert.
Die Zip-Funktion nimmt immer ein Element aus allen Argumenten und dann das nächste usw. Die Argumente müssen natürlich iterierbar sein.
Man bekommt also immer ein paar heraus: sensor, temp oder halt mehr, wenn mehr iterierbare Objekte an zip() übergeben werden.

Nach dem Durchlauf der Schleife, sollten alle Ergebnisse in results sein, wenn nirgendwo ein Fehler ist.

Diese könnte man dann einfach mit dem csv.writer in eine Textdatei speichern. Die Header würde ich mit einfügen.

Für den Rest bin ich jetzt zu faul, das sauber aufzuschreiben.

Code: Alles auswählen

In [41]: with open('test_f.csv', 'w') as fd: 
    ...:     writer = csv.writer(fd) 
    ...:     writer.writerow(['Zeitstempel', 'Sensor', 'Temperatur in °C']) 
    ...:     writer.writerows(matrix) 
    ...:                                                                                                                                           

In [42]: !cat test_f.csv                                                                                                                           
Zeitstempel,Sensor,Temperatur in °C
1,2,3
4,5,6
7,8,9
Nicht wundern, das ist IPython
Damit kann man auch einfach shell-Befehle in der REPL ausführen.
Wer noch etwas cooleres will, sollte sich mal xonsh ansehen.

Das CSV-Modul: https://docs.python.org/3/library/csv.html
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
ossihmz
User
Beiträge: 12
Registriert: Donnerstag 20. Dezember 2018, 06:44

Donnerstag 10. Januar 2019, 16:07

Ich danke euch.

Ich habe das Programm jetzt so weit, dass es die Daten aufnimmt und abspeichert, wie ich es brauche.

Nun habe ich einen Dauertest gemacht über 6000 Datensätze bzw etwa 3,5h.

Als Ergebnis habe ich nun in der .txt etwas nach dem Schema:
1, 2, 3
4 , 5, 6
....,
11000, 11001, 11002

Also praktisch wurden die Daten verkürzt widergegeben.
Was kann falsch sein? Ich habe die Daten als Array ausgegeben.

Eine Google Suche brachte keinen Erfolg, weil ich vermutlich nicht die richtigen Suchwörter gefunden habe. Sicher bin ich nicht der erste mit dem Problem.
Antworten