RGB LED über python einschalten

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
lexnared
User
Beiträge: 4
Registriert: Donnerstag 9. April 2020, 19:26

Hallo zusammen,

Ich verweile mich gerade an einem SmartMirror mit LED Hintergrundbeleuchtung.
Dieser funktioniert soweit super!
Auf dauer habe ich mir noch ein PIR Sensor verbaut, der bei Bewegung den Bildschirm für eine Minute einschaltet.

Nun würde ich gerne noch die WLAN RGB LED mit dem Bildschirm einschalten und ausschalten lassen.
Die LED muss ich bisher über das Terminal ein und ausschalten.
Die nötige Programmierung hierzu gibt es auf github
Für die LED EIN bzw. AUS muss ich im Terminal folgendes eingeben.

EIN:

Code: Alles auswählen

python -m flux_led 192.168.1.104 --on
AUS:

Code: Alles auswählen

python -m flux_led 192.168.1.104 --off

Das Programm für den PIR Sensor wurde auch von github übernommen

Code: Alles auswählen

#!/usr/bin/env python
 
import sys
import time
import RPi.GPIO as io
import subprocess
 
io.setmode(io.BCM)
SHUTOFF_DELAY = 20  # seconds
PIR_PIN = 23        # Pin 11 on the board
 
def main():
    io.setup(PIR_PIN, io.IN)
    turned_off = False
    last_motion_time = time.time()
 
    while True:
        if io.input(PIR_PIN):
            last_motion_time = time.time()
            sys.stdout.flush()
            if turned_off:
                turned_off = False
                turn_on()
                led_on()
        else:
            if not turned_off and time.time() > (last_motion_time + SHUTOFF_DELAY):
                turned_off = True
                turn_off()
        time.sleep(.1)
 
def turn_on():
    subprocess.call("sh monitor_on.sh", shell=True)
 
def turn_off():
    subprocess.call("sh monitor_off.sh", shell=True)
    
def led_on():
	subprocess.call("sh led_on.sh", shell=True)
 
if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        io.cleanup()
Die Idee war den Befehl "python -m flux_led 192.168.1.104 --on" mit einem sh skript aufzurufen.
Folgender def Anweisung wurde von mir angelegt

Code: Alles auswählen

def led_on():
	subprocess.call("sh led_on.sh", shell=True)
Jedoch habe ich noch keinen funktionierenden Lösungsansatz gefunden.
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lexnared: Das startet ja eine Shell, mit der Du dann eine Shell startest, in der ein Shellskript dann einen Python-Interpreter mit Python-Modul startet. Äh, nein, bitte nicht!

Die erste Shell wird man los wenn man das ``shell=True`` weg lässt und das Kommando nicht als Zeichenkette sondern als bereits auf Argumente aufgeteilte Liste angibt (und aus `call()` kann man dann auch gleich mal `run()` machen):

Code: Alles auswählen

    subprocess.run(["sh", "led_on.sh"], check=True)
Und dann vergessen wir mal das unnötige Shell-Skript und führen den Python-Aufruf direkt durch, und dass dann bitte auch mit Python 3, weil Python 2 nicht mehr verwendet werden sollte:

Code: Alles auswählen

    subprocess.run(["python3", "-m", "flux_led", "192.168.1.104", "--on"], check=True)
Aber auch das ist noch zu umständlich denn `flux_led` ist ja ein Python-Modul. Im Repository sind auch beispiele wie man das von Python aus verwendet, also direkt im Programm, ohne noch mal einen externen Python-Prozess zu starten.

Du hast übrigens das falsche Repository verlinkt. Das war ein Fork der ein paar Commits hinterher ist. Das richtige wäre das hier: https://github.com/Danielhiversen/flux_led

Komm aber als allererstes mal von Python 2 weg. Dafür gibt es von den Python-Entwicklern keine Unterstützung mehr und das wird eher früher als später aus den Linux-Distributionen verschwinden.
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lexnared: Anmerkungen zum Quelltext:

`GPIO` als `io` zu importieren ist IMHO keine so gute Idee, weil es in der Standardbibliothek ein `io`-Modul gibt. Das kann also den Leser verwirren und wird zu einem Problem wenn man *das* Modul irgendwann mal importieren möchte.

``gpio.setmode(gpio.BCM)`` und das ``try``/``except`` gehört auch in die `main()`-Funktion.

Der `clenup()`-Aufruf gehört nicht in den ``except``-Zweig. Der soll ja *immer* am Ende ausgeführt werden, also muss der in ein ``finally``.

Anstelle von `time.time()` würde man hier eher `time.monotonic()` verwenden.

Bei den Funktionen die externe Programme starten, wie gesagt `run()` verwenden, kein ``shell=True``, aber den Rückgabewert prüfen lassen, und da muss auch nicht noch mal extra "sh" gestartet werden sondern einfach nur das jeweilige Shell-Skript. Sofern es überhaupt Sinn macht da ein Shell-Skript zwischen zu schalten. Wie die beiden Monitor-Skripte aussehen wissen wir ja nicht, aber wenn das auch nur ein Aufruf eines weiteren Programms mit Argumente ist, kann man sich das sparen und den Aufruf gleich direkt mit `subprocess.run()` erledigen.

Mit `sys.executable` hat man einen Pfad zu dem Python-Interpreter der gerade ausgeführt wird.

``sys.stdout.flush()``? Warum?

`turned_off` würde ich in `turned_on` ändern, damit man keine doppelten Verneinungen lesen muss.

Selbst wenn ``monitor_on.sh`` und ``monitor_off.sh`` komplexer sein sollten, sind die tatsächlich so verschieden, dass man da nicht *ein* Skript schreiben kann, dem man eine Option zum an-/ausschalten mitgeben kann?

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import subprocess
import sys
import time

from RPi import GPIO as gpio

SHUTOFF_DELAY = 20  # in seconds.
PIR_PIN = 23  # Pin 11 on the board.


def turn_on():
    #
    # TODO
    # 
    # 1) Are ``monitor_on.sh`` and ``monitor_off.sh`` really that different that
    #    can't be merged into one skript with an argument to turn the monitor on
    #    and off?
    #
    # 2) Are those shell scripts really that complex that they can't be
    #    expressed in Python?
    #
    subprocess.run(["monitor_on.sh"], check=True)


def turn_off():
    subprocess.run(["monitor_off.sh"], check=True)


def led_on():
    #
    # TODO Do the neccessary steps in this programm.  `flux_led` is an
    # importable module.  There are examples in its repository.
    #
    subprocess.run(
        [sys.executable, "-m", "flux_led", "192.168.1.104", "--on"], check=True
    )


def main():
    try:
        gpio.setmode(gpio.BCM)
        gpio.setup(PIR_PIN, gpio.IN)
        turned_on = True
        last_motion_time = time.monotonic()
        while True:
            if gpio.input(PIR_PIN):
                last_motion_time = time.monotonic()
                if not turned_on:
                    turned_on = True
                    turn_on()
                    led_on()
            else:
                if turned_on and time.monotonic() > (
                    last_motion_time + SHUTOFF_DELAY
                ):
                    turned_on = False
                    turn_off()
            time.sleep(0.1)
    except KeyboardInterrupt:
        pass  # User pressed Ctrl+C.
    finally:
        gpio.cleanup()


if __name__ == "__main__":
    main()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
lexnared
User
Beiträge: 4
Registriert: Donnerstag 9. April 2020, 19:26

@__blackjack__
Vielen Danke für die Ausführliche Verbesserung!
Das Skript habe ich hier gefunden.

Ich setze mich heute Abend an die Verbesserung des Programmes.
Inzwischen funktioniert mein vorhaben.
Aber Sporadisch schaltet die RGB-LED nicht aus.
Vllt. wegen des Mehrfachen aufrufen der Shell?
Wenn diese fall nicht auftritt, ist das WLAN Modul auch nicht über den PING erreichbar.

Ich bin aber sehr zuversichtlich, das dieser BUG erledigt ist, sobald ich das Programm nach deinen Vorgaben angepasst habe.

Zu deiner Frage.

led_on.sh

Code: Alles auswählen

#!/bin/bash

echo "led an 0"
python -m flux_led 192.168.1.104 --on
echo "led an 1"
led_off.sh

Code: Alles auswählen

#!/bin/bash

echo "led aus 0"
python -m flux_led 192.168.1.104 --off
echo "led aus 1"
monitor_on.sh

Code: Alles auswählen

#! /bin/bash
vcgencmd display_power 1
monitor_off.sh

Code: Alles auswählen

#! /bin/bash
vcgencmd display_power 0
Benutzeravatar
DeaD_EyE
User
Beiträge: 1206
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Man kann doch die Klasse aus dem Modul direkt verwenden: https://github.com/Danielhiversen/flux_ ... __.py#L499

Code: Alles auswählen

import time
from flux_led import WifiLedBulb


led = WifiLedBulb("192.168.1.104")
led.turnOn()
time.sleep(10)
led.turnOff()
Die Anzahl der Versuche kannst du den Methoden `turnOn` und `turnOff` vorgeben.
Standardmäßig werden 2 Versuche unternommen und dann abgebrochen.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Also ohne Shell-Skripte ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import subprocess
import time

from flux_led import WifiLedBulb
from RPi import GPIO as gpio

SHUTOFF_DELAY = 20  # in seconds.
PIR_PIN = 23  # Pin 11 on the board.
LED_BULB_IP = "192.168.1.104"


def switch_monitor(state):
    subprocess.run(["vcgencmd", "display_power", str(int(state))], check=True)


def main():
    try:
        gpio.setmode(gpio.BCM)
        gpio.setup(PIR_PIN, gpio.IN)
        led_bulb = WifiLedBulb(LED_BULB_IP)
        turned_on = True
        last_motion_time = time.monotonic()
        while True:
            if gpio.input(PIR_PIN):
                last_motion_time = time.monotonic()
                if not turned_on:
                    turned_on = True
                    switch_monitor(turned_on)
                    led_bulb.turnOn()
            else:
                if turned_on and time.monotonic() > (
                    last_motion_time + SHUTOFF_DELAY
                ):
                    turned_on = False
                    switch_monitor(turned_on)
            time.sleep(0.1)
    except KeyboardInterrupt:
        pass  # User pressed Ctrl+C.
    finally:
        gpio.cleanup()


if __name__ == "__main__":
    main()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Antworten