Raspberry - Python Script - Aktion alle 5 Minuten ausführen ?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
motte3009
User
Beiträge: 10
Registriert: Sonntag 17. März 2013, 20:37

Moin !

Ich habe auf meinem Raspberry ein kleines Python-Script, welches per WiringPi einen GPIO abfragt (Taster) und bei jeder Betätigung eine Variable hochzählt.

Das Script funktioniert, jetzt würde ich gerne alle 5 Minuten den Wert der Variable auf meinen Server hochladen und die Variable dann auf Null setzen.

Das Hochladen auf den Server ist nicht das Problem, ich finde einfach keine Möglichkeit, zu definieren: wenn Minute = 05, 10, 15, 20 u.s.w., dann löse das Hochladen aus...

Habt ihr eine Idee oder einen Denkanstoss ?

Danke, Gruss Motte
BlackJack

@motte3009: Denkanstoss ist das `threading`-Modul. Nicht unbedingt um damit einen Thread zu starten, denn den würde ich von RPi.GPIO für den Taster starten lassen, sondern um den Zähler dann mit einem `Lock` zu schützen. Das Thema nebenläufige Programmierung ist allerdings nicht ganz trivial und wenn man das sauber lösen möchte kommt man um objektorientierte Programmierung nicht herum. Für das Warten auf die Zeitpunkte würde ich das `sched`-Modul aus der Standardbibliothek verwenden.
motte3009
User
Beiträge: 10
Registriert: Sonntag 17. März 2013, 20:37

Hallo, danke für deine Antwort. Da hab ich wohl noch einiges zu lesen b.z.w. zu lernen... :-)

Gruss
Motte
motte3009
User
Beiträge: 10
Registriert: Sonntag 17. März 2013, 20:37

So, nachdem ich Stunden damit zugebracht habe, dieses "Threading-Modul" zu verstehen und auch programmieren zu können, bin ich auf eine für mich funktionierende Lösung gestossen.

Ziel war es, einen GPIO-Kontakt (Taster) abzufragen und die Summe aller Betätigungen alle 5 Minuten an einen Server zu übertragen.

Code: Alles auswählen

#!/usr/bin/python

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

counter = 0
GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.IN)

a = ['00','05','10','15','20','25','30','35','40','45','50','55']

while True:

        lt = localtime()
        minute = strftime("%M", lt)
        sekunde = strftime("%S",lt)



        if GPIO.input(4) == GPIO.HIGH:
                counter = counter + 1
                sleep(0.3)


        if minute in a and sekunde == "00":

                minute = str(minute)
                counter = str(counter)
                print "Minute: " + minute
                print "Counter: " + counter
                counter = 0
                sleep(1)
Dieser Code zeigt erst mal die Minute und die Anzahl der Taster-Betätigungen an. Das Hochladen auf den Server ist ist dann kein Problem.

Die Profis unter euch werden wahrscheinlich die Hände über den Kopf zusammenschlagen. Ich weiss, der Code ist nicht wirklich toll, aber er funktioniert, erstmal....

Gruss Motte
Zuletzt geändert von Anonymous am Montag 11. Juli 2016, 20:44, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@motte3009: Ich schlag dann mal die Hände über dem Kopf zusammen. :-)

Der erste Import ist komisch geschrieben, das liegt aber nicht an Dir sondern das der Raspi Python langsam zu PHP verkommen lässt wo Leute die keine Ahnung haben, Sachen von Leuten abschreiben die auch keine Ahnung haben. ``from RPi import GPIO`` hat den gleichen Effekt, vermeidet aber die unsinnige Wiederholung

Sternchenimporte sollte man vermeiden. Zumal Du `time` auch vorher schon mal importiert hast. Entscheide Dich für `time` importieren und die Werte dann über das Modul zu referenzieren, oder explizit die Namen aufzulisten, die Du aus dem Modul brauchst. Sternchenimporte machen Programme schnell schwer durchschaubar und können auch zu Namenskollisionen führen.

