Zeitschaltung auf Rasberry

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

Hallo

Ich habe mir so einen kleinen Computer zugelegt und wollte mal anfangen zu programmieren.
(das wollte ich schon eine ganze weile mal lernen aber bisher hatte ich kein sinnvolles Projekt gefunden)

Das erste was ich verbessern will ist meine Heizungsteuerung aber für ein erstes Projekt ist die wohl etwas zu komplex.
Daher langsam anfangen.

Also dachte ich daran mir eine Zeitschaltuhr mit 8 Kanälen zu bauen.
Vor einer Wochen ist das Zeug angekommen und es konnte los gehen.

Meine Vorstellung:
Ein Fenster mit 8 Abschnitten. (pro Relais einer, wenn ich mich dann mal mit tuples befasst habe vielleicht nur noch einer)
Dort 3 Knöpfe (ein, aus, Automatik)
evtl. noch ein Abschnitt um die Zeiten einzustellen/anzuzeigen.

Zur Umsetzung:
1 Programm Fenster
je ein Programm pro Relais
beim Start des Fenters starten die 8 Unterprogramme
Das Drücken der Schaltflächen ein und aus killt das Automatikprogramm des jeweiligen Relais.
das Programm kann mit dem Automatikknopf wieder gestartet werden.

Kann das so funktionieren?

mein aktuelles Problem ist das ich nur eins der neun Programme zum laufen lassen kann. da mir die Endlosschleife alles blockiert.

Kann mir da jemand einen Tipp geben wie ich am besten so ein Multiprozessing starte.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das macht man nicht mit multiprocessing. Sondern einfach alles in einem Programm, und mit der after Funktion bzw Methode von tkinter, so dass du zeitabhängige und ohne blockierende Schleife mit deiner GUI und deine Schaltfunktionen arbeiten kannst.
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

Danke

werde ich gleich mal probieren.
Dachte nur es wird übersichtlicher und ist dann leichter in andere Programme einzubauen.
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

Ok das habe ich jetzt ausprobiert:
Zeile am Ende eingefügt:
folgender Befehl:

Code: Alles auswählen

root.after(50,callback2)
unter callback2 meinen funktionierenden Code von der Schaltuhr eingefügt. Dann passierte folgendes:
Nachdem die Zeit oben abgelaufen war konnte ich meine Schaltflächen nicht mehr betätigen. (hatte zwischenzeitlich das ganze auf 5000)
ohne while schleife lief das ganze nur 1x ab aber ich konnte die GUI bedingen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Einstein: zeig Deinen Code. Wo hast Du eine while-Schleife?
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

OK hier der code für die Zeitschaltung

Bitte nicht zu viel erwarten bin noch Anfänger. funktioniert aber

Code: Alles auswählen

def callback2():

    while True: 
        if aus_01 > time.strftime("%H:%M") >= ein_01:
            print("ein")
            DEF.R_01_on()
            time.sleep(t)
            print(time.strftime("%d.%m.%Y %H:%M:%S"))
        elif aus_02 > time.strftime("%H:%M") >= ein_02:
            print("ein")
            DEF.R_01_on()
            time.sleep(t)
            print(time.strftime("%d.%m.%Y %H:%M:%S"))       
        else:
            print("aus")
            DEF.R_01_off()
            time.sleep(t)
            print(time.strftime("%d.%m.%Y %H:%M:%S"))

Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Endlosschleifen und sleep darf es in GUI-Programmen nicht geben. Das muß man per after modelieren.

Code: Alles auswählen

def callback2(root):
    now = time.strftime("%H:%M")
    if aus_01 > now >= ein_01 or aus_02 > now >= ein_02:
        print("ein")
        DEF.R_01_on()
    else:
        print("aus")
        DEF.R_01_off()
    print(time.strftime("%d.%m.%Y %H:%M:%S"))
    root.after(t, callback2, root)
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

OK das Problem daran ist after wird nur einmal aufgerufen
gibt es da ein Argument das das ganze als Schleife ablaufen lässt? dann wäre es ja die Funktion die ich brauche und es läuft.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

schau doch, after muß man in callback2 selbst wieder aufrufen.
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

gut funktioniert
das after musste nur noch nach jeder if oder elif Operation aufgerufen werden.
dann läuft es

