Raspi zero w mit Python und MQTT -> Versionskollision?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Visionflier
User
Beiträge: 2
Registriert: Montag 1. Juli 2024, 19:19

Hallo zusammen!

Ich habe eine Spielstandanzeige mit MQTT in Python programmiert.
Ein gekauftes HF-Modul mit dem RFM69CW erfordert die Programmierung mit Python. Desweiteren die SPI sowie GPIOs.
All das hat schon mal funktioniert und lief lange Zeit problemlos.
Bis eines Tages folgender Fehler auftritt:

pi@rpi-zero-w:~/Spielstandsanzeige $ python3 SA_Anzeige_0.4.3.py
/home/pi/Spielstandsanzeige/SA_Anzeige_0.4.3.py:10: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
gpio.setup(LAT,gpio.OUT) # setup gpio no. 18 (GPIO5) to output
/home/pi/Spielstandsanzeige/SA_Anzeige_0.4.3.py:114: DeprecationWarning: Callback API version 1 is deprecated, update to latest version
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
home/rcpulse/fs20/61439/1 b'0'
Traceback (most recent call last):
File "/home/pi/Spielstandsanzeige/SA_Anzeige_0.4.3.py", line 118, in <module>
client.loop_forever()
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 2297, in loop_forever
rc = self._loop(timeout)
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 1686, in _loop
rc = self.loop_read()
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 2100, in loop_read
rc = self._packet_read()
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 3142, in _packet_read
rc = self._packet_handle()
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 3808, in _packet_handle
return self._handle_publish()
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 4145, in _handle_publish
self._handle_on_message(message)
File "/home/pi/.local/lib/python3.9/site-packages/paho/mqtt/client.py", line 4501, in _handle_on_message
on_message(self, self._userdata, message)
File "/home/pi/Spielstandsanzeige/SA_Anzeige_0.4.3.py", line 59, in on_message
hauscode=[int(temp) for temp in re.findall(r'-?\d+\.?\d*',msg.topic)]
NameError: name 're' is not defined


Damit bricht das Programm ab.
Ich habe schon viel Zeit mit der Fehlersuche und im Internet verbracht. Bisher erfolglos.

Hat jemand eine Idee?
Besten Dank im Voraus!

Mit freundlichem Gruß
Visionflier

P.S.: Ich bin leider in Python nicht wirklich bewandert. Ich war lange Zeit Hardwareentwickler.


Meine Systemvoraussetzungen:

- Raspi zero w mit Funkhardwaremodul 866MHz
- Softwareausschnitt:

</>#!/usr/bin/env python
import paho.mqtt.client as mqtt
import RPi.GPIO as gpio
import spidev
import time, os
.
.
.
def on_message(client, userdata, msg):
print(msg.topic," ",str(msg.payload))
hauscode=[int(temp) for temp in re.findall(r'-?\d+\.?\d*',msg.topic)]
global near # reset score value of "near"
global far # reset score value of "far"
global starttime, endtime
pos_hauscode=msg.topic.find('61439')
hauscode=msg.topic[pos_hauscode:pos_hauscode+5]
hauscode_wert=int(hauscode)

pos_adresse=msg.topic.find('/1')
adresse=msg.topic[pos_adresse+1:pos_adresse+4]
adresse_wert=int(adresse)
switch= int(msg.payload)
print(hauscode_wert,adresse_wert,int(msg.payload))
if hauscode_wert == 61439:
.
.
.
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
client.on_message = on_message
client.connect("localhost", 1883)
client.subscribe("home/rcpulse/#", 0)
client.loop_forever()
</>
Benutzeravatar
Dennis89
User
Beiträge: 1516
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

dir fehlt der Import von `re`.
Also ganz oben unter die anderen Importe ` import re `

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Visionflier
User
Beiträge: 2
Registriert: Montag 1. Juli 2024, 19:19

Hallo Dennis,

besten Dank für die schnelle Antwort. Etwas Hoffnung keimte in mir hoch, doch leider zeigte sich keine Änderung.
Die Fehlermeldungen bleiben die Gleichen.

Mit freundlichem Gruß
Visionflier
Benutzeravatar
__blackjack__
User
Beiträge: 13997
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Visionflier: Das kann nicht sein. Wenn man `re` importiert, ist das auch im Modul verfügbar, solange man nicht aus unverständlichen Gründen zwischen dem Import und dem Callback auf Modulebene den Namen wieder löscht.

Die Zeile in der der Fehler auftritt scheint da auch gar nicht wirklich hin zu gehören, denn das Ergebnis wird nirgends verwendet. Der Name wird ein paar Zeilen später an einen anderen Wert gebunden *der* dann auch tatsächlich weiterverwendet wird.

Der reguläre Ausdruck enthält einen Dezimalpunkt, trifft also auch auf Zeichenketten zu, die sich im nächsten Schritt gar nicht in eine ganze Zahl umwandeln lassen und zu einer Ausnahme führen.

Um die beiden Warnungen sollte man sich kümmern. Bei der von `GPIO` aber nicht wie in der Warnung vorgeschlagen, also nicht einfach Warnungen abschalten, sondern der Ursache nachgehen und *die* beseitigen. Üblicherweise ist das ein fehlendes aufräumen am Programmende und egal wie das Programm endete. Also ein ``try``/``finally`` einbauen wo im ``finally``-Zweig ein `GPIO.cleanup()` steht, ist die übliche Vorgehensweise.

Und die MQTT-API-Version sollte man wechseln solange man das noch in Ruhe machen kann, und nicht warten bis man das unter Zeitdruck machen muss weil der Code nach einem Update gar nicht mehr funktioniert.

``global`` hat in einem ordentlichen Programm nichts zu suchen. Wenn man sich Zustand über aufrufe hinweg merken muss, dann verwendet man dafür Objekte, also mindestens eine Datenklasse die man bei MQTT-Bibliothek ja als `userdata` in die Funktionen übergeben lassen kann.

Der Code des Hauptprogramms sollte nicht auf Modulebene stehen, sondern in einer Funktion verschwinden.

Die `find()`-Methode sollte man nicht verwenden, sondern `index()`. `find()` liefert eine -1 wenn die Zeichenkette nicht gefunden wird, die problemlos in den folgenden Zeilen als Index beim „slicing“ funktioniert, aber entweder zu einem falschen Ergebnis führt, oder aber zu etwas was “richtig“ ist, aber *sehr* undurchsichtig für den Leser ist. `index()` führt zu einer Ausnahme und macht nicht einfach mit falschen Werten weiter als wäre nichts passiert.

Dieser Code hier ist komisch:

Code: Alles auswählen

    pos_hauscode = message.topic.find("61439")
    hauscode = message.topic[pos_hauscode : pos_hauscode + 5]
    hauscode_wert = int(hauscode)
Warum wandelt man etwas in eine ganze Zahl um von dem man *vorher* schon den Wert kenn‽ Und das ist auch so ein Fall von einem Folgefehler von `find()`, wenn da dann letztlich ``message.topic[-1:4]`` mit `int()` umgewandelt wird.

Code: Alles auswählen

    if "61439" not in message.topic:
        raise ValueError("61439 not in topic")
    
    hauscode_wert = 61439
Die 61439 ist auch ein sehr magischer Wert, der nicht mehrfach im Code stehen, sondern als Konstante mit einem sprechendem Namen definiert werden sollte.

Namen sollten keine kryptischen Abkürzungen enthalten (oder gar nur daraus bestehen). Wenn es wichtig ist, das die Adresse ein positiver Wert ist, sollte man das auch so ausdrücken. Am Ende denkt noch jemand das könnte für etwas anderes stehen. 😜

Es ist ein bisschen komisch Werte mit `_wert` zu kennzeichnen. Namen stehen allgemein für Werte, das muss man nicht überall dran schreiben.

Das ``if hauscode_wert == 61439:`` ist überflüssig, denn `hauscode_wert` *hat* diesen Wert wenn der Code bis dort hin gekommen ist. Sonst wäre das vorher schon mit einer Ausnahme abgebrochen worden.

`print()` wandelt die auszugebenden Werte selbst schon in Zeichenketten um vor der Ausgabe.

Die `on_message()`-Funktion würde also eher so aussehen:

Code: Alles auswählen

HAUSCODE = 61439

...

def on_message(_client, _userdata, message):
    assert message.topic.startswith("home/rcpulse/")
    print(message.topic, " ", message.payload)
    if str(HAUSCODE) not in message.topic:
        raise ValueError(f"{HAUSCODE} not in topic")
    
    adressindex = message.topic.index("/1")
    adresse = int(message.topic[adressindex + 1 : adressindex + 4])
    switch = int(message.payload)
    print(HAUSCODE, adresse, switch)

    ...
Grundsätzlich sieht das nicht so ganz robust aus und als Leser weiss man auch nicht so wirklich wie die Topics aussehen (sollten). Der Code liefe ja beispielsweise auch mit einem Topic "home/rcpulse/spam/1-614390/parrot" problemlos durch.

Die `print()`-Aufrufe könnte man mal durch Logging ersetzen. Dann lassen sich auch verschiedene Stufen trennen und ausschalten wenn man nicht immer alles voller Debugging-Ausgaben haben will.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Antworten