`a` ist ein schlechter Name für eine Liste mit Zeichenketten mit Ziffern. Die Zeichenketten sind schon keine gute Idee. Minuten und Sekdungen sind Zahlen und als solche sollte man sie innerhalb des Programms auch verarbeiten. Ob eine Zahl durch 5 teilbar ist, macht man nicht mit einer Liste wo alles durch 5 teilbaren Minutenwerte als Zeichenketten aufgeführt sind, sondern in dem man sich den Rest von der Division der Minuten durch 5 mittels Modulo-Operation anschaut ob der 0 ist. Das Ergebnis von `localtime()` hat die gewünschten Informationen ja sogar bereits als Zahlen. Ich würde trotzdem das `datetime`-Modul dafür bevorzugen wenn man das schon über „busy waiting“ löst.

Das `sleep()` entschärft es ein wenig, aber es bleibt „busy waiting“. Du beschäftigst die CPU unnötig mit warten. Andererseits verhindert das warten die Erkennung von kurzen Tastendrücken und alle 5 Minuten blockiert es sogar eine ganze Sekunde die Eingabeerkennung per GPIO. Das kann mehr werden wenn Du da tatsächlich den Upload auch noch synchron erledigst, denn der kann je nach Netzwerksituation blockieren.

`minute` ist eine Zeichenkette. Die braucht man nicht noch mal in eine Zeichenkette zu wandeln, das bringt nichts.

Und `counter` ist in dem Programm mal an eine Zahl und mal an eine Zeichenkette gebunden. Das ist momentan verwirrend, wird vielleicht irgendwann mal beim weiterentwickeln zu einer Fehlerquelle. Ausserdem sind beide Zuweisungen überflüssig. Das hätte man auch in den ``print``-Zeilen schreiben können, oder noch besser, man hätte die Umwandlung ``print`` überlassen können. Da kann man ja mehr als einen Wert angeben, und ``print`` wandelt alles in Zeichenketten um.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@motte3009: wenn Du meinst, dass das funktioniert, dann laß das nicht auf einem System laufen, bei dem noch irgend etwas anderes passiert. Dann kann es nämlich sein, dass mal eine Sekunde übersprungen wird und Du statt eines 5min-Intervalls ein 10min-Intervall betrachtest.
BlackJack

Komplett ungetestet:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
from datetime import datetime as DateTime
from sched import scheduler as Scheduler
from threading import Lock
from time import sleep, time

from Rpi import GPIO

BUTTON_PIN = 4
FIVE_MINUTES = 300  # in seconds.


class Counter(object):

    def __init__(self, button_pin):
        self.lock = Lock()
        self.counter = 0
        GPIO.setup(button_pin, GPIO.IN)
        GPIO.add_event_detect(button_pin, GPIO.RISING, self.count)

    def count(self):
        with self.lock:
            self.counter += 1

    def get_and_reset(self):
        with self.lock:
            result = self.counter
            self.counter = 0
        return result


class UploadScheduler(object):

    def __init__(self, counter, interval):
        self.counter = counter
        self.interval = interval
        self.scheduler = Scheduler(time, sleep)

    def upload(self):
        count = self.counter.get_and_reset()
        print('{0:%H:%M} #{1}'.format(DateTime.now(), count))
        # 
        # TODO Upload here. Or better put it in a `Queue` and upload
        #   in a thread to make sure it doesn't block the next upload.
        # 
        self.enter_next()

    def enter_next(self):
        now = int(time())
        self.scheduler.enterabs(
            now + self.interval - now % self.interval,
            0,
            lambda _: self.upload(),
            None
        )

    def run(self):
        self.enter_next()
        self.scheduler.run()


def main():
    GPIO.setmode(GPIO.BCM)
    try:
        UploadScheduler(Counter(BUTTON_PIN), FIVE_MINUTES).run()
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
motte3009
User
Beiträge: 10
Registriert: Sonntag 17. März 2013, 20:37

Hallo, ich danke euch allen für eure Tipps und Hinweise und werde versuchen, das alles umzusetzen b.z.w. daraus zu lernen...

Gruss Motte
Antworten