Wählgerät für eine bestehende Alarmanlage

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Stevo
User
Beiträge: 5
Registriert: Dienstag 21. November 2017, 17:30

Moin moin zusammen :)
Ich arbeite gerade an einem Projekt, eine bestehende Alarmanlage in meinem Gartenhaus mit einem selbstgebauten GSM-Wählgerät zu erweitern. Dazu habe ich ein Pi2 genommen, bei dem die Eingänge über Relais der Alarmanlage angesteuert werden sollen. Die Ereignisse werden in einer Log gespeichert, Alarmmeldungen sollen zusätzlich als SMS übertragen werden.
Das Skript läuft soweit schon so wie es soll. Einziges Problem ist, dass es bei Auslösung eines Kontaktes auch die Zustände der anderen Kontakte in die Log schreibt bzw. eine SMS versendet.
Es gibt die drei Ereignisse (Scharf/Unscharf, Alarm/Alarm Rückstellung, Störung/Störungs Rücksetzung).
Beispiel: Alle Kontakte sind geschlossen, die anlage hat die Zustände Unscharf, Alarm rückgesetzt und Störung rückgesetzt. Wenn ich nun den Störungskontakt öffne, dann werden folgende Dinge gelogt: Unscharf, Alarm rückgesetzt, Störung. Dabei möchte ich in dem Fall natürlich nur die Zustandsänderung der Störung loggen.

Das Skript sieht wie folgt aus:

Code: Alles auswählen

#!/usr/bin/python3
import os
import RPi.GPIO as IO
import time
import datetime



TELEPHON_NUMBERS = "/home/pi/Alarm/alarm-system-dialer/telephonnumbers.txt"
LOGFILE = "/home/pi/Alarm/alarm-system-dialer/log.txt"
IO.setmode(IO.BCM)
ALARM_PIN = 22
ARM_PIN = 5
ERROR_PIN = 17
SYSTEM_STATUS = 26
IO.setup(SYSTEM_STATUS, IO.OUT)
IO.setup(ALARM_PIN, IO.IN, pull_up_down=IO.PUD_DOWN)
IO.setup(ARM_PIN, IO.IN, pull_up_down=IO.PUD_DOWN)
IO.setup(ERROR_PIN, IO.IN, pull_up_down=IO.PUD_DOWN)


def arm_disarm(channel):
    msg = "Scharf" if IO.input(ARM_PIN) == 0 else "Unscharf"
    with open(LOGFILE, 'a') as log:
        log.write('{} {}\n'.format(datetime.datetime.now(), msg))


def system_error(channel):
    msg = "Stoerung" if IO.input(ERROR_PIN) == 0 else "Stoerung beendet"
    with open(LOGFILE, 'a') as log:
        log.write('{} {}\n'.format(datetime.datetime.now(), msg))


def alarm_detection(channel):
    msg = "Einbruchalarm" if IO.input(ALARM_PIN) == 0 else "Einbruchalarm beendet"
    with open(LOGFILE, 'a') as log:
        log.write('{} {}\n'.format(datetime.datetime.now(), msg))
    with open(TELEPHON_NUMBERS, 'r') as numbers:
        for number in numbers:
            os.system('echo Alarmanlage meldet: %s | sudo gammu-smsd-inject TEXT %s' % (msg, number))


IO.add_event_detect(ALARM_PIN, IO.BOTH, callback=alarm_detection, bouncetime=100)
IO.add_event_detect(ARM_PIN, IO.BOTH, callback=arm_disarm, bouncetime=100)
IO.add_event_detect(ERROR_PIN, IO.BOTH, callback=system_error, bouncetime=100)

try:
    while True:
        time.sleep(0.1)
        IO.output(SYSTEM_STATUS, IO.LOW)

except KeyboardInterrupt:
IO.cleanup()
Das Logfile, wenn ich wie oben beschrieben ein Event auslöse, sieht dann wie folgt aus:

Code: Alles auswählen

2017-11-28 12:43:23.727971 Einbruchalarm beendet
2017-11-28 12:43:23.730008 Stoerung beendet
2017-11-28 12:43:23.731050 Unscharf
2017-11-28 12:43:26.194756 Unscharf
2017-11-28 12:43:26.828675 Stoerung beendet
2017-11-28 12:43:26.829609 Einbruchalarm beendet
2017-11-28 12:43:27.542416 Unscharf
2017-11-28 12:43:30.178337 Unscharf
2017-11-28 12:43:30.241358 Einbruchalarm beendet
2017-11-28 12:43:30.243009 Stoerung beendet
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Eigentlich sehe ich das deinem Code so nicht an, das er da mehrere Dinge auf einmals ausloesen sollte.

Davon abgesehen jedoch ist dein vorgehen sehr problematisch, weil du in den event-handlern viel zu viel machst. Das faellt dir frueher oder spaeter auf die Fuesse. Ein alternativer Ansatz, der dieses Problem loest und gleichzeitig deine Hautpschleife etwas besser gestaltet sieht so aus (Python 2, und ungetestet weil nicht am Pi):

Code: Alles auswählen

import Queue
from functools import partial

# eine funktion fuer all
def pin_detect(queue, name, channel):
      queue.put((name, IO.input(channel))


def main():
     # setup gpios...
     queue = Queue.Queue()
     IO.add_event_detect(ALARM_PIN, IO.BOTH, callback=partial(pin_detect, queue, "alarm"), bouncetime=100)
     IO.add_event_detect(ERROR_PIN, IO.BOTH, callback=partial(pin_detect, queue, "error"), bouncetime=100)
     ... 
     while True:
          pin_name, is_on = q.get() # kann ggf. auch mit timeout kombiniert werden, wenn zeitabhaengige Dinge passieren
          if pin_name == "alarm": 
              ....

Auf diese weise gehen dir keine Signale verloren, auch wenn der Hauptthread noch etwas zu tun hat.

Ein paar weitere Anmerkungen:

- rpi.gpio ist echt Mist. Wirklich. Es ist langsam, und unzuverlaessig. Nimm stattdessen mindestens gpiozero, besser noch pigpio.
- statt von Hand zu loggen schau dir mal das modul logging an. Damit kannst du dann zB auch das syslog verwenden, rotieren, archivieren, etc.
Stevo
User
Beiträge: 5
Registriert: Dienstag 21. November 2017, 17:30

@__deets__ danke für die rasche Antwort. Habe deine Version versucht anzupassen, hat leider nicht funktioniert.

Habe aber auch festgestellt, dass mein das Skript annährend so funktioniert wie es soll, wenn ich die Relais über die Anlage direkt ansteuere. Habe zuvor immer den Pin vom Pi abgezogen. Kann mir das nicht erklären. :K
Nun ist es so, wenn die Relais geschlossen sind, werden die Status so angezeigt, wie sie sollen. Beim öffnen der Relais ändert sich zwar auch der Status, aber wenn sie längere Zeit offen sind, dann tritt oben genannter Effekt wieder auf :K
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich wiederhole mich da, aber rpi.gpio ist wirklich, wirklich nicht gut. Gerade bei so dingen wie "nach laengerer Zeit" und so ist das mein erster Verdaechtiger.

Bau das um. Ja, es ist anstrengend, aber muss. Und auch meine anderen Hinweise sind nicht umsonst gewesen. Du wirst da keine Freude dran haben, wenn du so weiter machst.
Stevo
User
Beiträge: 5
Registriert: Dienstag 21. November 2017, 17:30

Ja, kann ja nicht schaden. Danach kann ich zumindestens sicher sein, ob es daran liegt oder nicht. Danke dir 8)
Antworten