gpio bei Programmstart schon High (OPi.GPIO threading)

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn der erste Ausdruck eines Modules, einer Klasse oder einer Funktion ein String ist, dann ist das ein sogenannter doc-String für die Dokumentation.
Kommentare # erklären, warum etwas in der nächsten Zeile gemacht wird.
Z.B. ›# Wörterbuch erstellen‹ erklärt nicht das warum, statt dessen sollte die Funktion besser soetwas wie ›create_command_dictionary‹ heißen.
Wenn man eine laufende Nummer braucht, nimmt man enumerate, das auch einen Startwert verträgt.
Einen String mit ‹str› nochmal in einen String umzuwandeln ist unsinnig. Explizit auf True zu prüfen ist unnötig und das was im else-Block steht ist fast identisch zum if-Block, und sollte daher zusammengefasst werden:

Code: Alles auswählen

def create_command_dictionary(udp_socket):
    command_to_func = {
        b"tor_auf_zu": partial(tor_auf_zu, udp_socket),
        b"reply_loxone": partial(reply_loxone, udp_socket)
    }
    for aus_nummer, aus_pin, initial_wert in enumerate(zip(PIN_AUS, INITIAL_AUS), start=1):
        command_to_func["ausgang_{}_ein".format(aus_nummer).encode()] = partial(
            ausgang_setzen, udp_socket, aus_pin, not initial_wert)
        command_to_func["ausgang_{}_aus".format(aus_nummer).encode()] = partial(
            ausgang_setzen, udp_socket, aus_pin, initial_wert)
    return command_to_func
Benutzeravatar
__blackjack__
User
Beiträge: 14028
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ergänzend zu den Docstrings: Neben denen die der Python-Compiler erkennt und entsprechend verarbeitet, erkennen einige Dokumentationswerkzeuge auch Zeichenketten an anderen Stellen als ”Docstrings”. Sphinx' `autodoc`-Erweiterung erkennt beispielsweise auch Zeichenketten als ”Docstrings” die auf Modulebene, Klassenebene, und in der `__init__()` nach Definitionen von Namen/Attributen stehen.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

Danke für Eure Hilfe

hab das jetzt so gelöst:

Code: Alles auswählen

def create_command_dictionary(udp_socket):
    command_to_func = {
        b"tor_auf_zu": partial(tor_auf_zu, udp_socket),
        b"reply_loxone": partial(reply_loxone, udp_socket)
    }

    for aus_nummer, aus_pin, initial_wert in zip(enumerate(PIN_AUS[DEVICE],start=1), PIN_AUS[DEVICE], INITIAL_AUS[DEVICE]):
        command_to_func["ausgang_{}_ein".format(aus_nummer[0]).encode()] = partial(
            ausgang_setzen, udp_socket, aus_pin, not initial_wert)
        command_to_func["ausgang_{}_aus".format(aus_nummer[0]).encode()] = partial(
            ausgang_setzen, udp_socket, aus_pin, not not initial_wert)
    return command_to_func
not not habe ich verwedet das das Ergebnis "True/False" und nicht "0/1" ist.


Ich hätte noch weitere Fragen:

Ich hab aus einigen Konstaten dictionary´s ersetellt.
Sind das jetzt noch Konstanten?

Sollte ich jedes Dictionary in eine Funkion mit return stecken?

Kann man Funktion erstellen die mehrere Werte rückliefern?

google hilft mir da nicht wirklich

Code: Alles auswählen

DEVICE = "keller"

SERVER = {"keller" : ("", 8008) , "garage" : ("", 8008) , "door" : ("", 8010), "loxberry" : ("", 8012)}
TARGET_HOST = {"keller" : ("10.10.10.4", 8005) , "garage" : ("10.10.10.4", 8007) , "door" : ("10.10.10.4", 8009), "loxberry" : ("10.10.10.4", 8011)}

PIN_AUS = {"keller": [11, 13, 15, 19],
                  "garage": [29, 31, 33, 35, 37],
                  "door": [29, 33, 35],
                  "loxberry": [29, 31, 33, 35, 37]}
INITIAL_AUS = {"keller": [GPIO.HIGH, GPIO.HIGH, GPIO.HIGH, GPIO.HIGH],
                      "garage": [GPIO.HIGH, GPIO.HIGH, GPIO.HIGH, GPIO.HIGH, GPIO.HIGH],
                      "door": [GPIO.LOW, GPIO.LOW, GPIO.LOW],
                      "loxberry": [GPIO.HIGH, GPIO.HIGH, GPIO.HIGH, GPIO.HIGH, GPIO.HIGH]}

Mit dem Logging muss ich mich noch intensiver beschäftigen, hab das auf die schnelle so gelöst.

Das GPIO System Liefert Warnungen, wie kann ich diese in den log eintragen?

Code: Alles auswählen

UserWarning: Pull up/down setting are not (yet) fully supported, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
schönen Sonntag
Benutzeravatar
__blackjack__
User
Beiträge: 14028
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das ``not not`` ist unsinnig. Ob 1/0 oder True/False ist ja auch egal. Falls es das nicht sein sollte dann sollte man in *beiden* Fällen `bool()` verwenden, also ``not bool(initialwert)`` und ``bool(initialwert)``.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

Danke, habe es nur wegen der Optik geändert.

Ich hätte noch eine Frage zum Logging, bzw zu RPI GPIO:

Wenn der Code nicht Richtig beendet wird habe ich beim nächsten Start solche Meldungen:

Code: Alles auswählen

in_out.py:92: UserWarning: Channel 11 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 13 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 15 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 19 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:94: UserWarning: Pull up/down setting are not (yet) fully supported, c                                                                         ontinuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
in_out.py:94: UserWarning: Channel 16 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
in_out.py:94: UserWarning: Channel 18 is already in use, continuing anyway. Use                                                                          GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)


Dann muss ich den Code nochmal neustarten und die Meldungen sind weg.


Kann ich für:

Code: Alles auswählen

UserWarning: Channel "KANAL"  is already in use, continuing anyway.
eine Exeption anlegen , damit ich in diesen Fall die GPIO^s neu initialisiere?

bzw könnte ich den Code automatisch neustarten?



Grüsse
Benutzeravatar
__blackjack__
User
Beiträge: 14028
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@pumuckll: Was heisst wenn der Code nicht richtig beendet wird? Man sollte halt sicherstellen das `GPIO.cleanup()` aufgerufen wird, egal aus welchem Grund das Programm beendet wird. Das einzige was man so nicht erwischt sind harte Abstürze bei denen der Kernel das Programm beendet und wenn jemand das Programm mit einem SIGKILL beendet. Beim einen sollte man dringend die Ursache finden und beheben, und das andere sollte man einfach nicht machen bzw. nur wenn ein SIGTERM nicht macht was man erwartet. Der ``try``-Block in Deinem Code hat beispielsweise eine Lücke zwischen konfigurieren der GPIOs und dem Punkt ab dem dann sichergestellt wird das `cleanup()` aufgerufen wird. Da `cleanup()` in jedem Fall aufgerufen werden kann, sollte der ``try``-Block einfach komplett die `main()`-Funktion abdecken.

`cleanup()` am Anfang aufzurufen macht keinen Sinn. Das räumt die Pins auf die in dem aktuellen Prozess bis dahin konfiguriert wurden. Also am Anfang gar keine.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

$das Programm läuft als systemd dienst.

Wenn ich den dienst beende und manuell starte habe ich diesen Fehler.

Code: Alles auswählen

 root@keller:~# sudo service in_out start
root@keller:~# sudo service in_out stop
root@keller:~#
root@keller:~#
root@keller:~# python3 in_out.py
in_out.py:92: UserWarning: Channel 11 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 13 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 15 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:92: UserWarning: Channel 19 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.OUT, initial=initial)
in_out.py:94: UserWarning: Pull up/down setting are not (yet) fully supported, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
in_out.py:94: UserWarning: Channel 16 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
in_out.py:94: UserWarning: Channel 18 is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
  GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
Es wird nur eine Warnung ausgegeben , das Programm läuft weiter und Anscheinen Funktioniert es auch.

wenn ich das Service Start und Stoppe bekommen ich das nicht mit.


Ich hab das Cleanup am Anfang raus genommen, es ändert nichts also hat es auch nichts gebracht.

Code: Alles auswählen

def set_gpio():
    """Gpio definieren
       Ein und Ausgänge Einstellen
       GPIO.cleanup()am Anfang, falls die Gpio^s schon aktive sind"""
    try:
        GPIO.setmode(GPIO.BOARD)
        for pin, initial in zip(PIN_AUS[DEVICE], INITIAL_AUS[DEVICE]):
            GPIO.setup(pin, GPIO.OUT, initial=initial)
        for pin, initial in zip(PIN_EIN[DEVICE], INITIAL_EIN[DEVICE]):
            GPIO.setup(pin, GPIO.IN, pull_up_down=initial)
    except Exception:
        logger.exception("gpio set error")

Deswegen würde ich diese Warnungen zumindest loggen.
Benutzeravatar
__blackjack__
User
Beiträge: 14028
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Was macht denn das Stoppen? Bei Python-Programmen würde ich das mit einem SIGINT machen.

Die Ausnahmebehandlung in `set_gpio()` ist fehlerhaft denn bei jeglicher Ausnahme wird einfach so weitergemacht als hätte es keine Ausnahme gegeben.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
Sirius3
User
Beiträge: 18264
Registriert: Sonntag 21. Oktober 2012, 17:20

Das sind ja auch nur Warnungen, und da hilft kein Exception-Handling.
pumuckll
User
Beiträge: 56
Registriert: Donnerstag 30. August 2018, 17:45

Das ist das ystemd script:

Code: Alles auswählen

[Unit]
Description=in_out
After=syslog.target

[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/root/
ExecStartPre=/bin/sleep 10
#ExecStart=/usr/bin/python3 /root/in_out.py
ExecStart=/usr/bin/python3 /home/pi/in_out.py
SyslogIdentifier=in_out
StandardOutput=syslog
StandardError=syslog
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target
das beenden ist nicht definiert.

man kann es definieren:

Code: Alles auswählen

ExecStop=/usr/bin/flume-ng agent stop

auf die schnelle hab ich das gefunden:

Code: Alles auswählen

As described in systemd documentation, processes will receive SIGTERM and then, immediately, SIGHUP. After some time, SIGKILL will be send.

All signals, as well as strategy of sending them, can be changed in relevant service file.

See another stackoverflow question to see how handle these signals in your Python program.
https://www.freedesktop.org/software/sy ... .kill.html

Vermutlich sollte ich SIGTERM in meinen Code integrieren.

Werde mich damit und mit den exceptions am Wochenende beschäftigen.
Antworten