Erschütterungssensoren abfragen

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
HansDB
User
Beiträge: 3
Registriert: Montag 19. September 2022, 13:33

Hallo,

Ich soll Mithilfe im Python einen Code für Erschütterungssensoren schreiben.
Diese Erschütterungssensoren soll dabei drei Zustände haben und diese sollen Mithilfe von 3 LEDs (RGB)gekennzeichnet werden.
1.Zustand: Die Sensoren  sind unscharf und es leuchten alle 3 LED's blau
Dann gibt man ein Passwort ein und die Anlage ist Scharf geschaltet ( alle 3 LED's) leuchten grün.
Wird jetzt ein Sensor oder mehrere aktiviert, soll die ihm ( bzw ihnen) zugewiesene(n) rote(n)LED('s) leuchten.
Leider habe ich aktuell das Problem, das mein Programm es nicht schafft die Sensoren zeitgleich abzufragen.
Es fängt mit dem ersten Sensor an und auch nur mit dem, heißt egal wie sehr ich an einem der anderen Sensoren rumspiele, deren LED wird nicht rot. Es kommt ein Ein-Signal, die LED vom ersten Sensor blinkt rot, ich gebe das entschärfungspasswort ein und es geht mit dem nächsten Sensor weiter....

file:///home/pi/.config/Thonny/temp/thonny_rjb17xok.html
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ohne Code wird man das nicht beurteilen koennen. Bitte den Code posten, in den dafuer vorgesehenen code-tags, damit er lesbar bleibt.
HansDB
User
Beiträge: 3
Registriert: Montag 19. September 2022, 13:33

from gpiozero import Button
from time import sleep
from gpiozero import LED
button_1 = Button (18)
button_2 = Button(14)
button_3 = Button (15)
led_red_1 = LED(16)
led_red_2 = LED(19)
led_red_3 = LED(26)
led_green = LED(20)
led_blue = LED(21)

while True:

led_blue.on()
print("Good Morning, system disarmed")
p = "1234"

versuch= input('please input your Password to arm the system: ')
while versuch != p:
print('Wrong Password!')
versuch = input ('Input your Password: ')
print('Password correct! System armed')
led_blue.off()
led_green.on()


if button_3.wait_for_press():
print("3")
led_green.off()
led_red_3.on()
led_red_3.blink()
p = "5678"
print("ALERT")
versuch= input('please input your Password to reset ALERT: ')
while versuch != p:
print('Wrong Password!')
versuch = input ('Input your Password: ')
print('Password correct! ALERT reset')
led_red_3.off()

if button_2.wait_for_press():
print("2")
led_green.off()
led_red_2.on()
led_red_2.blink()
p = "5678"
print("ALERT")
versuch= input('please input your Password to reset ALERT: ')
while versuch != p:
print('Wrong Password!')
versuch = input ('Input your Password: ')
print('Password correct! ALERT reset')
led_red_2.off()


if button_1.wait_for_press():
print("1")
led_green.off()
led_red_1.on()
led_red_1.blink()
p = "5678"
print("ALERT")
versuch= input('please input your Password to reset ALERT: ')
while versuch != p:
print('Wrong Password!')
versuch = input ('Input your Password: ')
print('Password correct! ALERT reset')
led_red_1.off()
HansDB
User
Beiträge: 3
Registriert: Montag 19. September 2022, 13:33

Die Einrückungen sind hier zwar nicht richtig, aber im richtigen Programm Code stimmt alles.
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Deshalb ja der Hinweis von __deets__, dass Du code-Tags benutzen sollst.
`sleep` wird gar nicht benutzt, aber importiert.
Du benutzt ja im Moment blockierende Funktionsaufrufe (wait_for_press). Natürlich läuft das Programm nicht weiter, bis der Knopf gedrückt worden ist.
Lies Dir mal die Dokumentation zu gpiozero.Button durch, welche Möglichkeiten es gibt, auf Knopfdrücke zu reagieren.

`input´ ist auch ein blockierender Aufruf, so dass es schwierig wird, wenn mehrere "Sensoren" losgehen, die Passworteingabe parallel dazu durchzuführen. Wie hast Du Dir das gedacht?

Vor der öffnenden Klammer eines Funktionsaufrufst wird kein Leerzeichen gesetzt.
Programme werden in Funktionen gegliedert, Du hast 4 mal den selben Code zur Abfrage eines Passworts, das sollte aber nur einmal im Code stehen.
Einbuchstabige Variablennamen sind meist schlecht, weil nichtssagend. Was soll der Leser mit `p´ anfangen?

Auch der Code für jeden Button ist identisch, also ein Fall für eine Funktion.
Das Hauptprogramm sollte dann auch noch in einer Funktion stehen:

Code: Alles auswählen

from gpiozero import Button, LED

def check_password(prompt, password):
    versuch= input(prompt)
    while versuch != password:
        print('Wrong Password!')
        versuch = input('Input your Password: ')


def disarm_sensor(button, label, led_green, led_red):
    if button.wait_for_press():
        print(label)
        led_green.off()
        led_red.on()
        led_red.blink()
        print("ALERT")
        check_password('please input your Password to reset ALERT: ', '5678')
        print('Password correct! ALERT reset')
        led_red.off()


def main():
    button_1 = Button(18)
    button_2 = Button(14)
    button_3 = Button(15)
    led_red_1 = LED(16)
    led_red_2 = LED(19)
    led_red_3 = LED(26)
    led_green = LED(20)
    led_blue = LED(21)

    while True:
        led_blue.on()
        print("Good Morning, system disarmed")
        check_password('please input your Password to arm the system: ', "1234")
        print('Password correct! System armed')
        led_blue.off()
        led_green.on()
        
        disarm_sensor(button_3, "3", led_green, led_red_3)
        disarm_sensor(button_2, "2", led_green, led_red_2)
        disarm_sensor(button_1, "1", led_green, led_red_1)

if __name__ == "__main__":
    main()
Benutzeravatar
__blackjack__
User
Beiträge: 13066
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@HansDB: Ausgehend von dem Anmerkungen und der Überarbeitung von von Sirius3:

Man sollte keine ”magischen” Werte, also Zahlen und andere feste Werte, die man vielleicht mal ändern möchte, im Programm verteilt stehen haben. So etwas wie die Pin-Nummern und die Passworte definiert man in der Regel am Anfang des Moduls nach den Importen als Konstanten. Dort kann man die Werte dann leicht finden und ändern, und auch sinnvolle Namen vergeben. Also beispielsweise `ARMED_LED_PIN` und `DISARMED_LED_PIN`. Solche Namen wären auch besser bei den Objekten für die Hardware. Es ist für den Leser eher nicht interessand welche Farbe eine LED hat, das wird ja eh, ausserhalb des Programms in der Hardware festgelegt, sondern was die LED *bedeutet* wenn sie leuchtet. Und Erschütterungssensoren `button` zu nennen, ist auch ein kleines bisschen falsch.

Auch das `wait_for_press()` ist da ein bisschen unpassend. Man braucht hier keinen `Button`, die Basisklasse `DigitalInputDevice` reicht aus, und dort (und natürlich auch im abgeleiteten `Button`) gibt es eine `wait_for_active()`-Methode die exakt das gleiche macht.

Was man auch nicht macht ist nummerieren von Namen. Man will sich dann entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste. Also für die Konstanten/Pin-Nummern dann beispielsweise:

Code: Alles auswählen

SENSOR_PINS = [18, 14, 15]
ALARM_LED_PINS = [16, 19, 26]
Da haben wir dann gleich das nächste Problem: Daten die eigentlich zusammengehören, in ”parallelen” Listen, wo Elemente an der gleichen Position in beiden Listen zusammen gehören. Die sollten dann nicht in getrennten Listen stehen, sondern in *einer* Liste, beispielsweise als Tupel zusammengefasst (wenn es nicht zu viele Werte sind). Wir haben hier pro Erschütterungssensor eine Pin-Nummer für den Sensor, eine für die dazugehörige LED, und im Grunde auch noch eine Beschreibung, die zu dem Sensor und der LED gehört.

Auch die Passwörter würde man nicht irgendwo tief im Quelltext verstecken.

"Good Morning" würde auch Nachmittags um halb vier noch ausgegeben.

Das an `disarm_sensor()` die LED für den ”armed”-Zustand des gesamten Systems übergeben wird ist komisch bis falsch, denn der angezeigte Zustand stimmt ja auch gar nicht. Zumindest nicht wie der Code gerade geschrieben ist. Das sollte ja auch für das gesamte System gelten und man kann laut Beschreibung und laut Programm gar nicht *einzelne* Sensoren aktiv oder inaktiv schalten. Also ist auch der Funktionsname nicht so ganz passend. Das mit der LED gehört mit ins Hauptprogramm und statt `disarm_sensor()` würde ich die Funktion `acknowledge_sensor()` oder so nennen. Die beiden LEDs kennzeichnen jeweils gegenteiligen Zustand, darum sollte man das Schalten der LEDs im Code immer direkt hintereinander stehen haben, damit man leicht sehen kann wo das System umgeschaltet wird, und dass es keinen komischen Zwischenzustand geben kann in dem das Programm weder scharf noch deaktiviert ist, oder gar beides gleichzeitig.

Zwischenstand, der immer im Grunde noch das gleiche macht:

Code: Alles auswählen

#!/usr/bin/env python3
from gpiozero import LED, DigitalInputDevice

#
# A tuple per sensor with a description, the pin of the sensor, and the pin of
# the LED.
#
SENSORS = [("1", 18, 16), ("2", 14, 19), ("3", 15, 26)]

DISARMED_LED_PIN = 21
ARMED_LED_PIN = 20

ACTIVATE_PASSWORD = "1234"
DEACTIVATE_PASSWORD = "5678"


def check_password(prompt, password):
    versuch = input(prompt)
    while versuch != password:
        print("Wrong Password!")
        versuch = input("Input your Password: ")


def ackknowledge_sensor(sensor):
    label, sensor_input, sensor_led = sensor
    if sensor_input.wait_for_active():
        print(label)
        sensor_led.on()
        sensor_led.blink()
        print("ALERT")
        check_password(
            "please input your password to reset ALERT: ", DEACTIVATE_PASSWORD
        )
        print("Password correct! ALERT reset")
        sensor_led.off()


def main():
    sensors = [
        (label, DigitalInputDevice(sensor_pin_number), LED(led_pin_number))
        for label, sensor_pin_number, led_pin_number in SENSORS
    ]
    armed_led = LED(ARMED_LED_PIN)
    disarmed_led = LED(DISARMED_LED_PIN)

    while True:
        disarmed_led.on()
        armed_led.off()
        print("Hello. The system is disarmed.")
        check_password(
            "Please input your password to arm the system: ", ACTIVATE_PASSWORD
        )
        print("Password correct! System armed")
        disarmed_led.off()
        armed_led.on()

        for sensor in sensors:
            ackknowledge_sensor(sensor)


if __name__ == "__main__":
    main()
Im Hauptprogramm werden Tupel benutzt, um die Bestandteile jedes Sensors zu einem Wert zusammen zu fassen. Das geht bei drei Elementen noch, aber so etwas wird schnell unübersichtlich wenn man sich immer merken muss welcher Wert an welcher Stelle steht. Da würde es sich anbieten einen eigenen Datentyp mit `collections.namedtuple()` zu erstellen, oder gleich eine eigene Klasse zu schreiben.

Das Problem ist ja, dass Du die Sensoren der Reihe nach prüfst und nicht in der Reihenfolge, in der sie ausgelöst werden. Die Sensoren müssen gleichzeitig überwacht werden, also nebenläufig. Da gibt es wie Sirius3 schon angedeutet hat, Möglichkeiten von `gpiozero`. Am einfachsten sorgt man dafür, dass die Daten zum ausgelösten Sensor in eine `queue.Queue` gesteckt werden, die dann im Hauptprogramm in einer Endlosschleife abgearbeitet wird. Neben einer Queue wirst Du sehr wahrscheinlich noch `functools.partial()` benötigen, oder eben eine eigene Klasse mit der die Alarmanlage modelliert wird.

Und da es über Funktionsaufrufe hinweg mindestens den Zustand „scharf oder deaktiviert“ gibt, würde man insgesamt hier wohl besser eine Klasse für die Alarmanlage schreiben.

Für die Passworteingabe gibt es in der Standardbibliothek übrigens das `getpass`-Modul, damit nicht jeder die Eingabe mitlesen kann.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
hyle
User
Beiträge: 96
Registriert: Sonntag 22. Dezember 2019, 23:19
Wohnort: Leipzig

HansDB hat geschrieben: Montag 19. September 2022, 15:22 und diese sollen Mithilfe von 3 LEDs (RGB)gekennzeichnet werden.
Btw. Dafür gibt es RGBLED in gpiozero. >> https://gpiozero.readthedocs.io/en/stab ... -color-led
Alles was wir sind ist Sand im Wind Hoschi.
Antworten