ok grundfunktion ist jetzt gegeben.
next steps:
- variable erstellen zum umschalten manuell / auto
- variable sichern das sie beim neustart erhalten bleibt
- variable in irgendeiner Form auf dem GIU anzeigen (den Hintergrund des GUI konnte ich schon per Knopfdruck ändern an die Schaltflächen bin ich noch nicht gekommen)
- bequemere Programmierung der Zeiten
- das ganze an die Weihnachtsbeleuchtung anschließen
- nächstes Jahr vielleicht noch ne Anzeige dazu

jetzt mus nur noch einen Varialble gesetzt werden das ich ganze in den manuellen Modus bekomme.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

wenn etwas in jedem if-elif-Block aufgerufen wird, kannst Du das auch einmal danach machen (s.o.).
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

Stimmt
hatte beim ersten Versuch ein Problem mit dem Einrücken.

so Variable habe ich jetzt erstellt:
jetzt habe ich das Problem das ich sie nicht ändern kann oder die Änderung beim after nicht ankommt.
die Variable wir am Anfang des Programms definiert.

hier der code dazu:
test wird ausgeführt beim drücken einer Schaltfläche

Code: Alles auswählen

r_01_auto = 2
def test():
    print ("test")
#    print (r_01_auto)
    r_01_auto = 1
    print (r_01_auto)
def callback2():
    if r_01_auto == 2:
        print ("auto aus")
        print (r_01_auto)
    elif aus_01 > time.strftime("%H:%M") >= ein_01:
        print("ein")
        GPIO.output(5, GPIO.LOW)
        print(time.strftime("%d.%m.%Y %H:%M:%S"))
    elif aus_02 > time.strftime("%H:%M") >= ein_02:
        print("ein")
        GPIO.output(5, GPIO.LOW)
        print(time.strftime("%d.%m.%Y %H:%M:%S"))
    else:
        print("aus")
        GPIO.output(5, GPIO.HIGH)
        print(time.strftime("%d.%m.%Y %H:%M:%S"))
    root.after(5000,callback2)

und das ist die Ausgabe:
auto aus
2
auto aus
2
test
1
auto aus
2

Wenn ich die Raute vor dem ersten Print wegnehme kommt ein Fehler. Wenn ich aber r_01_auto = 1 auskommentiere funktioniert es

auto aus
2
test
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/__init__.py", line 1562, in __call__
return self.func(*args)
File "/home/pi/Python_gui/GUI.py", line 21, in test
print (r_01_auto)
UnboundLocalError: local variable 'r_01_auto' referenced before assignment
auto aus
2
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Einstein: Das sind zwei verschiedene Variablen. Die auf Modulebene gehört da aber auch nicht hin. Funktionen und Methoden sollten alles was sie benötigen als Argumente übergeben bekommen und nicht ”magisch” irgendwie aus der Umgebung nehmen, und schon gar nicht irgendwelche Variablen ausserhalb der Funktion neu zuweisen. Da steigt man dann ganz schnell nicht mehr durch und hat ein fehleranfälliges Geflecht aus ”Funktionen” und globalen Variablen was man nur verstehen kann, wenn man tatsächlich das gesamte Programm auf einmal betrachtet.

Spätestens bei GUI-Programmierung kommt man um objektorientierte Programmierung (OOP) nicht herum.

Die Zeit sollte man nur einmal ermitteln. Während die Funktion abläuft vergeht ja Zeit und man möchte ja eigentlich den einen Zeitpunkt testen an dem diese Funktion ausgeführt wird. Wenn man dabei nicht aufpasst, kann man Probleme bekommen, die aber nur sehr selten auftreten. Solche Fehler sind dann schwer zu finden.

Durchnummerierte Namen deuten in der Regel darauf hin, dass man sich mehr Mühe beim finden eines passenden Namens machen möchte, oder das man eigentlich gar keine einzelnen Namen braucht sondern eine Datenstruktur. Oft ist das eine Liste.

`r` ist kein guter Name. Kann es sein, dass das `relais` heissen soll? Falls nicht, was hätte man den da erraten sollen?

`callback` ist auch sehr generisch. Besser wäre ein Name der beschreibt was diese Funktion tut.

Statt Zahlen die irgendeine Bedeutung haben, die man ihnen aber nicht ansehen kann, würde ich mal ins `enum`-Modul schauen und eine Aufzählung mit verständlichen Namen erstellen. Oder eventuell `True` und `False` verwenden, falls es nur zwei Möglichkeiten gibt. Wobei eine Aufzählung meistens lesbareren Code ergibt.

