LCD mit PIR steuern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
KB Lc
User
Beiträge: 1
Registriert: Samstag 12. Januar 2019, 12:35

Hallo an alle.
Ich habe dieses Forum gefunden und mich vor lauter Begeisterung gleich angemeldet. Der Grund dafür folgt unten.

Kurze Vorstellung zu meiner Person (falls es jemand wissen will):
Mein Name ist Kai und ich komme aus dem schönen Fichtelgebirge in Oberfranken. Zur Zeit habe ich sehr viel mit Elektrizität zu tun und mache das auch beruflich. Meine Hobbys decken sich mit meinen beruflichen Aktivitäten. Programmiertechnich habe ich allerdings außer SPS und KNX beruflich nichts zu tun. Dennoch gehört das nunmal zu meinem Vorhaben dazu und ich bin sehr wissbegierig und will das auch können und wissen.

Zu meinem derzeitigen Problem:

Ich habe ein 7" Raspberry Pi Touch LCD (Original Raspberry) und einen Raspberry Pi 3 mit Raspbian. Der Raspberry Pi soll eine Internetseite über Kiosk dauerhaft anzeigen.
Am Raspberry Pi ist auch ein Bewegungsmelder PIR angeschlossen. Der PIR ist an GPIO 23 angeschlossen. Er gibt ein 5V-Signal aus, wenn eine Bewegung erfolgt.
Über ein Python-Programm soll der PIR den Monitor ein und wenn keine Bewegung mehr erfolgt wieder nach einer bestimmten Zeit (nehmen wir mal 65 Sekunden) wieder ausschalten.

Was funktioniert:
Also der Raspberry Pi wird hochgefahren, zeigt in Kioskmodus die gewünschte Internetseite an. Den Bildschirm schalte ich über den Befehl
sudo chmod 777 /sys/class/backlight/rpi_backlight/bl_power;sudo echo 1 > /sys/class/backlight/rpi_backlight/bl_power
ab.

Der PIR schaltet mit dem Programm
sudo nano pir.py
das Programm
import RPi.GPIO as GPIO
import time
import os

SENSOR_PIN = 23

GPIO.setmode(GPIO.BCM)
GPIO.setup(SENSOR_PIN, GPIO.IN)

def mein_callback(channel):
os.system("sudo chmod 777 /sys/class/backlight/rpi_backlight/bl_power;sudo echo 0 > /sys/class/backlight/rpi_backlight/bl_power")
print('Es gab eine Bewegung!')
time.sleep(30)



try:
GPIO.add_event_detect(SENSOR_PIN , GPIO.RISING, callback=mein_callback)
while True:
time.sleep(100)
except KeyboardInterrupt:
print "Beende..."
GPIO.cleanup()
.

Dieses Programm habe ich selbständig erweitert und geändert. Ich hoffe es passt so. Zumindest geht das LCD bei einer Bewegung an.

Wenn ich nun time.sleep (65000) eingebe, müsste es doch 65 Sekunden an sein, bevor es wieder aus geht. Allerdings wird eine neue bewegung nicht mehr registriert. Es wird also immer nach 65 Sekunden abgeschaltet. Wie bekomme ich das hin, dass es die neue Bewegung auch registriert.

Vielen vielen Dank für jeden Tip und Hinweis.

Kai aus Oberfranken.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hallo Kai,

Man kann deinen Code nicht wirklich entziffern, weil du quote statt code tags verwendest. In Zukunft bitte die richtigen Tags benutzen, denn so geht die Einrueckung verloren, und die sind fuer Python wichtig. Im vollstaendigen Editor der </>-Button.

Kommen wir zu deinem Vorhaben und dem Code:

- time.sleep bekommt als Argument *SEKUNDEN*. Wenn du da also 65000 angibst, ist fuer 18h Licht an.
- nur weil dein callback verlassen wird, heisst das noch lange nicht, dass die Aktionen, die er vorher ausgeloest hat, magisch wieder verschwinden. Wenn du in die Kueche gehst, den Lichtschalter drueckst, 65 Sekunden wartest, und dann einfach ohne weiteres aus der Kueche verschwindest - dann ist das Licht ja auch noch immer an. Du musst das schon explizit ausschalten. Indem du genau den Befehl, den du ja oben selbst schon zeigst, absetzt.

Wenn du das alles tust, dann funktioniert das erstmal theoretisch so wie gedacht. Das Problem ist, dass du mit deinem zeitbasierten Verhalten in einer der Regionen vorgestossen bist, wo es leider ziemlich duester ist bei all den vielen Beispielen da draussen im Internet. Das macht aus irgendwelchen Gruenden einfach keiner richtig.

