Seite 1 von 1
Python - globale Variable
Verfasst: Dienstag 3. Januar 2017, 11:30
von sunshineh
Hallo,
ich bin Anfängerin in python und möchte gerne einen Eingang prüfen und mir durch eine Ausgabe mitteilen, wenn der Eingang länger als x-Sekunden auf 0 ist. Vom Grundgedanken hab ich mir das wie unten dargestellt gedacht:
Code: Alles auswählen
def cntUnterbrochen(u):
if u == 0:
cnt = cnt + 1
print("cnt aufaddieren = ", cnt)
if u == 1:
cnt = 0
print("cnt rucksetzen = ", cnt)
def loop():
while True:
time.sleep(1)
cntUnterbrochen(GPIO.input(BtnPin))
print("main")
if __name__ == '__main__': # Program start from here
setup()
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
destroy()
Allerdings bekomm ich hier die Meldung
cnt = cnt + 1
UnboundLocalError: local variable "cnt" referenced before assignment
Wie kann ich das lösen? Wäre für einen Tipp sehr dankbar!!
Re: Python - globale Variable
Verfasst: Dienstag 3. Januar 2017, 12:00
von BlackJack
@sunshineh: *Nicht* lösen sollte man das mit globalen Variablen. Entweder Du machst `cntUnterbrochen()` mehr zu einer ”echten” Funktion und übergibst den Zählstand als Argument und den veränderten Zählstand als Rückgabewert an den Aufrufer, oder Du müsstest Du mal mit objektorientierter Programmierung (OOP) auseinandersetzen.
Der Funktionsname entspricht übrigens in der Schreibweise nicht nicht dem
Style Guide for Python Code und inhaltlich verwendet man für Funktionen üblicherweise einen Namen der eine Tätigkeit beschreibt. Also die Tätigkeit die von der Funktion oder Methode ausgeführt wird.
Namen sollte man nicht kryptisch Abkürzen. Wenn man sich nicht traut so schmutzige Wörter wie „c*nt“ auszuschreiben, kann man auch was anderes, vielleicht auch passenderes verwenden, wie `count`.

Einbuchstabige Namen ohne jegliche erkennbare Bedeutung sollte man komplett vermeiden. Ausnahmen wären sehr begrenzte Gültigkeitsbereiche, beispielsweise in „list comprehensions“, Generatorausdrücken, oder anonymen Funktionen, aber sicher nicht ein `u` als Argument einer normalen Funktion. Verschiedene natürliche Sprachen bei Namen zu mischen ist auch nicht so praktisch. Üblich ist Englisch.
Inhaltlich passt das Programm nicht so ganz zur Beschreibung, denn es wird nicht geprüft ob x Sekunden wirklich ununterbrochen der Stromkreis unterbrochen ist, sondern nur einmal pro Sekunde. Dazwischen kann sich der Zustand unerkannt beliebig oft ändern.
`destroy()` ist ziemlich sicher eine Funktion, oder allgemein auf jeden Fall ein „aufrufbares Objekt“, aber im Sprachgebrauch von Python kein Kindprogramm („child program“). Ausserdem vermute ich stark, dass dieser Aufruf eigentlich in einen ``finally``-Zweig zu dem ``try`` gehört und nicht nur bei Strg+C ausgeführt werden soll, sondern *immer* bevor das Programm endet.
Re: Python - globale Variable
Verfasst: Dienstag 3. Januar 2017, 12:14
von BlackJack
Ungetestet:
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import time
from RPi import GPIO
BUTTON_PIN = 42
def setup():
GPIO.setup(BUTTON_PIN, GPIO.IN)
# ...
def destroy():
GPIO.cleanup()
# ...
def count_interrupted(is_interrupted, count):
if is_interrupted:
count += 1
print('count aufaddieren =', count)
else:
count = 0
print('count rucksetzen =', count)
return count
def loop():
interrupted_count = 0
while True:
time.sleep(1)
interrupted_count = count_interrupted(
not GPIO.input(BUTTON_PIN), interrupted_count
)
print('main')
def main():
setup()
try:
loop()
except KeyboardInterrupt:
pass
finally:
destroy()
if __name__ == '__main__':
main()
Re: Python - globale Variable
Verfasst: Sonntag 22. Januar 2017, 14:53
von sunshineh
Hi,
vielen Dank für deine Antwort! Den Code hab ich mittlerweile so angepasst, bin aber über folgendes Punkte noch nicht glücklich:
1.Das Warten, bis der nächste Alarm ausgelöst werden darf, habe ich mit "time.sleep(xxx)" realisiert. Was natürlich dumm ist, da in dieser Zeit der Prozessor ja nicht sinnvoll andere Anfgaben übernehmen kann.
2.Das Programm kommt immer wieder ins Stoppen und "deaktiviert sich selbst" mit der Meldung:
"...login raise SMTPAuthenticationError(code, resp)
SMTPAuthenticationError: (454, '4.7.0 Too many loginattempts, please try again later. ...)
3.Könnte das Programm theoretisch so Jahre lang auf dem Raspberry laufen, oder würde die Shell vollgeschrieben und an den Printmeldungen eingehen?
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import RPi.GPIO as GPIO
import smtplib
import time
import paho.mqtt.client as mqtt
BtnPin = 13
Gpin = 12
Rpin = 11
TimeOut_Alert_sec = 90
TimeOut_Disconnected_sec = 600
fromaddr = 'Raspberry Pi Alarmsystem'
toaddrs = 'xxx@googlemail.com'
username = 'xxx@googlemail.com'
password = 'xxx'
msg1_part1 = """From: Raspberry Pi Alarmsystem
To: xxxt@googlemail.com
Subject: """
msg1_part2 = """Zone17: Lichtschranke1
"""
msg2_part1 = """From: Raspberry Pi Alarmsystem
To: xxxt@googlemail.com
Subject: """
msg2_part2 = """Zone17: Lichtschranke1 (dauerhaft)
"""
def on_connect(client,userdata,flags,rc):
print("Connected with result code "+str(rc))
client = mqtt.Client()
client.on_connect = on_connect
client.connect("localhost",1883,60)
client.loop_start()
def setup():
GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location
GPIO.setup(Gpin, GPIO.OUT) # Set Green Led Pin mode to output
GPIO.setup(Rpin, GPIO.OUT) # Set Red Led Pin mode to output
GPIO.setup(BtnPin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Set BtnPin's mode is input, and pull down to low
GPIO.add_event_detect(BtnPin, GPIO.BOTH, callback=detect, bouncetime=1000)
def Led(x):
if x == 1:
GPIO.output(Rpin, 1)
GPIO.output(Gpin, 0)
if x == 0:
GPIO.output(Rpin, 0)
GPIO.output(Gpin, 1)
def SendMail_detected(cnt): # x=Type, lastMail_time=letzte Zeit wann die Mail gesendet wurde
# Sind weniger als x Sekunden seit dem letzten Aufruf dieser Funktion vergangen?
# print("Mail wobei cnt ="+str(cnt))
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
date = time.strftime("%Y%m%d")
zeit = time.strftime("%H:%M")
if cnt < 1:
WriteFile_detected()
server.sendmail(fromaddr, toaddrs, msg1_part1+" "+date+" "+zeit+" "+msg1_part2+""+date+" "+zeit+" "+msg1_part2+"("+str(TimeOut_Alert_sec)+" Seconds blocked)")
print("Lichtschranke unterbrochen > Mail gesendet")
client.publish("alarm/lichtschranke",date+" "+zeit+" Zone17: Lichtschranke1")
server.quit()
# Zeitpunkt der letzten Ausfuehrung speichern:
if cnt < 1:
return 99
else:
return 0
def WriteFile_detected():
date = time.strftime("%Y%m%d")
zeit = time.strftime("%H:%M")
fileout = open("/home/pi/Desktop/Lichtschranke.txt","a")
time.sleep(0.1)
fileout.write(date+";"+zeit+"; Die Lichtschranke wurde unterbrochen \n")
time.sleep(0.1)
fileout.close()
print("Log-File geschrieben")
def SendMail_disconnected(count): # x=Type, lastMail_time=letzte Zeit wann die Mail gesendet wurde
if count != TimeOut_Disconnected_sec/TimeOut_Alert_sec:
# print("Disconnected")
return
# Sind weniger als x Sekunden seit dem letzten Aufruf dieser Funktion vergangen?
WriteFile_disconnected()
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
date = time.strftime("%Y%m%d")
zeit = time.strftime("%H:%M")
server.sendmail(fromaddr, toaddrs, msg2_part1+" "+date+" "+zeit+" "+msg2_part2+""+date+" "+zeit+" "+msg2_part2+"("+str(TimeOut_Disconnected_sec/60)+" Minutes disconnected)")
client.publish("alarm/lichtschranke",date+" "+zeit+" Zone17: Lichtschranke1 (dauerhaft)")
#print("Lichtschranke immer noch unterbrochen > erneut Mail gesendet")
server.quit()
# Zeitpunkt der letzten Ausfuehrung speichern:
def WriteFile_disconnected():
date = time.strftime("%x")
zeit = time.strftime("%X")
fileout = open("/home/pi/Desktop/Lichtschranke.txt","a")
time.sleep(0.1)
fileout.write(date+";"+zeit+"; Die Lichtschranke wurde dauerhaft unterbrochen \n")
time.sleep(0.1)
fileout.close()
#print("Log-File disconnected geschrieben")
def detect(count):
if GPIO.input(BtnPin) == GPIO.LOW: # Lichtschranke wurde unterbrochen
back= SendMail_detected(count)
SendMail_disconnected(count)
count += 1
Led(1)
if back == 99:
time.sleep(TimeOut_Alert_sec)
#print("detect count = "+str(count))
if GPIO.input(BtnPin) == GPIO.HIGH: # Lichtschranke wieder frei
count = 0
Led(0)
#print("count ="+str(count)+" >> is back")
return count
def loop():
count = 0
while True:
time.sleep(1)
#print("vor loop "+str(count))
count = detect(count)
#print("nach loop "+str(count))
def destroy():
GPIO.output(Gpin, GPIO.HIGH) # Green led off
GPIO.output(Rpin, GPIO.HIGH) # Red led off
GPIO.cleanup() # Release resource
def main(): # Program start from here
setup()
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
pass
finally:
destroy()
if __name__ == '__main__': # Program start from here
main()
Re: Python - globale Variable
Verfasst: Sonntag 22. Januar 2017, 15:50
von Sirius3
@sunshineh: Du baust jede Sekunde eine Verbindung auf und wunderst Dich, dass das dem Server zu viel wird? Du detektierst gleichzeitig per add_event_detect als auch per loop Flanken. Was denn nun? Die Functionen SendMail_detected und SendMail_disconnected, sowie WriteFile_detected und WriteFile_disconnected sind quasi identisch. Versuche mal doppelten Code zu entfernen. Was sollen denn die sleep in WriteFile_detected bewirken?
Versuch erst einmal eine saubere Lichtschrankensteuerung hinzubekommen, bevor Du anfängst, alle möglichen Nachrichten zu verschicken. Wenn mal die Grundfunktionalität tut, kannst Du solche Gimmicks immer noch einfügen.
Re: Python - globale Variable
Verfasst: Dienstag 24. Januar 2017, 23:04
von sunshineh
Hallo, dane für die Hinweise
Code: Alles auswählen
#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import RPi.GPIO as GPIO
import smtplib
import time
import paho.mqtt.client as mqtt
BtnPin = 13
Gpin = 12
Rpin = 11
Counter = 0
TimeOut_Alert_sec = 90
TimeOut_Disconnected_sec = 600
fromaddr = 'Raspberry Pi Alarmsystem'
toaddrs = 'xxx@googlemail.com'
username = 'xxx@googlemail.com'
password = 'xxx'
msg1_part1 = """From: Raspberry Pi Alarmsystem
To: xxxt@googlemail.com
Subject: """
msg1_part2 = """Zone17: Lichtschranke1
"""
msg2_part1 = """From: Raspberry Pi Alarmsystem
To: xxx@googlemail.com
Subject: """
msg2_part2 = """Zone17: Lichtschranke1 (dauerhaft)
"""
def on_connect(client,userdata,flags,rc):
print("Connected with result code "+str(rc))
client = mqtt.Client()
client.on_connect = on_connect
client.connect("localhost",1883,60)
client.loop_start()
def setup():
GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location
GPIO.setup(Gpin, GPIO.OUT) # Set Green Led Pin mode to output
GPIO.setup(Rpin, GPIO.OUT) # Set Red Led Pin mode to output
GPIO.setup(BtnPin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Set BtnPin's mode is input, and pull down to low
GPIO.add_event_detect(BtnPin, GPIO.BOTH, callback=detect, bouncetime=1000)
def Led(x):
if x == 1:
GPIO.output(Rpin, 1)
GPIO.output(Gpin, 0)
if x == 0:
GPIO.output(Rpin, 0)
GPIO.output(Gpin, 1)
def SendMail(reason): # reason=detected / reason=disconnected
date = time.strftime("%Y%m%d")
zeit = time.strftime("%H:%M")
WriteFile(reason)
server = smtplib.SMTP('smtp.gmail.com:587')
server.starttls()
server.login(username,password)
if reason == "detected":
server.sendmail(fromaddr, toaddrs, msg1_part1+" "+date+" "+zeit+" "+msg1_part2+""+date+" "+zeit+" "+msg1_part2+"("+str(TimeOut_Alert_sec)+" Seconds blocked)")
client.publish("alarm/lichtschranke",date+" "+zeit+" Zone17: Lichtschranke1")
if reason == "disconnected":
server.sendmail(fromaddr, toaddrs, msg2_part1+" "+date+" "+zeit+" "+msg2_part2+""+date+" "+zeit+" "+msg2_part2+"("+str(TimeOut_Disconnected_sec/60)+" Minutes disconnected)")
client.publish("alarm/lichtschranke",date+" "+zeit+" Zone17: Lichtschranke1 (dauerhaft)")
server.quit()
def WriteFile(reason):
date = time.strftime("%Y%m%d")
zeit = time.strftime("%H:%M")
fileout = open("/home/pi/Desktop/Lichtschranke.txt","a")
time.sleep(0.1)
if reason == "detected":
fileout.write(date+";"+zeit+"; Die Lichtschranke wurde unterbrochen \n")
if reason == "disconnected":
fileout.write(date+";"+zeit+"; Die Lichtschranke wurde dauerhaft unterbrochen \n")
time.sleep(0.1)
fileout.close()
print("Log-File geschrieben")
def detect():
if Counter < 1:
SendMail("detected")
if Counter == TimeOut_Disconnected_sec/TimeOut_Alert_sec:
SendMail("disconnected")
def loop():
global Counter
while True:
time.sleep(1)
if GPIO.input(BtnPin) == GPIO.HIGH: # Lichtschranke wieder frei
Led(0)
Counter = 0
if GPIO.input(BtnPin) == GPIO.LOW: # Lichtschranke unterbrochen
Led(1)
Counter += 1
def destroy():
GPIO.output(Gpin, GPIO.HIGH) # Green led off
GPIO.output(Rpin, GPIO.HIGH) # Red led off
GPIO.cleanup() # Release resource
def main(): # Program start from here
setup()
try:
loop()
except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed.
pass
finally:
destroy()
if __name__ == '__main__': # Program start from here
main()
Ist es nun besser so, oder kann man noch was verbessern?
Re: Python - globale Variable
Verfasst: Dienstag 24. Januar 2017, 23:18
von BlackJack
Solange ein ``global`` im Code ist kann man immer etwas verbessern — das ``global`` loswerden.
Re: Python - globale Variable
Verfasst: Mittwoch 25. Januar 2017, 09:45
von sunshineh
Sorry, für mich ist Python ganz neu und ich versteh nicht wirklich wie ich den Parameter "Counter" sonst der Interrupt übergeben könnte.
Im Internet hab ich nur Beispiele mit "globalen Variablen" gefunden.
Kann ich eigentlich auch den Interrupt testen, ohne ständig durch die Lichtschranke rennen zu müssen?
Ich dachte eigentlich, wenn ich das Python Skritp mit F5 ausführe und ich keine Fehlermeldung erhalte, müsste das Programm fehlerfrei durchlaufen. Allerdings habe ich erst bei der wirklichen Interruptausführung folgenden Error erhalten:
"TypeError: detect() takes no arguments (1 given)"
Re: Python - globale Variable
Verfasst: Mittwoch 25. Januar 2017, 11:11
von BlackJack
Spätestens an dieser Stelle kommt man wohl um objektorientierte Programmierung nicht herum.
Wenn man dann zusätzlich noch darauf achtet leicht testbaren Code zu schreiben und tatsächlich Unittests dafür schreibt, kann man die Programmlogik auch testen ohne die Hardware auszulösen.
Wobei man zum Testen auch einen Aufbau mit Taster statt Lichtschrankte realisieren könnte damit man da nicht immer durchlaufen muss.
`client` ist auch eine globale Variable die da so nicht stehen sollte.
Beim ermitteln der Textfragmente für Datum und Uhrzeit machst Du einen Fehler: So etwas darf man nicht getrennt ermitteln, sondern immer nur beides aus *einem* Zeitpunkt. Sonst kann es passieren dass Du beispielsweise das Datum ganz knapp vor Mitternacht ermittelst und die Uhrzeit ganz knapp nach oder um Mitternacht und zusammengenommen sind die Informationen dann um fast 24 Stunden falsch.
Am besten verwendet man `datetime.now()` mit `datetime` aus dem `datetime`-Modul und verwendet die `format()`-Methode auf Zeichenketten um aus dem Objekt dann die Teilzeichenketten zu erstellen.
In dem Zusammenhang: Ebenfalls mit `format()` wird man diese Fragmente der E-Mail-Vorlagen und das wirklich sehr schwer lesbare zusammenstückeln mit ``+`` und `str()` los.
Laufzeitfehler treten erst zur Laufzeit auf. Wie sollte Python auch wissen welche Funktion oder Methode zur Laufzeit mit welchen Argumenten aufgerufen wird?