Wie würdet ihr das lösen / angehen?

  • Hallo zusammen,

    ich bin gerade dabei Python zu lernen, kenne mich aber bei anderen Sprachen (Java, PHP,...) nicht so schlecht aus, ein solches "Problem" hatte ich aber bisher noch nicht zu lösen.

    Und zwar will ich mit meinem Raspberry Pi 3 (und später dem Pi Zero Wifi) 6 Magnetventile zur Gartenbewässerung steuern.

    Zum einen möchte ich einstellen können, dass z.B. täglich um 4 Uhr morgens die Ventile 1 - 3 öffnen und für 30 Minuten offen bleiben, danach sollen sie wieder schließen. Um 5 Uhr morgens sollen die Ventile 4 - 6 öffnen und 30 Minuten später wieder schließen.

    Die Ventile hängen an einem Relais und dieses über die GPIO am PI. Grundsätzlich kann ich jedes Ventil ansprechen. Das ist kein Problem.

    Für mich stellen sich folgende Fragen:
    - wie merke ich, wenn die 30 Minuten rum sind
    1. Möglichkeit: 4 Cronjobs, der erste um 4 Uhr der die Ventile 1-3 öffnet, der zweite um 4.30 Uhr, der die Ventile 1-3 schließt. Cronjob 4 und 5 machen dasselbe mit den Ventilen 4-6 um 5 bzw. 5.30 Uhr.
    2.Möglichkeit: Zwei Cronjobs, der eine ruft um 4 Uhr ein Script auf, öffnet das Ventil, danach kommt eine while-Schleife, die so lange läuft, wie die Uhrzeit < 4.30 Uhr ist. Der zweite macht dasselbe für 5 bis 5.30 Uhr.

    Zusätzlich will ich mit Python, MariaDB und NGinx eine "Webseite" in meinem Intranet aufbauen, über die ich manuell jedes Ventil an- und ausmachen kann (das soll nicht verwendet werden, um die täglichen Bewässerungen zu beenden).
    Da wäre es natürlich auch sehr schön, wenn ich auch die tägliche Bewässerung darüber konfigurieren könnte. Z.B. wenn ich sehe, dass es am nächsten Tag regnet, ich für den nächsten Tag die tägliche Bewässerung aussetze, die Uhrzeit und Intervalle (z.B. jeder zweite Tag), in denen die Bewässerung startet und deren Dauer, welche Ventile dabei geöffnet werden,...

    Würdet ihr dazu dann z.B. immer im Hintergrund bei jeder Änderung die bestehenden Cronjobs löschen und neue anlegen oder wie würdet ihr das machen?

    Was ich eher kritisch sehe ist, ein Skript mit Dauer-Schleife laufen zu haben, das "nie" endet und ständig in der DB prüft, wann bewässert werden soll und ob zufällig gerade die Uhrzeit ist,...

    Für Ideen / "best practices", wie man sowas angeht, wäre ich sehr Dankbar.

    Vielen Dank und viele Grüße,

    Dirk

  • 1. und 2. Möglichkeit - geht natürlich und wäre IMHO die beste Vorgehensweise, da ein permanent laufendes Script auch mal crashen könnte und dafür dann auch wieder ein watchdog_script in crontab eingtragen werden müsste um es wieder zu starten...
    3.Möglichkeit wäre das Script jede Minute auszuführen und im Script dann zu prüfen ob es Zeit ist irgendwas zu schalten....

    Die Schaltzeiten könnten aus einer json Datei ausgelesen werden, die du dann auch via Web bearbeiten könntest.... Oder eben aus der Datenbank abrufen. Sehe da nichts kritisches dran.

    Webseite: FAQ => Nützliche Links / Linksammlung => [Python] Webserver, Websocket und ein bisschen AJAX


    Genaue Details wenn du dich für eine konkrete Vorgehensweise entschieden hast ;)

  • Hallo Dirk,

    wenn Du nicht unbedingt darauf aus sein solltest, alles selbst zu programmieren, dann lässt sich sowas wunderbar mit pimatic umsetzen. Das läuft hier absolut zuverlässig seit mehreren Jahren. Anfangs hatte ich ebenfalls cron jobs angelegt, das war mir jedoch auf Dauer zu umständlich.

    Gruß, STF

  • Ich würde ein dauerhaft laufendes Skript basteln. Letztendlich ist der Pi ja auch nicht unbedingt voll ausgelastet bei der Aufgabenstellung, daher würde ich mir da erstmal über Performance-Probleme keine Gedanken machen.

    In dem Loop würde ich dann immer einen aktuellen Zeitstempel holen und abgleichen, ob alle Schaltungen so sind, wie sie sein sollten. Also bspw. würde ich ein Objekt pro Schalter haben, der den Soll-Schalterzustand kennt. Ganz einfach gedacht könnten das einfach wiederum Zeitpunkte sein und der Zustand (halb-offen, offen, geschlossen etc.).

    In jedem Durchlauf des Loops würde ich dann überprüfen, ob die Schalter die Soll-Einstellung haben und ggf. dann Schalten.

    Darauf aufbauend kann man dann immernoch optimieren und refaktorisieren :)

    Edit: Warum das mit den Zeitstempeln? Ganz einfach - wenn das Skript mal ausfällt und bspw. als Daemon neu gestartet wird und ein kritischer Zeitpunkt nicht getroffen wurde, dann wäre die Schaltung in einem Zeitraum fehlerhaft und das soll ja nicht sein :)

    .NET-, Unity3D-, Web-Dev.
    Mikrocomputer-Hobbyist.

    Einmal editiert, zuletzt von Renão (19. Juni 2017 um 23:01)

  • Hallo,

    vielen Dank für eure Antworten.

    Hmm, das mit den permanent laufenden Skripts wollte ich eigentlich verhindern, sehe aber auch nicht wirklich eine Möglichkeit, das anders zu lösen.
    Neben diesem Skript bräuchte ich dann ja noch eines das schaut, ob das erste Skript noch läuft und es ggf. neu startet.

    Vielen Dank und viele Grüße,

    Dirk


  • Hmm, das mit den permanent laufenden Skripts wollte ich eigentlich verhindern, sehe aber auch nicht wirklich eine Möglichkeit, das anders zu lösen.

    Hallo Dirk,

    wie ich bereits erwähnte, könntest Du sehr wohl andere Varianten wählen.

    - reine cronjobs (imho etwas frickelig, aber rel. leicht zu machen)
    - eine Hausautomation (pimatic, pilight, WasAuchImmer : ist etwas überdimensioniert, funktioniert jedoch zuverlässig, hat alles dabei und ist sehr flexibel)

    Nur, wenn Du tatsächlich selbst programmieren willst, kommst du um regelmäßige Aufrufe nicht rum, wie auch immer Du das löst (cron, schleifen etc.)

    Gruß, STF

  • Hallo Dirk,

    ich würde eine stabile Anwendung (kein Memory-Leakage, kein Stack-Overflow, ...) schreiben, die permanent läuft und durch eine Liste bzw. Konfigurationsdatei gesteuert wird. Diese Liste enthält Informationen, wann welcher GPIO auf HIGH oder LOW zu setzen ist.io

    Folgender Beispielcode in der Programmiersprache Icon zeigt dies. Kannst Du gern nach Python übertragen.


    Code-Deutung:

    Eine Struktur (Record) wird definiert, die 4 Felder enthält.
    clock ist die Uhrzeit, zu der irgendwas passieren soll. Dies kann man auf die Sekunde genau einstellen - bei Bedarf auch hinunter bis ms. Definieren könnte man noch im Bereich µs (macht aber beim Raspberry Pi keinen Sinn Kleine Zeitdefinitionen bis zu Yoktosekunden kannst Du zwar machen - ist aber sinnfrei. Wenn Schaltzeiten im Abstand kleiner als eine Minute gefordert sein sollten, dann verbietet sich cron als Alternative und dieser Code kann seine Stärke voll entfalten.
    delayed ist eine Verzögerung, die nach dem Schalten vorgenommen werden soll. Die Angabe erfolgt in ms. Kann man setzen - muss man nicht.
    pin ist der GPIO-Pin, der geschaltet werden soll.
    status ist dann der Wert, auf den der GPIO-Pin gesetzt werden soll. Hier ist jeder Ausdruck erlaubt, der letztlich 0 oder 1 liefert. Also auch die typischen Dinger wie

    Code
    temperatur - 19 < 1


    sind erlaubt, wenn man einen GPIO dann auf 1 / HIGH setzen möchte, wenn die Temperatur < 20 ist. Oder auch das hier

    Code
    temperatur - 16 < 1 | temperatur - 20 > 0


    um z.B. eine Heizung bei Unterschreiten von 17 °C einzuschalten oder bei Überschreiten von 21 °C auszuschalten. In dem nicht definierten Bereich dazwischen bleibt der Status, wie er gerade ist - egal wie.
    Mit anderen Temperaturen ist so auch eine Lüftersteuerung umgesetzt.


    meigrafd: Dir zuliebe habe ich mal einen Code geschrieben, mit dem auch in Standard-Icon Threads definiert werden. Diese nennen sich in Icon und verwandten Programmiersprachen wie Unicon, ObjectIcon, Idol, MT Icon, Goaldi Co-Expressions. Diese funktionieren technisch wie Koroutinen (die es in recht vielen Programmiersprachen gibt).

    Auf das Thema Co-Expressions möchte ich jetzt nicht ins Detail eingehen. Wer mehr dazu wissen will, möge ins Icon-Tutorial Teil 8 seinen Blick schweifen lassen. Dieser Technical Report ist auch recht aufschlussreich.

    Die Co-Expressions werden mit create definiert, mit @co-expressio aufgerufen und mit ^co-expression aufgefrischt bzw. zurückgesetzt. Mit *co-expression kann man erfahren, wieviele Werte die Co-Expression noch liefern kann - und rechtzeitig auffrischen. In dem Code-Beispiel liefert beispielsweise die Co-Expression decimal nur 256 Werte. Nach dem letzten gelieferten Wert (255) würde jeglicher Ausdruck, der die Co-Expression decimal nochmals verwendet, scheitern. Dieses Scheitern setzt sich für jeden Ausdruck fort, weshalb das Programm letztlich nicht mehr viel Sinnvolles machen würde.

    Im Programmeintrittspunkt main() wird dann eine Liste bestehend aus der oben vorgestellten Struktur definiert, die zur Demonstration nur die aktuelle Uhrzeit (hh:mm) enthält und die Sekunden 00 bis 55 im 5 Sekundenabstand angehängt sind. Auf diese Weise kann man sehen, dass alle 5 Sekunden irgendwas mit den GPIOs passiert. Da sieht man schneller, dass der Code funktioniert, als wenn man nur alle paar Stunden Ereignisse hervorruft.

    Übrigens kann man mit dieser Technik auch die GUI-Ereignisse hervorrufen (wenn man z.B. eine GUI-Anwendung fernsteuern will oder deren Verhalten auf Ereignisse automatisiert testen will).


    Das Programm definiert zwei Co-Expressions.
    @decimal liefert alle natürlichen Zahlen von 0 bis 255. Hier wird zur Definition der Co-Expression einfach nur ein Generator verwendet.
    @sleeper() bedient sich einer Funktion, die im Code definiert wird.
    (Um mal zwei gängige Möglichkeiten vorzustellen, wie man Co-Expressions definieren kann.)

    Dann befindet sich das Programm in einer Endlosschleife, aus der es nur den Ausweg gibt, wenn die oben vorgestellte Liste leer läuft.

    Dann finden zwei Vergleiche statt. Einmal, wenn die aktuelle Uhrzeit der Zeit entspricht, die in der Liste enthalten ist. Dann passiert genau das, was in der Liste bzw. in der Struktur definiert wurde. Der entsprechende GPIO-Pin wird auf 0 oder 1 gesetzt. Da @sleeper nur einen einzigen Wert zurückliefert, muss hier die Co-Expression nach dem ersten Aufruf wieder aufgefrischt werden. Sonst würde @sleeper nur einmal funktionieren. get() entfernt den gerade behandelten Eintrag aus der Liste. Die Liste schrumpft somit zusammen.

    Wenn die Zeit, die in der Liste definiert ist, noch in der Zukunft steht, dann passiert eigentlich nur eine Ausgabe über die aktuelle Uhrzeit.

    Na ja, und wenn die Zeit bereits vorbei ist, dann macht es ja keinen Sinn, die Einträge noch abzuwarten oder abzuarbeiten. Die gefundenen Einträge werden einfach nur gelöscht.

    Der Ausdruck

    Code
    writes(get(interrupts).clock)


    ist vielleicht noch erwähnenswert. get() entfernt nicht nur den ersten Eintrag der Liste (hier also die Struktur bestehend aus den vier Feldern). get() liefert auch diesen Eintrag - egal welchen Datentyp er darstellt. In diesem Fall ist es eine Struktur, die das Datenfeld clock enthält. Somit kann dieses Datenfeld auch ausgegeben werden - obwohl die Liste diesen Eintrag nicht mehr enthält.


    Ausgabe des Programms:

    Spoiler anzeigen


    12
    ---
    vorbei...10:02:00
    11
    ---
    vorbei...10:02:05
    10
    ---
    GPIO!
    Pin 17 => 0
    9
    ---
    10:02:11 0
    ***
    9
    ---
    10:02:12 1
    ***
    9
    ---
    10:02:13 2
    ***
    9
    ---
    10:02:14 3
    ***
    9
    ---
    GPIO!
    Pin 18 => 0
    8
    ---
    10:02:16 4
    ***
    8
    ---
    10:02:17 5
    ***
    8
    ---
    10:02:18 6
    ***
    8
    ---
    10:02:19 7
    ***
    8
    ---
    GPIO!
    Pin 17 => 1
    7
    ---
    10:02:21 8
    ***
    7
    ---
    10:02:22 9
    ***
    7
    ---
    10:02:23 10
    ***
    7
    ---
    10:02:24 11
    ***
    7
    ---
    GPIO!
    Pin 18 => 1
    6
    ---
    10:02:26 12
    ***
    6
    ---
    10:02:27 13
    ***
    6
    ---
    10:02:28 14
    ***
    6
    ---
    10:02:29 15
    ***
    6
    ---
    GPIO!
    Pin 17 => 0
    5
    ---
    10:02:31 16
    ***
    5
    ---
    10:02:32 17
    ***
    5
    ---
    10:02:33 18
    ***
    5
    ---
    10:02:34 19
    ***
    5
    ---
    GPIO!
    Pin 18 => 0
    4
    ---
    10:02:36 20
    ***
    4
    ---
    10:02:37 21
    ***
    4
    ---
    10:02:38 22
    ***
    4
    ---
    10:02:39 23
    ***
    4
    ---
    GPIO!
    Pin 17 => 1
    3
    ---
    10:02:41 24
    ***
    3
    ---
    10:02:42 25
    ***
    3
    ---
    10:02:43 26
    ***
    3
    ---
    10:02:44 27
    ***
    3
    ---
    GPIO!
    Pin 18 => 1
    2
    ---
    10:02:46 28
    ***
    2
    ---
    10:02:47 29
    ***
    2
    ---
    10:02:48 30
    ***
    2
    ---
    10:02:49 31
    ***
    2
    ---
    GPIO!
    Pin 17 => 0
    1
    ---
    10:02:51 32
    ***
    1
    ---
    10:02:52 33
    ***
    1
    ---
    10:02:53 34
    ***
    1
    ---
    10:02:54 35
    ***
    1
    ---
    GPIO!
    Pin 18 => 0
    0



    Natürlich kann man ereignisgesteuert die Liste ergänzen, wenn z.B. Ventilatoren, Heizungen, Rolläden, ... zusätzlich zur definierten Liste bei Unterschreiten / Überschreiten von Klimadaten, Helligkeiten gestartet, gestoppt, geöffnet, geschlossen, ... werden sollen.
    Damit es kein Durcheinander in den Zeitangaben gibt, empfiehlt es sich den Befehl sort() einzusetzen, um die Liste nach der Uhrzeit zu sortieren.

    Bei tagesüberschreitenden Einsätzen empfiehlt sich der Einsatz von Unix-Timestamps. In dem Fall muss dann der Datumszeitstempel in die Unix-Zeit umgerechnet werden.
    Alternativ kann auch jeden Tag die Liste neu erzeugt werden bzw. die noch existierende Liste um Standard-Einträge erweitert werden.
    Letztlich kann dieses kleine Programm endlos laufen, da es

    • praktisch zu keiner CPU-Auslastung beiträgt,
    • außer der Liste kein Speicher benötigt wird
    • keine Rekursion beinhaltet

    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.

    3 Mal editiert, zuletzt von Andreas (12. Januar 2018 um 21:49)

  • Sehe da kein Problem das ganze über einen einzigen Cronjob zu regeln und somit auch ein _nicht_ permanent laufendes Script.


    Ich hatte sowas ähnliches schon mal hier im Forum für eine Aquarium-Steuerung umgesetzt... Seinerzeit über PHP, mittlerweile bevorzuge ich aber Python auch für den Webserver Part ;)
    Da wurden 4 MySQL Tabellen erzeugt: Schaltzeit, Status, Typ und Vorgang.

    Schaltzeit:
    id => Ist eindeutig und AUTO_INCREMENT
    WochenTag => Beinhaltet die Nummer des Wochentags, 1 (für Montag) bis 7 (für Sonntag) - optional, muss nicht befüllt sein. Wenn etwas zum Beispiel nur ein paar mal die Woche an bestimmten Tagen geschaltet werden soll, zB nur Mittwochs das Wasser ablassen ;)
    Stunde => Beinhaltet die Stunde, im 24h Format.
    Minute => Beinhaltet die Minute, mit führender 0.
    id_Typ => Beinhaltet die ID des zu schaltenden Geräts, zum Beispiel "4" was dann "Nachfüllanlage" wäre.
    id_Status => Beinhaltet die ID des Schaltzustands, also 0 oder 1.

    Code
    CREATE TABLE Schaltzeit (id INT(11) NOT NULL AUTO_INCREMENT, WochenTag INT(1), Stunde INT(2), Minute NOT NULL INT(2), id_Typ INT(11) NOT NULL, id_Status INT(11) NOT NULL, PRIMARY KEY (id))

    Für jeden Schaltvorgang brauchst du also je 2 Einträge:
    - zum Einschalten.
    - zum Ausschalten.

    Beispiel:

    SQL
    INSERT INTO Schaltzeit (Minute, id_Typ, id_Status) VALUES ('10', '4', '1')
    INSERT INTO Schaltzeit (Minute, id_Typ, id_Status) VALUES ('30', '4', '0')


    Es soll also an jedem Tag und zu jeder Stunde, ein mal um :10 Gerät 4 eingeschaltet und um :30 wieder ausgeschaltet werden.
    Wenn es jeden Tag und jede Stunde geschaltet werden soll bleiben die "WochenTag" & "Stunde" Spalten leer.

    Dann noch die anderen Tabellen mit den nötigen Infos erzeugen/befüllen:

    In die "Vorgang" Tabelle würde dann zum Beispiel folgendes eingetragen werden:
    id = 1
    id_Typ = 1
    id_Status = 1
    Zeit = <unixtimestamp>

    id = 2
    id_Typ = 2
    id_Status = 0
    Zeit = <unixtimestamp>

    id = 3
    id_Typ = 3
    id_Status = 1
    Zeit = <unixtimestamp>

    id = 4
    id_Typ = 2
    id_Status = 1
    Zeit = <unixtimestamp>

    Die "Vorgang" Tabelle ist dann auch die in der du UPDATES durchführst sofern ein Schaltvorgang geändert wird. Um welchen Typ es sich dabei handelt kann man aus der jeweiligen Tabelle anhand der id_Typ JOIN 'nen (Verknüpfungen), ebenso mit dem Status usw, zum Beispiel:

    SQL
    SELECT Typ.Bezeichnung AS TypBezeichnung, Status.Bezeichnung AS StatusBezeichnung, Zeit FROM Vorgang LEFT JOIN Typ ON Vorgang.id_Typ = Typ.id LEFT JOIN Status ON Vorgang.id_Status = Status.id;

    Ergebnis:

    Code
    +--------------------+-------------------+------------+
    | TypBezeichnung     | StatusBezeichnung | Zeit       |
    +--------------------+-------------------+------------+
    | Nachtlicht         | An                | 1409332866 |
    +--------------------+-------------------+------------+

    Alternativ kann man sich natürlich die Daten auch einzeln raus lutschen. In PHP sah das seinerzeit so aus:


    [/spoiler]


    Aber das ist nur ein Beispiel - du musst es nicht so machen :fies:
    Quelle: cron Uhrzeit überprüfen nach reboot


    Alternative https://www.forum-raspberrypi.de/Thread-php-wecker

  • Hallo,

    vielen Dank für eure Antworten. Das ist schon sehr hilfreich.

    Ich denke, ich werde es initial über vier einfache Cronjobs zum Ein- bzw. Ausschalten machen und es dann peu à peu weiterentwickeln.

    meigrafd: Wie sah dann dein Cronjob aus? Wann lief der?

    Viele Grüße,

    Dirk

  • Ein einziger Crontabeintrag, der jede Minute das Script ausführt und das Script prüft dann ob es Zeit ist etwas zu tun :fies: Dadurch brauch man an Crontab nichts mehr zu ändern.

    Naja Oke, zwei Crontabeinträge, um bei einem Reboot den alten Schaltzustand wiederherzustellen....

    Siehe Beitrag#8 im Spoiler.


    LG,
    Dirk
    :D

  • Hallo,

    noch als kurzer Nachtrag (falls jemand eine ähnliche Problemstellung hat wie ich): Ich bin ja davon ausgegangen, dass ich pro Ventil zwei Skripte habe, das eine zum Öffnen des Ventils, das anschließend beendet wird und das andere zum Beenden.
    Beim Öffnen klappte alles super, wenn ich es jedoch über das zweite Skript schließen wollte bekam ich die Meldung, dass "This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
    GPIO.setup(9, GPIO.OUT)".

    Ich hatte mir dann diesen Beitrag durchgelesen, dass ich immer den GPIO-Zustand bereinigen muss, wenn ein Skript beendet wird (ob bewusst beendet oder durch Exception): http://raspi.tv/2013/rpi-gpio-…protect-your-pi
    Das Problem war nun nur, dass durch dieses cleanup das Ventil direkt nach dem Öffnen wieder geschlossen wurde oder, was ich mir auch vorstellen könnte (ich teste aktuell noch ohne Wasser), dass der PIN des Pi auf GPIO.HIGH war, das Relais der Stromkreis am Relais fürs Ventil aber immer noch offen. Wenn das zweite Skript dann sagte "öffne den Stromkreis mal wieder" machte der Raspberry Pi nichts, da sein PIN den Zustand ja schon hatte.

    Ich habe es nun zu testzwecken so verpackt, dass ich sowohl öffnen, als auch schließen in jeweils einem Skript habe und dann aktuell über ein sleep warte, bis es eine bestimmte Zeit abgelaufen ist.

    Im echten Skript würde ich es so umsetzen, dass jeder Kreislauf weiß (steht z.B. in der DB), wie lange er offen sein soll / muss und wenn diese Zeit über eine while(true)-Schleife erreicht ist, schließt er seine Ventile und wenn alle Kreisläufe fertig sind wird die while-Schleife verlassen und der cleanup durchgeführt.

    Viele Grüße,

    Dirk

  • Das ist nur eine Meldung/Hinweis die man auch deaktivieren kann (steht da sogar wie das geht).
    Wenn du aber ein Script nutzt nur um einen einzigen GPIO einzuschalten und direkt danach wieder beendest, wäre es vielleicht sinnvoller stattdessen einen Konsolen Befehl zu verwenden... Installier dir dafür "wiringpi" und lass über Crontab dann einfach direkt den "gpio" Befehl ausführen.
    Ansonsten empfehle ich dir eine einzige Scriptdatei, der du bei Ausführung ein/zwei Argument übergibst und im Script dann geregelt wird was entsprechend passieren soll.

    Ein Problem was du kriegen könntest und auch beachten solltest, wäre: was passiert wenn der Pi (warum auch immer) zwischen zwei Schaltvorgängen abschmiert/neustartet?
    Wurde zuvor das Ventil geöffnet und dein Lösungsweg sieht vor dass das Script nur ein mal ausgeführt wird und dann über ein "sleep" bis zum wieder abschalten wartet, wird das Ventil nie wieder geschlossen und dein Garten säuft ab.
    Keine Sorge, auch hierfür gibt es Lösungen ;) Vorausgesetzt der Pi fährt auch wieder hoch...

  • Danke. Wie in dem von mir ausgeführten Link beschrieben wurde, soll man es aber nicht einfach nur "ignorieren" bzw. warnings abschalten.
    Ich meine auch, dass es das Ventil gar nicht geschlossen hat. Aktuell arbeite ich nur nach Gehör, da ich erst heute meine Ventilbox bekomme und auch die Fittings,... noch nicht bestellt habe, werde ich erst heute über eine Gartenschlauchkupplung die Ventile mit einem Gartenschlauch verbinden (hatte ich ganz am Anfang mal mit einem noch simpleren Skript gemacht, um zu sehen, ob das Ventil wirklich öffnet.

    Aktuell höre ich nur ein leises klicken des Relais und ein lautes des Ventils, wenn es öffnet / schließt. Erst durch entsprechenden (Wasser-)Druck merke ich, ob es wirklich geöffnet / geschlossen wird.

    Ich bilde mir nun ein, dass ich beim Schließen weder das Klicken des Relais, noch das des Ventils gehört habe, wenn diese Meldung kam.

    Das mit dem "wenn der Raspberry Pi abschmiert" ist auf jeden Fall ein guter Tipp und sollte ich ausprobieren, indem ich ihn bei laufendem Betrieb einfach neustarten lasse oder herunterfahre.
    Da würde ich erwarten, dass an den GPIOs keine Spannung mehr anliegt und somit das Relais aufgeht?!

    Vielen Dank und viele Grüße,

    Dirk

  • Hallo Meigrafd,


    Wurde zuvor das Ventil geöffnet und dein Lösungsweg sieht vor dass das Script nur ein mal ausgeführt wird und dann über ein "sleep" bis zum wieder abschalten wartet, wird das Ventil nie wieder geschlossen und dein Garten säuft ab.


    Das sollte man durch die Wahl des Relais (stromlos: angezogen) verhindern. Ein solches Relais schließt das Ventil, wenn kein Strom anliegt. Bei den meisten GPIOs solte das der Zustand sein, der besteht, wenn der RPi heruntergefahren ist oder hochgefahren wird (Ausnahme GPIO 14).

    Wenn durch solche Effekte das Relais unbeeinflusst sein soll und immer sichergestellt sein soll, dass der Schaltzustand zwischen Absturz und Hochfahren nicht beeinflusst werden soll, kann man sich immer noich der Selbsthalteschaltung bedienen.


    Beste Grüße

    Andreas

    Ich bin wirklich nicht darauf aus, Microsoft zu zerstören. Das wird nur ein völlig unbeabsichtigter Nebeneffekt sein.
    Linus Torvalds - "Vater" von Linux

    Linux is like a wigwam, no windows, no gates, but with an apache inside dancing samba, very hungry eating a yacc, a gnu and a bison.


  • Ein einziger Crontabeintrag, der jede Minute das Script ausführt und das Script prüft dann ob es Zeit ist etwas zu tun :fies: Dadurch brauch man an Crontab nichts mehr zu ändern.

    Naja Oke, zwei Crontabeinträge, um bei einem Reboot den alten Schaltzustand wiederherzustellen....

    Siehe Beitrag#8 im Spoiler.


    LG,
    Dirk
    :D

    Ehrlicherweise verstehe ich dann noch nicht, warum das komplette Starten des Skriptes jede Minute (was passiert, wenn es länger als eine Minute benötigt?) besser sein soll, als ein Skript, dass durchgängig läuft, mit zeitweisen Timeouts?

    Klar, der Cron-Service läuft die ganze Zeit im Hintergrund, feuert dann aber im Zweifel jedes mal die ganze Python-Maschinerie an, um ein übersichtliches Skript zu starten. Also ich wäre daher immernoch für ein permanentes Skript/Prozess im Hintergrund. Aber vielleicht gibt es gute Gründe?! Bin halt auch nur Quereinsteiger in die Linux-Welt ;)

    .NET-, Unity3D-, Web-Dev.
    Mikrocomputer-Hobbyist.

  • Ehrlicherweise verstehe ich dann noch nicht, warum das komplette Starten des Skriptes jede Minute (was passiert, wenn es länger als eine Minute benötigt?) besser sein soll, als ein Skript, dass durchgängig läuft, mit zeitweisen Timeouts?

    Klar, der Cron-Service läuft die ganze Zeit im Hintergrund, feuert dann aber im Zweifel jedes mal die ganze Python-Maschinerie an, um ein übersichtliches Skript zu starten. Also ich wäre daher immernoch für ein permanentes Skript/Prozess im Hintergrund. Aber vielleicht gibt es gute Gründe?! Bin halt auch nur Quereinsteiger in die Linux-Welt ;)

    Ein permanent laufendes Script kann auch mal crashen - gerade wenn man noch nicht so viel mit Python gemacht hat, können "unvorhersehbare" Probleme auftreten.
    Für die Ausfallsicherheit brauch man dann also ein weiteres Script, einen Watchdog, das ebenfalls jede Minute über Crontab ausgeführt wird, um zu prüfen ob das Python-Script läuft und falls nicht es neu zu starten. Das wären dann also mindestens ein Crontabeintrag für den Watchdog und ggf noch ein weiterer @reboot Eintrag um das Python-Script bei Systemstart zu starten... Also 2 Scripts die für den Betrieb benötigt werden.

    Natürlich kann man bei der von mir vorgeschlagenen Vorgehensweise nur minimal Minuten Intervalle abdecken, keine 0.5 oder 1.5 o.ä.., aber das ist soweit ich das verstanden habe hier auch nicht nötig.
    Benötigt eine Instanz länger als eine Minute zur Ausführung, sollte das eigentlich auch nicht weiter schlimm sein, es wird dann über Crontab trotzdem ein weiterer Prozess bzw Instanz des Scripts ausgeführt - es sei denn man hat dafür einen Schutz eingebaut wie zB ein "lock" oder "pid" File das erzeugt und geprüft wird....

    Es kommt immer darauf an was das jeweilige Script machen soll. Natürlich haben permanent-laufende-Scripts auch eine Daseinsberechtigung.

    Aber gerade wenn er ggf später mal vor hat mehrere Dinge zur gleichen Zeit zu steuern wird das über ein permanent laufendes Script mit "sleep" problematisch, da sleeps das Script blockieren bzw den einen Thread des Scripts. Sicherlich gibts dafür auch Lösungswege, die aber über meine Variante wegfallen könnten ;) Apropo: FAQ => Nützliche Links / Linksammlung => python: Schedule / mehrere Abhandlungen in einem Script / Parallelisierung

  • Danke für deine Erläuterungen.

    Okay, was ich vergessen habe zu erwähnen, dass ich ein permanentes Skript auch mit einem Daemon ausgestattet hätte, sodass ein direkter Restart des Skriptes forciert wird, sollte es crashen.

    Ich vertraue nie einem System, in dem zufälligerweise dieselben Prozesse sich überlagern und ggf. auf dieselben Resourcen zugreifen. Daher bin ich da auch eher vorsichtig und rechne immer mit dem Supergau in so einer Konstellation. Da dann lieber einmal etwas mehr Arbeit reinstecken, als wenn am Ende das Wasser bis zum Hals steht.

    So oder so aber schön zu sehen, dass aber beide Ansätze immernoch ihre "Pitfalls" haben ;)

    .NET-, Unity3D-, Web-Dev.
    Mikrocomputer-Hobbyist.

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!