Denn dein Problem ist doch, dass du so lange gewartet wird, *KEINE* Erkennung ueber den PIR hast. Womit also der Fall, dass wer laenger vor deinem Screen steht als die angebotenen 65 Sekunden, IMMER ein aus/einschaltvorgang stattfindet. Ziemlich oede.

Was du eigentlich willst ist

- im Callback einen Zeitstempel setzen, wann jemand sich bewegt hat, und das Licht anschalten.
- in der Hauptschleife immer eine Sekunde(!) warten, und pruefen, ob (jetzt - zeitstempel) > 65 ist. Wenn ja, dann Licht aus.

Last but not least: bitte nicht RPI.GPIO benutzen. Das ist wirklich uralt und sollte nicht mehr benutzt werden. Stattdessen gpiozero oder pigpio benutzen. Die sind deutlich besser.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@KB Lc: Noch Anmerkungen zum Quelltext:

In der ersten Zeile sollte die übliche „she bang“-Zeile stehen, damit das unter Linux auch als Programm ausgeführt werden kann. Da würde man auch leichter sehen ob es Python 2 oder Python 3 ist.

Das es Python 2 sein muss, sieht man am zweiten ``print``, das als Anweisung verwendet wird. Die Schreibweise als Funktion beim ersten ``print`` ist also falsch und irreführend. Das sollte man konsequent machen. Entweder als Anweisung oder man importiert die `print()`-Funktion aus der Zukunft. Ich würde letzteres machen, dann muss man nicht so viel anpassen wenn man das Programm auf Python 3 portiert.

Oder man schreibt das gleich für Python 3.

Das ``as`` beim Import macht keinen Sinn. Das ist zum Umbennen gedacht, hier wird aber nicht wirklich etwas umbenannt, hier möchte man das `GPIO`-Modul genau unter diesem Namen haben. Dafür gibt es ``from … import …``.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Noch unübersichtlicher als das Hauptprogramm auf Modulebene zu schreiben, ist das vermischen von Hauptprogramm und Funktionsdefinitionen.

Das `GPIO.cleanup()` gehört in einen ``finally``-Zweig, sonst ist das ja nicht garantiert das es immer ausgeführt wird.

`mein_callback()` ist kein guter Name. Da weiss ein Leser weder wann der aufgerufen wird, noch was der macht. So etwas wie `on_movement()` wäre passender.

`os.system()` sollte man nicht mehr verwenden. Die Dokumentation verweist auf das `subprocess`-Modul.

Den Pfad zur Beleuchtung würde ich nicht zweimal im Code stehen haben wollen. Den kann man als Konstante definieren.

Dieses Aufrufen von einer Shell ist IMHO aber auch sehr unschön. Läuft denn das Programm selbst nicht schon mit Root-Rechten? Oder kann man nicht alternativ die Datei für die LCD-Beleuchtung für Benutzer oder eine Gruppe unter das Programm läuft, freigeben? Dann kann man diese Sachen einfach mit Python selbst machen, ohne ein externes Programm. Nach einem ``chmod 777`` sollten auch gar kein ``sudo`` mehr für das beschreiben nötig sein. Und das ``chmod`` muss man doch auch nur *einmal* machen, und nicht jedes mal wenn man etwas in die Datei schreiben möchte‽ Oder umgekehrt: Wenn man ``sudo`` zum Schreiben in die Datei verwendet, braucht man vorher die Rechte der Datei nicht zu ändern.

Falls das Programm nicht mit Root-Rechten läuft, warum hast Du dann ein ``sudo`` vor ``nano pir.py`` geschrieben?
Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
from __future__ import absolute_import, division, print_function
import subprocess
import time

from RPi import GPIO

SENSOR_PIN = 23
BACKLIGHT_PATH = '/sys/class/backlight/rpi_backlight/bl_power'


def on_movement(_channel):
    # 
    # TODO Change access to `BACKLIGHT_PATH` so no external shell and ``sudo``
    #   must be used.
    # 
    subprocess.call('sudo echo 0 > {0}'.format(BACKLIGHT_PATH), shell=True)
    print('Es gab eine Bewegung!')
    time.sleep(30)


def main():
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(SENSOR_PIN, GPIO.IN)
    try:
        GPIO.add_event_detect(SENSOR_PIN, GPIO.RISING, callback=on_movement)
        while True:
            time.sleep(100)
    except KeyboardInterrupt:
        print('Beende...')
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Für das was Du vorhast braucht man objektorientierte Programmierung (OOP). Du solltest Dich also im Tutorial bis mindestens einschliesslich Klassen durcharbeiten.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten