Anfänger Fragen...

Code-Stücke können hier veröffentlicht werden.
Antworten
Marius82
User
Beiträge: 13
Registriert: Dienstag 28. August 2012, 15:15

Hallo, ich will mit meinem Raspberry Pi die Temperatur loggen, das klappt soweit auch. Jetzt möchte ich mir dazu die Uhrzeit und das Datum mit loggen. Leider kenne ich mich in Python noch nicht gut aus.
Wenn ich meine Variable "zeit" ausgeben will kommt immer der Fehler, das "zeit" nicht definiert ist.
Meine Vermutung ist das "zeit" nur innerhalb der der 'def read_temp()' verfügbar ist, sehe ich das richtig? Wie bekomme ich die "zeit" in meiner Ausgabe zu Verfügung?

Hier mein bisheriger Code:

Code: Alles auswählen

#! /usr/bin/python

import os
import glob
import RPi.GPIO as GPIO
import time
from time import *

GPIO.cleanup()
GPIO.setmode(GPIO.BOARD)
GPIO.setmode(GPIO.BCM)  # BCM GPIO Nummern
GPIO.setup(24,GPIO.OUT) # P4 Ausgang fuer LED

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_c = '%.2f' % temp_c # Anzahl Nachkommastellen = 2
		zeit = localtime()
		return str(temp_c)
intervall = int(raw_input("Messintervall in Sekunden? "))
datenlog = raw_input("Daten loggen? (j/n)")

if datenlog == "j":
	while True:
		print(strftime("%d.%m.%Y", zeit) + " | " + strftime("%H:%M:%S", zeit) + " | " + read_temp()) +"C"
		GPIO.output(24,True) #LED leuchtet fuer 1 sek wenn daten geschrieben werden
		sleep(1)
		GPIO.output(24,False)
		sleep(intervall-1)
		fobj_out = open("temp-daten.csv","a")
		fobj_out.write(strftime("%d.%m.%Y", zeit) + ";" + strftime("%H:%M:%S", zeit) + ";" + read_temp() + "\n")
		fobj_out.close()

if datenlog == "n":
	while True:
		print(strftime("%d.%m.%Y", zeit) + " | " + strftime("%H:%M:%S", zeit) + " | " + read_temp()) +"C"
		sleep(intervall)

Danke
Zuletzt geändert von Marius82 am Dienstag 17. Dezember 2013, 20:44, insgesamt 1-mal geändert.
Ein Mann ist Programmierer. Seine Frau schickt ihn einkaufen und sagt: "Geh bitte Brot kaufen und wenn Eier da sind, dann bring sechs mit."
Er geht los und kommt mit sechs Broten zurück.
Seine Frau wütend: "Sechs Brote, was soll dass denn?"
Er: Wieso, es waren Eier da..."
BlackJack

@Marius82: Die Vermutung ist richtig. Warum ist das überhaupt in der Funktion drin? Es wird dort offensichtlich überhaupt nicht benötigt. Hol das dort raus und schon ist das Problem beseitigt.
Marius82
User
Beiträge: 13
Registriert: Dienstag 28. August 2012, 15:15

Ich brauche ja die aktuelle Uhrzeit, wann auch die Temperatur gemessen wird, deswegen habe ich das in die selbe Funktion gepackt. Oder sehe ich da etwas falsch?
Ein Mann ist Programmierer. Seine Frau schickt ihn einkaufen und sagt: "Geh bitte Brot kaufen und wenn Eier da sind, dann bring sechs mit."
Er geht los und kommt mit sechs Broten zurück.
Seine Frau wütend: "Sechs Brote, was soll dass denn?"
Er: Wieso, es waren Eier da..."
BlackJack

@Marius82: Was denkst Du denn wieviel Zeit da vergeht zwischen einer Anweisung direkt vor dem ``return`` und der ersten Anweisung nach dem dem die Funktion zum Aufrufer zurück gekehrt ist?
Marius82
User
Beiträge: 13
Registriert: Dienstag 28. August 2012, 15:15

Mir ist schon klar das es da um einen Bruchteil einer Sekunde geht...
Aber es geht mir ja auch ums Prinzip- es muss doch möglich sein, die Variable 'zeit' auch außerhalb der Funktion verfügbar zu machen.
Von VBA denke ich da zB an 'public'
Ein Mann ist Programmierer. Seine Frau schickt ihn einkaufen und sagt: "Geh bitte Brot kaufen und wenn Eier da sind, dann bring sechs mit."
Er geht los und kommt mit sechs Broten zurück.
Seine Frau wütend: "Sechs Brote, was soll dass denn?"
Er: Wieso, es waren Eier da..."
BlackJack

@Marius82: Also wenn Du wie in VBA programmieren willst, dann lass bitte die Finger von Python. ;-)

Das was Dir da vorschwebt kann man auch in Python machen, das kann Dir aber jemand anders erklären. Werte sollten Funktionen als Argumente betreten und als Rückgabewerte verlassen und nicht einfach so nach aussen verfügbar gemacht werden. Damit pervertiert man die Idee von Funktionen als eine in sich geschlossene „black box” mit definierten Ein- und Ausgängen.

Glaubst Du die Zeit die die Ausgabe jetzt daneben liegt ist grösser oder kleiner als die Schwankungen die man sowieso schon hat weil `sleep()` auch mehr und auch weniger Zeit als angegeben verschlafen kann?
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Marius82: wenn Du »zeit« auch außerhalb von »read_temp« haben willst, mußt Du sie halt auch zurückgeben.
Was glaubst Du bewirkt das »str« im »return«? Was wird zurückgegeben wenn »equals_pos« -1 ist?
Marius82
User
Beiträge: 13
Registriert: Dienstag 28. August 2012, 15:15

Ok, war vielleicht etwas zu genau ;)
Hab jetzt den Code so geändert:

Code: Alles auswählen

#! /usr/bin/python

import os
import glob
import RPi.GPIO as GPIO
import time
from time import *

GPIO.cleanup()
GPIO.setmode(GPIO.BOARD)
GPIO.setmode(GPIO.BCM)  # BCM GPIO Nummern
GPIO.setup(24,GPIO.OUT) # P4 Ausgang fuer LED

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_c = '%.2f' % temp_c # Anzahl Nachkommastellen = 2
		global zeit
		return str(temp_c)
intervall = int(raw_input("Messintervall in Sekunden? "))
datenlog = raw_input("Daten loggen? (j/n)")

if datenlog == "j":
	while True:
		zeit = localtime()
		print(strftime("%d.%m.%Y", zeit) + " | " + strftime("%H:%M:%S", zeit) + " | " + read_temp()) +"C"
		GPIO.output(24,True) #LED leuchtet fuer 1 sek wenn daten geschrieben werden
		sleep(1)
		GPIO.output(24,False)
		sleep(intervall-1)
		fobj_out = open("temp-daten.csv","a")
		fobj_out.write(strftime("%d.%m.%Y", zeit) + ";" + strftime("%H:%M:%S", zeit) + ";" + read_temp() + "\n")
		fobj_out.close()

if datenlog == "n":
	while True:
		zeit = localtime()
		print(strftime("%d.%m.%Y", zeit) + " | " + strftime("%H:%M:%S", zeit) + " | " + read_temp()) +"C"
		sleep(intervall)
Ist VBA und Python so ein krasser Unterschied? Also abgesehen von Syntax?
Ein Mann ist Programmierer. Seine Frau schickt ihn einkaufen und sagt: "Geh bitte Brot kaufen und wenn Eier da sind, dann bring sechs mit."
Er geht los und kommt mit sechs Broten zurück.
Seine Frau wütend: "Sechs Brote, was soll dass denn?"
Er: Wieso, es waren Eier da..."
BlackJack

@Marius82: Okay, genau das ``global`` wollte ich nicht verraten weil das in sauberen Programmen nichts zu suchen hat. Bitte ganz schnell wieder entfernen.