Die GPIO-Pinnummer sollte als Konstante definiert werden, damit man die einfach ändern/anpassen kann, ohne durch den gesamten Code gehen und sich jede literale 5 anschauen zu müssen was die bedeutet und ob sie von der Änderung betroffen ist oder nicht.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

OK danke

ich versuch das mal zu übersetzten was ich verstanden habe:
erster Absatz wenn ich in einem Moduls sage x=2 gilt das nur für das Modul heißt alle das was eingerückt ist wen das zu Ende ist dann ist das weg.
da müsste ich jetzt nur noch die Herausfinden wie man die Varible Global ändert.

Absatz OOP schon mal davon gelesen aber noch nicht verstanden.

Absatz 3: zeit am Anfang vom Durchlauf ermittel und dann als Verable weiter nutzen.

Hast recht ne Liste wäre besser. Listenname Relais und dann 1-8

für mich ziehe ich das Fazit: Programmieren lernen ist aufwendiger als ich dachte.
ich werde Die Zeitschaltuhr erst mal irgendwie hin-schustern das sie irgendwie funktioniert. (nicht so wie ich will aber geht)

Dann mir ein gutes Buch zu Thema Programmierung mit Python leisten. Und das erst mal durcharbeiten.
Dazu jetzt meine Frage kann mir jemand eins empfehlen? Möglichst mit blick auf Rasberry.
ps: das hab ich schon durch

und wenn mal mal nicht weiter komme hoffe ich hier noch antworten zu finden
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Einstein: Wenn Du auf Modulebene ``x = 2`` schreibst, dann ist dieses `x` in allen Funktionen und Methoden in dem Modul sichtbar, die nicht selbst ein `x` als lokalen Namen verwenden. Namen sind lokal wenn irgendwo in der Funktion/Methode ein Wert an den Namen zugewiesen wird.

Du musst nicht herausfinden wie man Variablen global ändert, denn globale Variablen verwendet man nicht.

Wie gesagt: OOP ist Voraussetzung für jedes nicht-triviale GUI-Programm. Und dann braucht man auch keine Gedanken an globale Variablen mehr verschwenden.

Ich weiss nicht ob man speziell für den Raspi programmieren lernen muss. So ganz allgemein reicht, denn letztlich ist der Raspi ja auch nur ein normales Linux-System was sich von der Programmierung her nicht wirklich von einem ”normalen” Rechner unterscheidet.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
noisefloor
User
Beiträge: 3854
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Dann mir ein gutes Buch zu Thema Programmierung mit Python leisten. Und das erst mal durcharbeiten.
Brauchst du nicht. Einfach das offizielle Python-Tutorial auf docs.python.org durcharbeiten. Da ist alles drin, was du zum Lernen brauchst. Und es ist fehlerfrei - was dummerweise auf die meisten deutschsprachigen Einsteigerbücher nicht wirklich zu trifft.

Tipp: Stell' die GUI-Programmierung einen Schritt zurück. Wie schon gesagt wurde: für eine nicht-triviale GUI musst du die Objektorientierung von Python verstanden haben, sonst gibt das nix.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Einstein: Ich habe mal in das verlinkte Buch geschaut und das ist gruselig. War auch irgendwie zu erwarten, denn der Autor hat zu allen möglichen Programmiersprachen Bücher geschrieben. Der verspricht nicht nur für Python sondern auch für Java, C++, PHP, und Arduino den leichten Weg zum Experten. Das ist üblicherweise ein Warnzeichen. Der kann vielleicht programmieren, aber hat nicht wirklich viel Ahnung von Python und wie man damit idiomatisch programmiert.

Beim schnellen durchblättern: Es wird `eval()` benutzt um Benutzereingaben in Zahlen umzuwandeln.

Wie Objekte funktionieren ist anscheinend nicht verstanden worden. Es steht dort das bei ``for item in sequence: item = item * 2`` sich die Werte in `sequence` nicht ändern weil `item` nicht die Elemente aus der Liste sind, sondern Kopien davon und das man nur die Kopie verändert. WTF‽

Wenn man die Listenelemente verändern will, soll man laut Author das hier machen:

Code: Alles auswählen

liste = [3, 6, 123, 54, 927]
print (liste)
for inhalt in enumerate(liste):
    liste[inhalt[0]] = inhalt[1] * 2
print (liste)
Gefolgt von der Anmerkung das man daran sieht das ``for``-Schleifen nicht immer sinnvoll sind und eine ``while``-Schleife hier einfacher wäre. WTF‽ Das man in Python grundsätzlich eher neue Listen erstellt, statt vorhandene zu verändern, wäre hier wohl die sinnvollere Anmerkung.

`range()` gibt angeblich eine Liste zurück. Ob der das Buch auch schon für Python 2 am Start hatte? ;-)

Bei OOP gibt es dann ziemlich am Anfang schon den Unsinn das man zwei führende Unterstriche für Kapselung verwendet. Und gleich darauf kommen dann natürlich Getter und Setter, damit man da dann wieder dran kommt. Natürlich alles nicht in der für Python üblichen Namensschreibweise sondern in camelCase.

Als Beispiel für eine Klassenvariable gibt's dann den ”Instanzzähler” damit der Autohändler weiss wieviele Autos er hat. Argh! Und runtergezählt wird mit einer `__del__()`-Methode. Natürlich ohne irgendeinen Hinweis was für Probleme man sich durch die blosse Existenz dieser Methode einhandeln kann. Doppel-Argh! ``del`` löscht angeblich Objekte. Die Vererbungsbeispiele machen keinen Sinn, zeigen also nur die Syntax. Das Buch ist IMHO für die Tonne…

Das Kapitel über Dateien ist einfach nur umständlich und unpythonisch.

Im Tk-Kapitel wird reichlich Gebrauch von `place()` mit absoluten Positionsangaben gemacht. Und es wird von `Button` ein `MyButton` abgeleitet mit einer Methode die auch einfach eine Funktion hätte sein können. Mir ist absolut schleierhaft was dieses Beispiel vermitteln soll. Und natürlich wird fröhlich auf globale Variablen zugegriffen. Da das Tk-Kapitel nach dem OOP-Kapitel kommt, hätte man das dort eigentlich richtig machen können/sollen.

Im letzten Kapitel, einem Anwendungsbeispiel wird dann noch mal schön gezeigt wie man GUI-Programme *nicht* schreiben sollte. Mal abgesehen davon das es extrem unschön ist, sind da auch so nette Fehler wie mehr als ein `Tk`-Objekt enthalten.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Einstein
User
Beiträge: 9
Registriert: Mittwoch 12. Dezember 2018, 22:37

na dann werde ich mal ich mir mal das offizielle Tutorial zu Gemüte ziehen.

Ja der unterschied von einem Raspberry und einem "normalen" Rechner mit Linux sind meinen Meinung nach die GPIO`s da Kann man ohne größere Hardware direkt etwas Steuern und Auslesen ansonsten läuft bei mir ein modifiziertes Sybian Namens Raspian drauf.

Na dann wird meine Heizung wohl noch etwas länger auf den Raspberry warten müssen wie geplant.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich waere da eh ein bisschen vorsichtig. Der PI ist toll, und ich benutze den sowohl beruflich als auch privat. Allerdings ist es nicht trivial, ein PI-basiertes System auf die Beine zu stellen, dass auf Dauer robust und zuverlaessig ist. Dazu ist die volle Komplexitaet einfach ein bisschen sehr hoch. Und zB nach einem Stromausfall (oder nur wackler..) kommt das System ggf. nicht mehr hoch. Da wuerde ich nur Steuer- und Regelungsaufgaben mit machen, die im Notfall auch nicht funktionieren muessen. Das es morgens mal kaelter als geplant ist, weil die Verbindung aus Wecker und PI nicht funktioniert hat - geschenkt.

Aber wenn dir die Rohre einfrieren, weil der PI versagt hat, ist das deutlich unangenehmer.

Fuer solche Aufgaben ist ggf. ein ESP32 besser geeignet - mit WIFI und micropython sehr schoen zu arbeiten drauf.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Einstein: Die GPIOs ändern aber nichts daran wie man unter Linux programmiert. GPIOs kann man sich auch an jeden ”normalen” Rechner zum Beispiel per USB anschliessen. Da braucht man also nicht wirklich spezielle Literatur zum programmieren lernen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten