Wetterstation mit seriellem Interface RS232
-
- Beiträge: 253
- Registriert: 31. Jan 2022 06:35
Re: Wetterstation mit seriellem Interface RS232
hmmm jetzt steh ich aber auf einer Leitung. In welchem "Editor" schreib ich dann die Datenkonvertierung rein? Ich sehe momentan noch nicht wo ich das Programm reinschreibe sodass ich dann die erfassten Daten von der seriellen Schnittstelle weiterverarbeiten kann.
- udo1toni
- Beiträge: 15249
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: Wetterstation mit seriellem Interface RS232
Vielleicht ist das nicht so ganz klar geworden...
Wenn wir von einem Python Script reden, dann ist das ein externes Programm, welches nichts mit openHAB zu tun hat.
Wenn die Daten in Form eines JSON Objekts vorliegen, brauchst Du innerhalb openHAB nur ein passendes Addon, um das JSON Objekt lesen zu können (unter der Voraussetzung, dass Du bei MQTT bleibst wäre das das MQTT Addon). In der Oberfläche von openHAB stellst Du die Verbindung immer über Things her, sie sind die Entsprechung eines Hardware Geräts. Im Fall von MQTT reden wir vielleicht besser von logischen Einheiten. Das Script wäre eine solche logische Einheit, denn es stellt ja wiederum die Verbindung zur Wetterstation her.
Das Gerät stellt immer irgendwelche Daten zur Verfügung und/oder reagiert auf Steuerbefehle. Im Fall des Scripts ist es so, dass nur Daten herein kommen, und zwar in Form eines JSON Objekts.
Das Thing in openHAB hat deshalb nur einen Channel. Da es sich bei JSON um Text handelt, nutzen wir einen String Channel.
Das Objekt beinhaltet verschiedene voneinander unabhängige Werte. Diese Werte sollen schlussendlich in Items landen. Also benötigen wir für jedes Datum ein Item. Bei den Zahlenwerten benötigen wir Number Items, bei den logischen Werten jeweils ein Switch Item (Regen ON/OFF entspricht dann es regnet,/es regnet nicht).
Die Items werden alle mit dem String Channel verlinkt. Innerhalb des Links kann man ein Profile auswählen. Da es sich hier um Bestandteile eines JSON Objekts handelt, nutzen wir JSONPATH (ein weiteres Addon, welches installiert sein muss). Als Path geben wird dann z.B. $.Temperature an und erhalten das Datum, welches im Feld Temperature gespeichert ist. Für die anderen Items geschieht das entsprechend.
In openHAB wird für die ganze Aktion keine Zeile Code gebraucht.
Wenn es daran geht, Regeln zu erstellen, kann man diese entweder über die UI erstellen (Administration->Einstellungen->Rules->Add Rule(der blaue Kreis mit weißem plus rechts unten) und dann eben auswählen, wann die Rule gestartet werden soll, welche Voraussetzungen dafür erfüllt sein müssen und was dann passieren soll. Bei letzterem gibt es dann verschiedene Möglichkeiten, unter anderem, ein Script auszuführen. Wenn man diese Option wählt, landet man in einem Editor, in dem man den Code eingeben kann.
Alternativ kann man DSL Rules auch direkt im Dateisystem hinterlegen, dazu kann man jeden beliebigen Editor verwenden, zu bevorzugen ist aber VSCode, der aber bitte nicht auf dem Raspberry ausgeführt wird, sondern unter Windows, unabhängig von openHAB.
Wenn wir von einem Python Script reden, dann ist das ein externes Programm, welches nichts mit openHAB zu tun hat.
Wenn die Daten in Form eines JSON Objekts vorliegen, brauchst Du innerhalb openHAB nur ein passendes Addon, um das JSON Objekt lesen zu können (unter der Voraussetzung, dass Du bei MQTT bleibst wäre das das MQTT Addon). In der Oberfläche von openHAB stellst Du die Verbindung immer über Things her, sie sind die Entsprechung eines Hardware Geräts. Im Fall von MQTT reden wir vielleicht besser von logischen Einheiten. Das Script wäre eine solche logische Einheit, denn es stellt ja wiederum die Verbindung zur Wetterstation her.
Das Gerät stellt immer irgendwelche Daten zur Verfügung und/oder reagiert auf Steuerbefehle. Im Fall des Scripts ist es so, dass nur Daten herein kommen, und zwar in Form eines JSON Objekts.
Das Thing in openHAB hat deshalb nur einen Channel. Da es sich bei JSON um Text handelt, nutzen wir einen String Channel.
Das Objekt beinhaltet verschiedene voneinander unabhängige Werte. Diese Werte sollen schlussendlich in Items landen. Also benötigen wir für jedes Datum ein Item. Bei den Zahlenwerten benötigen wir Number Items, bei den logischen Werten jeweils ein Switch Item (Regen ON/OFF entspricht dann es regnet,/es regnet nicht).
Die Items werden alle mit dem String Channel verlinkt. Innerhalb des Links kann man ein Profile auswählen. Da es sich hier um Bestandteile eines JSON Objekts handelt, nutzen wir JSONPATH (ein weiteres Addon, welches installiert sein muss). Als Path geben wird dann z.B. $.Temperature an und erhalten das Datum, welches im Feld Temperature gespeichert ist. Für die anderen Items geschieht das entsprechend.
In openHAB wird für die ganze Aktion keine Zeile Code gebraucht.
Wenn es daran geht, Regeln zu erstellen, kann man diese entweder über die UI erstellen (Administration->Einstellungen->Rules->Add Rule(der blaue Kreis mit weißem plus rechts unten) und dann eben auswählen, wann die Rule gestartet werden soll, welche Voraussetzungen dafür erfüllt sein müssen und was dann passieren soll. Bei letzterem gibt es dann verschiedene Möglichkeiten, unter anderem, ein Script auszuführen. Wenn man diese Option wählt, landet man in einem Editor, in dem man den Code eingeben kann.
Alternativ kann man DSL Rules auch direkt im Dateisystem hinterlegen, dazu kann man jeden beliebigen Editor verwenden, zu bevorzugen ist aber VSCode, der aber bitte nicht auf dem Raspberry ausgeführt wird, sondern unter Windows, unabhängig von openHAB.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet
-
- Beiträge: 253
- Registriert: 31. Jan 2022 06:35
Re: Wetterstation mit seriellem Interface RS232
Vielen Dank für die gute Erklärung wozu ich folgende Frage habe. Die Daten welche über die serielle Schnittstelle kommen liegen bei mir nicht als JSON Objekt vor, sodass ich die Konvertierung mit einem Skript (Programm) machen muss.
Kann ich dann also unter "Rules" -> "Code" die Datenkonvertierung eingeben - richtig? In welcher Skriptsprache erfolgt dies?
Oder wo muss man es als Alternative im Dateisystem hinterlegen bzw. auch hier in welcher Sprache erfolgt dies?
Kann ich dann also unter "Rules" -> "Code" die Datenkonvertierung eingeben - richtig? In welcher Skriptsprache erfolgt dies?
Oder wo muss man es als Alternative im Dateisystem hinterlegen bzw. auch hier in welcher Sprache erfolgt dies?
- udo1toni
- Beiträge: 15249
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: Wetterstation mit seriellem Interface RS232
Nein. Hatte ich gleich als zweiten Satz geschrieben...
Genau. Du brauchst ein externes Script, welches die Daten ausliest und an den Broker schickt. Welche Sprache Du dafür verwendest, ist Deine Sache. Gewöhnlich wird man solche Programme unter /usr/local/bin/ ablegen.
Aus Deinen Fragen schließe ich allerdings, dass Du überhaupt nicht mit dem Erstellen von Programmen und Scripten unter GNU/Linux vertraut bist.
Das Problem dabei: die einzelnen Teile eines solchen (relativ kleinen) Programms sind einfach, aber man schreibt sowas halt nicht einfach so hin.
Du musst Dich für eine Sprache entscheiden. Meine Empfehlung wäre hier Python, weil es sehr gut im Netz dokumentiert ist und recht einfach zu verstehen ist. Aber vielleicht hast Du Erfahrung in einer anderen Sprache, dann wäre das vermutlich besser, sofern diese Sprache zur Verfügung steht.
Dann brauchst Du die notwendigen Module, um auf die serielle Schnittstelle und den MQTT Broker zugreifen zu können. Für mqtt wäre das paho-mqtt, für den Zugriff auf die serielle Schnittstelle wäre es vermutlich serial, was eventuell auch direkt mit Python installiert wird. Für Paho musst Du aber wohl pip verwenden. Je nachdem, wie ordentlich Du die Aufgabe erledigen willst, könntest Du noch eine Option einbauen, um die verschiedenen notwendigen Parameter über eine Konfigurationsdatei einstellen zu können. Dazu bräuchte es einen weiteren Import.
Es ist sinnvoll, das Programm so zu schreiben, dass es als Dienst laufen kann. Es wird dann automatisch nach Mosquitto gestartet und schreibt dann die Werte zyklisch in ein passendes Topic. Dazu musst Du natürlich eine bpassende service-Datei schreiben und dem System bekannt machen.
Das sind alles lösbare Aufgaben, aber das von null zu tun, ist ein großer Berg. Es käme also sehr darauf an, auf welchem Level Du startest...
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet
-
- Beiträge: 253
- Registriert: 31. Jan 2022 06:35
Re: Wetterstation mit seriellem Interface RS232
das stimmt dass ich eigentlich bei Null anfange und wenn du als Experte schon sagst dass dies ein grosser Berg ist dann na ja weiss ich nicht so recht. Mit Pyhton habe ich keine Probleme aber ich sehe eher Schwierigkeiten bei der Integration von allem inkl. dem Service der das Ganze dann zyklisch aufruft. Ebenfalls weiss ich nicht wie die Parameterübergabe aussieht oder in welchem Format die Daten übergeben werden.
Das sind alles Fragen die man wahrscheinlich ohne Tutorial nicht so einfach sind. Kennst du per Zufall so eine Doku welche meinen Fall ungefähr abdeckt? Ansonsten frag ich mich gerade ob Blockly nicht doch die einfachere Variante ist mal abgesehen von den Nachteilen die du erläutert hast.
Das sind alles Fragen die man wahrscheinlich ohne Tutorial nicht so einfach sind. Kennst du per Zufall so eine Doku welche meinen Fall ungefähr abdeckt? Ansonsten frag ich mich gerade ob Blockly nicht doch die einfachere Variante ist mal abgesehen von den Nachteilen die du erläutert hast.
- udo1toni
- Beiträge: 15249
- Registriert: 11. Apr 2018 18:05
- Wohnort: Darmstadt
Re: Wetterstation mit seriellem Interface RS232
Ich glaube nicht, dass Du die Aufbereitung der Daten in Blockly hinbekommen wirst, mal abgesehen davon, dass Du ja auch die Kommunikation mit der seriellen Schnittstelle brauchst.
Ich habe in der Zwischenzeit einige kleine Scripte bei mir laufen, die über mqtt mit openHAB sprechen. Allerdings geht es bei mir darum, auf Befehle von openHAB zu reagieren. Aber um mal zu zeigen, dass es zwar nicht selbsterklärend, aber auch keine Raketenwissenschaft ist...
Ich zäume hier das Pferd mal von hinten auf. Programm als Dienst starten: die meisten GNU/Linux Distros nutzen inzwischen systemd, so auch Raspberry Pi OS. Die Dienste werden über service-Dateien gesteuert.
Diese Datei definiert also einen Service, der mqttclient heißt. Dein Dienst könnte z.B. weather2mqtt heißen (nur so als Vorschlag)
Der Install-Teil definiert, unter welchen Umständen der Dienst gestartet werden soll. Wenn der Rechner startet, durchläuft er mehrere Phasen, wovon der Normalzustand (also wenn das System komplett läuft) das multi-user.target ist. Der Dienst wird also gestartet, wenn das System nach multi-user wechselt und wird gestoppt, wenn dieser Modus verlassen wird.
Der Service-Teil beschreibt die notwendigen Schritte. Unter ExecStart steht also, wie das Programm aufzurufen ist, die anderen Parameter sind eigentlich genauso selbsterklärend. Restart besagt, dass der Dienst im Normalbetrieb automatisch wieder gestartet wird, sobald er abstürzt.
Um so einen Dienst im System bekannt zu machen, muss man lediglich die Daemon-Liste neu laden, den Dienst starten und als Autostart einrichten:
Der Dienst kann immer gestartet oder gestoppt werden, auch wenn er disabled ist. Das enable bezieht sich einzig auf den Autostart.
Dieses Script dient dazu, remote zwei Dienste alternativ zu starten oder zu stoppen. Es ist deshalb etwas komplexer, weil ich ja systemd fernsteuern muss. Das ist übrigens auch der Grund, warum dieses Script als root gestartet wird. Man könnte versuchen, einen extra User anzulegen, aber da der User ohnehin Rechte auf Systemebene benötigt, kann er nicht komplett ausgesperrt werden (abgesehen davon bin ich ja auch nur Laie...)
Witzigerweise benötigst Du den on_message-Part nicht (das macht den Löwenanteil meines Codes aus...)
Aber Du kannst am Code die notwendigen Schritte sehen, um die Verbindung zum Broker aufzubauen, die Konfiguration über eine Konfigurationsdatei zu lesen und auf ankommende Nachrichten zu reagieren. Genauso kannst Du sehen, dass zum Senden nicht wirklich viel passieren muss. Letztlich wird nur eine Funktion client.publish() aufgerufen, in meinem Fall mit der Rückmeldung über den gestarteten Dienst, bei Dir kommt dann dort das gesendete JSON Objekt rein.
Fehlt noch die Konfigurationsdatei:
Damit das ganze funkitioniert, braucht das System ein paar Pakete:
und natürlich ein paar python Libraries:
Wobei Du das letzte Paket natürlich nicht brauchst, nur der Vollständigkeit halber...
Der wesentliche Teil für Dein Programm ist natürlich das zyklische Lesen der seriellen Schnittstelle und die Umsetzung in die zu sendende Payload.
Als Einstieg mag dieser Link diesen https://stackoverflow.com/questions/676 ... al-package. Ich bin mir nicht sicher, ob serial ohne weiteres Zutun bei Python3 mit kommt, aber das "schlimmste" was passieren kann, ist, dass ein Programm, welches den Import nutzt, mit einer Fehlermeldung abbricht, weil die Bibliothek fehlt.
Hier noch ein Beispiel einer serial<->mqtt Bridge: https://github.com/perrin7/ninjacape-mq ... TBridge.py, das kommt Deinen Bedürfnissen schon recht nahe, Du musst also den Code "nur" verstehen und anschließend so abändern, dass a) die Kommunikation nur in einer Richtung läuft (bedeutet Du musst nur Code weg lassen...) und b) der Teil, welcher aus der empfangenen Datenzeile die Daten aufbereitet wird entsprechend angepasst ist.
Ich habe in der Zwischenzeit einige kleine Scripte bei mir laufen, die über mqtt mit openHAB sprechen. Allerdings geht es bei mir darum, auf Befehle von openHAB zu reagieren. Aber um mal zu zeigen, dass es zwar nicht selbsterklärend, aber auch keine Raketenwissenschaft ist...
Ich zäume hier das Pferd mal von hinten auf. Programm als Dienst starten: die meisten GNU/Linux Distros nutzen inzwischen systemd, so auch Raspberry Pi OS. Die Dienste werden über service-Dateien gesteuert.
Code: Alles auswählen
udo1toni@calibre:~$ cat /etc/systemd/system/mqttclient.service
[Install]
WantedBy=multi-user.target
Type=simple
[Service]
ExecStart=/usr/bin/python3 /usr/local/sbin/mqttclient.py
User=root
Restart=always
RestartSec=10
udo1toni@calibre:~$
Der Install-Teil definiert, unter welchen Umständen der Dienst gestartet werden soll. Wenn der Rechner startet, durchläuft er mehrere Phasen, wovon der Normalzustand (also wenn das System komplett läuft) das multi-user.target ist. Der Dienst wird also gestartet, wenn das System nach multi-user wechselt und wird gestoppt, wenn dieser Modus verlassen wird.
Der Service-Teil beschreibt die notwendigen Schritte. Unter ExecStart steht also, wie das Programm aufzurufen ist, die anderen Parameter sind eigentlich genauso selbsterklärend. Restart besagt, dass der Dienst im Normalbetrieb automatisch wieder gestartet wird, sobald er abstürzt.
Um so einen Dienst im System bekannt zu machen, muss man lediglich die Daemon-Liste neu laden, den Dienst starten und als Autostart einrichten:
Code: Alles auswählen
sudo systemctl daemon-reload
sudo systemctl enable mqttclient.service
sudo systemctl start mqttclient.service
Dieses Script dient dazu, remote zwei Dienste alternativ zu starten oder zu stoppen. Es ist deshalb etwas komplexer, weil ich ja systemd fernsteuern muss. Das ist übrigens auch der Grund, warum dieses Script als root gestartet wird. Man könnte versuchen, einen extra User anzulegen, aber da der User ohnehin Rechte auf Systemebene benötigt, kann er nicht komplett ausgesperrt werden (abgesehen davon bin ich ja auch nur Laie...)
Code: Alles auswählen
udo1toni@calibre:~$ cat /usr/local/sbin/mqttclient.py
#!/usr/bin/python
# define imports
import configparser
import logging
from paho.mqtt import client as mqtt
import time
from sysdmanager import SystemdManager
# define config reader
config = configparser.ConfigParser()
# read config from /etc/default/mqttclient
config.read('/etc/default/mqttclient')
# set config and default value if not set
# for logger
log_file = (config['logging']).get('filename','/var/log/pymqtt.log')
log_level = int((config['logging']).get('level','30'))
logging.basicConfig(filename=log_file,format='%(asctime)s %(levelname)s:%(message)s', datefmt='%Y/%m/%d/ %H:%M:%S', level=log_level)
# for mqtt connection
MQTT_SERVER = (config['mqtt']).get('url','localhost')
MQTT_PORT = int((config['mqtt']).get('port','1883'))
MQTT_RECONNECT = int((config['mqtt']).get('reconnect','60'))
MQTT_PATH = (config['mqtt']).get('path','/')
MQTT_COMMAND = (config['mqtt']).get('command','cmnd')
MQTT_STATE = (config['mqtt']).get('state','state')
MY_COMMAND = MQTT_PATH+MQTT_COMMAND
MY_STATE = MQTT_PATH+MQTT_STATE
# define functions
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
client.publish(MQTT_PATH+"LWT", payload="Online", qos=0, retain=True)
logging.info("Connected with result code "+str(rc))
if rc: logging.warning("Connecting resulted with" +str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe(MY_COMMAND)
logging.info("Subscribed to "+str(MY_COMMAND))
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
manager = SystemdManager()
logging.debug("got message!")
payload=format(msg.payload.decode("utf-8"))
logging.info(msg.topic+" : "+payload)
startunit=""
stopunit1=""
stopunit2=""
if payload == "1":
logging.debug("Payload is 1 -> CS")
stopunit1="cps.service"
startunit="calibre-server.service"
elif payload == "2":
logging.debug("Payload is 2 -> CPS")
startunit="cps.service"
stopunit1="calibre-server.service"
elif payload == "0":
logging.debug("Payload is 0 -> OFF")
stopunit2="cps.service"
stopunit1="calibre-server.service"
else:
logging.info("Payload is "+payload+" -> only send state")
if stopunit1 != "":
logging.debug("stop service "+stopunit1+"!")
manager.stop_unit(stopunit1)
if stopunit2 != "":
logging.debug("stop service "+stopunit2+"!")
manager.stop_unit(stopunit2)
client.publish(MY_STATE, payload="0", qos=0, retain=False)
logging.debug("published state 0 to "+MY_STATE)
if startunit != "":
logging.debug("wait until starting service!")
time.sleep(10)
logging.debug("starting service "+startunit+"!")
manager.start_unit(startunit)
logging.debug("publishing state")
if manager.is_active("cps.service"):
cpsstate="ON"
client.publish(MY_STATE, payload="2", qos=0, retain=False)
logging.debug("published state 2 to "+MY_STATE)
else:
cpsstate="OFF"
if manager.is_active("calibre-server.service"):
csstate="ON"
client.publish(MY_STATE, payload="1", qos=0, retain=False)
logging.debug("published state 1 to "+MY_STATE)
else:
csstate="OFF"
client.publish(MQTT_PATH+"cs/state", payload=csstate, qos=0, retain=False)
client.publish(MQTT_PATH+"cps/state", payload=cpsstate, qos=0, retain=False)
logging.debug("published state "+csstate+" to "+MQTT_PATH+"cs/state")
logging.debug("published state "+cpsstate+" to "+MQTT_PATH+"cps/state")
# main program
# set a new client and create hooks to functions
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.will_set(MQTT_PATH+"LWT", payload="Offline", qos=0, retain=True)
# connect
client.connect(MQTT_SERVER, MQTT_PORT, MQTT_RECONNECT)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
client.loop_forever()
udo1toni@calibre:~$
Aber Du kannst am Code die notwendigen Schritte sehen, um die Verbindung zum Broker aufzubauen, die Konfiguration über eine Konfigurationsdatei zu lesen und auf ankommende Nachrichten zu reagieren. Genauso kannst Du sehen, dass zum Senden nicht wirklich viel passieren muss. Letztlich wird nur eine Funktion client.publish() aufgerufen, in meinem Fall mit der Rückmeldung über den gestarteten Dienst, bei Dir kommt dann dort das gesendete JSON Objekt rein.
Fehlt noch die Konfigurationsdatei:
Code: Alles auswählen
udo1toni@calibre:~$ cat /etc/default/mqttclient
[mqtt]
url = 192.168.178.55
port = 1883
reconnect = 60
path = calibre/
command = cmnd
state = state
lwt = LWT
[logging]
filename = /var/log/pymqtt.log
# CRITICAL=50 ERROR=40 WARNING=30 INFO=20 DEBUG=10 NOTSET=0
level = 30
udo1toni@calibre:~$
Code: Alles auswählen
sudo apt install python3 pip
Code: Alles auswählen
sudo pip install configparser paho-mqtt sysdmanager
Der wesentliche Teil für Dein Programm ist natürlich das zyklische Lesen der seriellen Schnittstelle und die Umsetzung in die zu sendende Payload.
Als Einstieg mag dieser Link diesen https://stackoverflow.com/questions/676 ... al-package. Ich bin mir nicht sicher, ob serial ohne weiteres Zutun bei Python3 mit kommt, aber das "schlimmste" was passieren kann, ist, dass ein Programm, welches den Import nutzt, mit einer Fehlermeldung abbricht, weil die Bibliothek fehlt.
Hier noch ein Beispiel einer serial<->mqtt Bridge: https://github.com/perrin7/ninjacape-mq ... TBridge.py, das kommt Deinen Bedürfnissen schon recht nahe, Du musst also den Code "nur" verstehen und anschließend so abändern, dass a) die Kommunikation nur in einer Richtung läuft (bedeutet Du musst nur Code weg lassen...) und b) der Teil, welcher aus der empfangenen Datenzeile die Daten aufbereitet wird entsprechend angepasst ist.
openHAB4.3.3 stable in einem Debian-Container (bookworm) (Proxmox 8.3.5, LXC), mit openHABian eingerichtet