VBA ist oft dahingehackter Müll von Leuten die nicht Programmieren können und es auch nicht wollen, weil „es funktioniert doch” das Mass der Dinge ist. Fast so schlimm wie bei PHP. Ich hoffe es gibt da noch einen Unterschied zwischen den Communities der krass ist. :-)
Marius82
User
Beiträge: 13
Registriert: Dienstag 28. August 2012, 15:15

Oh, ja- das

Code: Alles auswählen

global
muss noch weg- hatte damit rum experimentiert ;)
Ein Mann ist Programmierer. Seine Frau schickt ihn einkaufen und sagt: "Geh bitte Brot kaufen und wenn Eier da sind, dann bring sechs mit."
Er geht los und kommt mit sechs Broten zurück.
Seine Frau wütend: "Sechs Brote, was soll dass denn?"
Er: Wieso, es waren Eier da..."
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

BlackJack hat geschrieben:VBA ist oft dahingehackter Müll von Leuten die nicht Programmieren können und es auch nicht wollen, weil „es funktioniert doch” das Mass der Dinge ist.
Ich "darf" derzeit im Job seit Wochen massenhaft Excel-VBA schreiben. Alleine das Fehlen von diversen sinnvollen Datenstrukturen und das Fehlen von Operationen auf vorhandenen Datenstrukturen tut weh. Wenn man dann auch noch Code bekommt den jemand mal mit dem Makrorecorder aufgezeichnet und dann nur rudimentär angepasst hat, dann ist echt alles vorbei. VBA-Coding ist eine Aufgabe für jemanden der Vater und Mutter erschlagen hat ...
BlackJack

@Marius82: Nochmal allgemeine Maneuverkritik:

Wenn man in der „#!”-Zeile ``env`` benutzt, dann kann Python auch an anderen Stellen als ``/usr/bin/`` installiert sein. Zum Beispiel wenn auf dem Rechner ein eigenes Python in ``/usr/local/bin`` installiert wurde oder sich der Benutzer eines in sein Heimatverzeichnis gepackt hat.

Bei Sternchen-Importen verliert man schnell die Übersicht wo welcher Name eigentlich her kommt und handelt sich eventuell Namenskollisionen ein. Es wird auch `sleep()` einmal über das Modul referenziert und sonst der per Sternchen importierte Name auf Modulebene verwendet. Das sollte man vereinheitlichen.

Das Hauptprogramm auf Modulebene sollte in einer Funktion verschwinden. Dann ist man sicherer das man nicht in Versuchung gerät in Funktionen Werte zu verwenden die nicht als Argumente übergeben wurden. Und man kann mit dem ``if __name__…``-Idiom dafür sorgen dass man das Modul ohne Effekte importieren kann.

Einrücktiefe und Namenschreibweise (Konstanten) entsprechen nicht dem Style Guide for Python Code.

Pfade werden mit `os.path.join()` zusammen gesetzt.

Der Name `read_temp_raw()` ist zu spezifisch, da wird einfach eine Datei gelesen, dass es sich um eine Datei handelt in der eine Temperatur steht, spielt keine Rolle. Den Pfad sollte man hier als Argument übergeben können. Und mit der ``with``-Anweisung wird die Ressourcenverwaltung bei der Datei sicherer und kürzer.

Bei der ``while``-Schleife zum lesen der Datei bis die erste Zeile mit 'YES' endet steht die Anweisung zum lesen der Datei zweimal im Quelltext. Hier sollte man eher eine Schleife schreiben an deren Ende geprüft wird ob die Schleife verlassen werden soll. In Python ist das syntaktisch eine Endlosschleife mit einem bedingten ``break`` im Schleifenkörper.

Ob eine Zeichenkette mit einer anderen endet prüft man mit der `endswith()`-Methode.

`str.find()` ist so ein Überbleibsel aus Zeiten wo weniger Ausnahmen verwendet wurden. Hier würde ich `index()` verwenden. Dann muss man den Rückgabewert nicht auf einen speziellen Fehlerwert prüfen und der Fall das es nicht gefunden wird, endet in einer Ausnahme und nicht dem Folgefehler den Du Dir mit Deinem Code einhandelst. Insgesamt könnte man es sich mit `partition()` noch einfacher machen.
Und der Rückgabewert sollte eine Zahl und keine Zeichenkette sein, denn die Temperatur ist eine Zahl. Die zwei Nachkommastellen ist eine Sache die zur Ausgabe gehört und nicht zum Ermitteln des Wertes.

„Magische” Zahlen wie die Nummer des LED-Pins lassen sich einfacher ändern wenn man sie nicht mehrfach in den Quelltext schreibt, sondern als Konstante an einen Namen bindet.

Die Dokumentation zu `os.system()` verweist selbst schon darauf das man eigentlich das `subprocess`-Modul verwenden sollte.

Bei der Entscheidung „loggen” oder nicht ist viel Code doppelt vorhanden. Das deutet darauf hin, dass der Code falsch organisiert ist, also die Frage ob loggen oder nicht, an der falschen Stelle gestellt wird.

Im Fall vom Logging wird die Temperatur zweimal gelesen, also eventuell etwas anderes mit ``print`` ausgegeben als in der Datei gespeichert wird. In die Datei wird aber die Zeit von der ersten Messung geschrieben. Dafür dass Du Dir sorgen um eine Verfälschung durch die Laufzeit der ``return``-Anweisung gemacht hast, bist Du hier *sehr* grosszügig. ;-)

Insgesamt würde ich dann bei so etwas hier heraus kommen (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
import os
import glob
from datetime import datetime as DateTime
from subprocess import call
from time import sleep
import RPi.GPIO as GPIO

BASE_DIR = '/sys/bus/w1/devices/'
DEVICE_FOLDER = glob.glob(os.path.join(BASE_DIR, '28*'))[0]
DEVICE_FILE = os.path.join(DEVICE_FOLDER, 'w1_slave')

LOG_FILENAME = 'temp-daten.csv'

LED_PIN = 24
LED_DURATION = 1  # In Sekunden.


def read_lines(filename):
    with open(filename) as lines:
        return list(lines)


def read_temperature():
    while True:
        lines = read_lines(DEVICE_FILE)
        if lines[0].rstrip().endswith('YES'):
            break
        sleep(0.2)
    _, _, temperature_string = lines[1].partition('t=')
    return float(temperature_string) / 1000.0


def main():
    GPIO.cleanup()
    GPIO.setmode(GPIO.BOARD)
    GPIO.setmode(GPIO.BCM)  # BCM GPIO Nummern
    GPIO.setup(LED_PIN, GPIO.OUT)  # Ausgang fuer LED

    for module_name in ['w1-gpio', 'w1-therm']:
        call(['modprobe', module_name])

    interval = int(raw_input('Messintervall in Sekunden? '))
    should_log = raw_input('Daten loggen? (j/n) ') == 'j'

    while True:
        now = DateTime.now()
        values = [
            '{0:%Y-%m-%d}'.format(now),
            '{0:%H:%M:%S}'.format(now),
            '{0:.2f}C'.format(read_temperature())
        ]
        print ' | '.join(values)
        if should_log:
            # 
            # LED leuchtet fuer LED_DURATION sek wenn daten geschrieben werden.
            # 
            GPIO.output(LED_PIN, True)
            sleep(LED_DURATION)
            GPIO.output(LED_PIN, False)

            sleep(interval - LED_DURATION)
            with open(LOG_FILENAME, 'a') as log_file:
                log_file.write(';'.join(values) + '\n')
        else:
            sleep(interval)


if __name__ == '__main__':
    main()
Ene Uran
User
Beiträge: 125
Registriert: Sonntag 17. September 2006, 20:14
Wohnort: Hollywood

Entweder:
GPIO.setmode(GPIO.BOARD) # Pin Nummern
oder:
GPIO.setmode(GPIO.BCM) # BCM GPIO Nummern
Atomkraftwerkaktienbesitzer
Antworten