Wago /SPS über Modbus(TCP/IP) in FHEM steuern

Begonnen von lechez, 05 Mai 2013, 10:50:13

Vorheriges Thema - Nächstes Thema

ChrisD

Hallo,

Das Schreiben funktioniert nicht weil ich einen falschen Funktionsnamen verwendet habe, die Funktion zum Schreiben heißt 'write_modbus' und nicht 'write_modbus_zaehler'. So sollte es funktionieren:
define at_write_Temperatur_Ist_mw3 at +*00:00:05 {write_modbus(12291,ReadingsVal("MAX_07e1ea","temperature",0)*10)}
Bei der Funktion 'ReadingsVal' muss übrigens ein 3. Parameter (Default-Wert wenn Reading nicht existiert) mit angegeben werden.

Den Batteriestatus könntest du so übertragen:
define act_Dummy4_4 notify MAX_0850c2:battery.* {write_modbus_coil(12356,$EVTPART1 eq "ok"?1:0)}
Wenn die Batterie ok ist wird eine 1 übertragen, wenn nicht eine 0.

Grüße,

ChrisD


oniT

Hallo Chris,

mich würde einmal den Ausgang des Tests interessieren. Läuft es mit dem Modul? Würdest Du dieses mir auch zu Testzwecken zur Verfügung stellen?

Zitat von: ChrisD am 04 Januar 2014, 13:58:13

@Tino: Solange es ohne Probleme funktioniert würde ich es nicht ändern. Wenn du die 20 Werte in einer Funktion ausliest und ein Aufruf z.B. 100ms dauert, ist FHEM 2s 'blockiert'. Dies reicht nicht für den Disconnect des HMLAN. Wenn du das Dummy verwendest wird in den Internals des Dummys eine Zeile mit 'packets' angezeigt in der Zeiten stehen.  Wenn alle Werte auf 0 stehen musst du ein 'reload 99_modbus' machen, den Grund dafür habe ich noch nicht gefunden.

Es ist bei ModbusTCP möglich (und laut Spezifikation sogar erwünscht) die Verbindung zu öffnen und bis zum Ende des Clients (FHEM) offen zu lassen. Weiterhin ist es auch erlaubt mehrere Anfragen parallel abzuschicken, ob dies allerdings von den unterschiedlichen SPSen korrekt unterstützt ist weiß ich nicht. Das aktuelle Modul müsste dazu umgebaut werden.

Ich habe das Ganze zu Testzwecken mal als Modul umgebaut das über define deklariert wird. Dies hat verschiedene Vorteile (nur ein Verbindungsaufbau, automatisches Pollen, FHEM wird nicht blockiert bei Timeouts, ...) muss allerdings nach ausgiebig getestet werden.


Danke
Gruß,
Tino
BBB - debian weezy - FHEM 5.7
HMLAN - HM-LC-Bl1-FM, HM-ES-PMSw1-PI, HM-LC-Sw1-FM, HM-TC-IT-WM-W-EU, HM-WDS40-TH-I, HM-Sen-Wa-Od, HM-Sec-RHS
Dimplex Wärmepumpe / Dimplex ZL 300 - Modbus TCP
SDM630M - Modbus TCP
SolarLog 200 / SMA SonnyBoy 1.5/2.5 - Modbus TCP

Hollo

#122
Ich hänge mich mal hier dran, um nicht gleich einen neuen Thread zu starten.
Natürlich auch auf die Gefahr hin, als Dummuser gleich gesteinigt zu werden...

Ich habe seit Kurzem FHEM als "gemeinsame Oberfläche" für verschiedene "Einzelsachen" installiert; dafür ist es meines Erachtens auch gedacht.

Jetzt möchte ich gerne einen Wago 750-342 (Ethernet-Koppler, Modbus, keine programmierbare SPS) einbinden.
Dafür benötige ich doch auch die Modbus-Einbindung... wer kann mir da weiterhelfen?
Ziel soll es sein, dass ich digitale und analoge I/Os bidirectional setzen, lesen, auswerten kann; und diese dann im FHEM entsprechend verknüpfen kann.

Gruß,
Hollo
FHEM 6.x auf RPi 3B Buster
Protokolle: Homematic, Z-Wave, MQTT, Modbus
Temp/Feuchte: JeeLink-Clone und LGW mit LaCrosse/IT
sonstiges: Linux-Server, Dreambox, "RSS-Tablet"

ChrisD

#123
Hallo,

@Tino: Ich habe das Modul (in Wirklichkeit 2) seit längerem mit dem was ich benötige am Laufen. Es fehlen allerdings noch einige Sachen (Dokumentation, Coils, Leseaufträge zusammenfassen). Anbei mein aktueller Stand. Zum Testen sollte dies mit Wago-SPSen funktionieren:
define MB_Test ModbusTCPServer 192.168.78.250
attr MB_Test pollIntervall 0.1
define MW0 ModbusRegister 0 12288

Bei der Definition des Registers muss zuerst die UnitID (bei Wago immer 0) und dann die Adresse (hier 12288 für MW0) angegeben werden.

Eine kurze Erklärung der Attribute:

IODev - legt fest welcher ModbusTCPServer verwendet wird, wenn nur einer definiert ist, wird er automatisch gesetzt, wenn es bereits mehrere gibt muss überprüft werden ob der richtige zugewiesen wurde

plcDataType - gibt den Datentyp auf der SPS an und konvertiert die gelesenen Daten automatisch (getestet mit diversen Codesys Steuerungen, zumindest INT sollte aber mit allen Steuerungen funktionieren)

updateIntervall - wie häufig der Wert gelesen wird, Angabe in s, wenn das Attribut nicht existiert wird 100ms (=0.1) verwendet

conversion - Umrechnungsfaktoren für den gelesenen (und von plcDataType angepassten) Wert, Format: a:b, mit state = a * gelesener Wert + b, z.B. 0.1:0 für die Umrechnung von Fixpunktwerten (SPS) in Fliesskomazahlen (FHEM)

@Hollo: Du kannst den Koppler mit den Funktionen aus 99_Modbus.pm verwenden, die aktuelle Version hängt an Beitrag 99. Die Anbindung an FHEM musst du dir aber damit selbst zusammenbauen da es keine fertigen Module gibt. Die beigefügten Test-Module könntest du verwenden um analoge I/Os zu lesen und schreiben.

Grüße,

ChrisD

Edit: Die aktuellen Versionen der Module sind unter https://github.com/ChrisD70/FHEM-Modules zu finden.

qwikser

Hi Chris,

danke nochmals für deine bisherigen Antworten!! :) Habe nun mein gesamtes MAX! Heizungssystem über fhem an meine Wago SPS weitergeleitet, funktioniert tadellos.
Habe aber noch eine kurze Frage.

Das hier ist die Übertragung der Batterie Meldung an die SPS, mit auslesen des Wertes.

define stellmotor_battery_buero dummy
attr stellmotor_battery_buero group Büro
attr stellmotor_battery_buero room Wago
attr stellmotor_battery_buero setList On Off
define act_stellmotor_battery_buero notify MAX_0850c2:battery.* {write_modbus_coil(12768,$EVTPART1 eq "ok"?1:0)}
define at_read_stellmotor_battery_buero at +*00:00:30 {fhem("set stellmotor_battery_buero ".((read_modbus_zaehler(12318) & 1)?"on":"off"))}

..das funktioniert auch ohne Probleme.

Was ich nun wissen wollte, gibt es auch den Befehl "read_modbus_coil" ???
Und wäre das hier dann richtig?

define at_read_stellmotor_battery_buero at +*00:00:30 {fhem("set stellmotor_battery_buero ".((read_modbus_coil(12768))?"on":"off"))}


Vielen Dank nochmal!  ;D
MfG.
Daniel

ChrisD

Hallo,

Es gibt in 99_modbus die Funktion read_modbus_coil. Diese wandelt von sich aus bereits 0 in off und 1 in on um, das Lesen sollte also so funktionieren:

define at_read_stellmotor_battery_buero at +*00:00:30 {fhem("set stellmotor_battery_buero ".read_modbus_coil(12768))}

In der Funktion ist aber noch ein Log-Aufruf enthalten der dazu führt dass bei jedem Aufruf von read_modbus_coil eine Zeile in die FHEM-Logdatei geschrieben wird. Um dies abzuschalten muss in der Datei 99_modbus.pm die Zeile 164 mit
    Log 0,"Modbus read from coil $reg, value @$coils[0]";
entfernt werden.

Grüße,

ChrisD


qwikser

Hi,

das funktioniert ja wunderbar mit den Coils. :) Wenn ich nun dem Status z.B. ein Offen/Geschlossen mitgeben möchte anstatt dem On/Off. Dachte ich mir das das so funktionieren würde, leider steht im Status immer nur "Offen".

define at_read_fenster_aufzu_schlafzimmer at +*00:00:30 {fhem("set fenster_aufzu_schlafzimmer ".((read_modbus_coil(12955))?"Offen":"Geschlossen"))}

Meine Idee war es, es aus dieser Zeile abzuleiten.

define at_read_fenster_battery_schlafzimmer at +*00:00:30 {fhem("set fenster_battery_schlafzimmer ".((read_modbus_zaehler(12329) & 4096)?"Batterie_OK":"Batterie_Leer"))}


MfG.
Daniel

ChrisD

Hallo,

Da read_modbus_coil 'on' und 'off' zurückgibt funktioniert die Abfrage so leider nicht. Folgendes sollte aber gehen:
define at_read_fenster_aufzu_schlafzimmer at +*00:00:30 {fhem("set fenster_aufzu_schlafzimmer ".((read_modbus_coil(12955) eq "on")?"Offen":"Geschlossen"))}

Ich könnte read_modbus_coil so erweitern dass beliebige Werte für 0 und 1 zurückgegeben werden, der Aufruf könnte dann z.B. so aussehen:

define at_read_fenster_aufzu_schlafzimmer at +*00:00:30 {fhem("set fenster_aufzu_schlafzimmer ".read_modbus_coil(12955,"Geschlossen","Offen"))}

Grüße,

ChrisD

oniT

Hallo Chris,

ah ok. Danke für die Antwort. Ich hätte gehofft das Du schon dazu gekommen bist es so umzubauen, dass man bei einer Anfrage nur einmal die Verbindung öffnen muss. Ist dann wohl noch nicht der Fall.

Bitte melde Dich doch hierzu wenn Du irgendwann dies so umsetzen konntest.

Danke und Grüße,
Tino
BBB - debian weezy - FHEM 5.7
HMLAN - HM-LC-Bl1-FM, HM-ES-PMSw1-PI, HM-LC-Sw1-FM, HM-TC-IT-WM-W-EU, HM-WDS40-TH-I, HM-Sen-Wa-Od, HM-Sec-RHS
Dimplex Wärmepumpe / Dimplex ZL 300 - Modbus TCP
SDM630M - Modbus TCP
SolarLog 200 / SMA SonnyBoy 1.5/2.5 - Modbus TCP

ChrisD

Hallo,

Die Verbindung zum Server wird nur einmalig aufgebaut und dann offengehalten. Alle Lese- und Schreibaufträge werden dann über die offene Verbindung gesendet. Im Gegensatz zu 99_modbus.pm wird also nicht mehr bei jedem Lese-/Schreibauftrag die Verbindung geöffnet und geschlossen. Weiterhin wird auch nicht mehr gewartet bis die Antwort des Servers eintrifft (und FHEM damit blockiert). Sobald ein Antwort eintrifft wird dies von FHEM über DevIO dem Modul signalisiert und die empfangenen Daten werden verarbeitet. Was im Moment nicht implementiert ist ist das Zusammenfassen von mehrere Aufträgen, wenn du z.B. MW0 und MW1 lesen möchtest werden 2 Anfragen geschickt statt nur einer. Dies wäre besonders für Coils interessant, ich bin aber noch nicht dazu gekommen dies umzusetzen. Ich weiß auch nicht ob es überhaupt Bedarf an Coils und einer solchen Optimierung gibt.

Das Lesen von Daten mit den neuen Modulen erfolgt automatisch, zusätzliche 'at's o.ä. Konstrukte sind nicht mehr nötig. Die 3 Zeilen aus Beitrag vom 18.03. reichen aus. Zum Schreiben reicht einset MW0 1234

Grüße,

ChrisD


fischle

Hallo Chris,
wie viel Aufwand ist es denn, die Funktion so zu erweitern, dass ich damit auch über die serielle Schnittstelle (RS485) Geräte auslesen kann? Ich habe hier einen Stromzähler, der sich über Modbus (RS485) auslesen lässt. Den würde ich sehr gerne mit Hilfe deines Moduls auslesen. Meinst du, du kannst soetwas noch ergänzen oder mir ggf. gute Tipps geben, welche Funktionen ich bearbeiten muss?

Gruß

Fabian
RPi,
- USB RS485 Adapter für Stromzähler DRS155M und SDM630M-DC (B+G E-Tech)
- Viesmann KO2B Heizung mit selbstbau Optolink Adapter
- Mi-Light WiFi-Bridge V4, WW/CW LED-Birne

golem

Hallo Chris,


so einen Stromzähler habe ich auch noch.  Interesse also auch von mir.
Tcp und seriell sollten sich ja mit dem IODev lecht realisieren lassen.
Gruß Denis
Pi - Max-Lan - 8x max Ht -3x Max WT - Max Fk -modbus umg103- 2x Arduino mit Firmata Ethernet- ws300 - 433Mhz Sender Empfänger - 7x 1wire ds1820

ChrisD

#132
Hallo,

Ich hatte ursprünglich die Funktionen auf 2 Module aufgeteilt um so auch Modbus RTU unterstützen zu können. Ich habe mir jetzt die Spezifikation zu Modbus RTU nochmal genauer angesehen und versucht sie im beiliegenden Modul umzusetzen. Das Modul habe ich allerdings lediglich mit einem Simulator testen können da ich im Moment keine Hardware mit Modbus RTU habe. Im Modul fehlen auch noch verschiedene Sachen um die Spezifikation einzuhalten:
- die Parität wird nicht überprüft
- das Timing wird nicht überwacht, hierbei müssen Zeiten im 1-stelligen ms-Bereich eingehalten werden was mit FHEM nicht ganz einfach ist
- Diagnosefunktionen

Zusätzlich zu diesem Modul wird noch das Modul 37_ModbusRegister.pm aus Beitrag 123 benötigt.

Die Definition sieht so aus:
define MB_RTU ModbusRTU /dev/ttyACM0
attr MB_RTU pollIntervall 0.1


Optional kann die Geschwindigkeit mit angegeben werden:
define MB_RTU ModbusRTU /dev/ttyACM0@19200

Die Schnittstelle wird mit 8E1 geöffnet, falls die Slaves andere Parameter benötigen können diese über ein Attribut gesetzt werden:
attr MB_RTU charformat 8N2
Wenn an einem Bus mehrere Slaves hängen müssen diese mit der gleichen Geschwindigkeit und den gleichen Parametern betrieben werden.

Anschließend können die Register definiert werden (z.B. für Slave 1):
define Reg0 ModbusRegister 1 0
define Reg20 ModbusRegister 1 20
define Reg25 ModbusRegister 1 25


Das IODev wird dabei automatisch zugewiesen. Wenn mehrere ModbusRTU oder ModbusTCP-Geräte definiert sind muss aber überprüft werden ob die Zuordnung passt.

Wichtig: Im Gegensatz zum ModbusTCP-Modul ist dieses Modul nur kurz getestet worden (und das auch nur unter Windows).

Grüße,

ChrisD

Edit 05.05.2014, Anhang gelöscht, aktuelle Version in folgenden Beiträgen

fischle

Hallo Chris,
ich habe mich nun mal daran gemacht, das Modul zu testen, auch erst mal unter Windows .

Hier meine Config define MB_RTU ModbusRTU com9@9600
attr MB_RTU pollIntervall 20
attr MB_RTU charformat 8N1

define Reg0 ModbusRegister 1 30001


Was ich nun noch nicht gefunden habe ist, wie ich andere Registertypen auslesen kann? Ich würde gerne die Adresse 30001 (Adresse 0 im Input register bereich lesen). Was muss ich machen um den Funktionscode von 03 auf 04 zu ändern. Ist der fest hinterlegt oder kann der per define geändert werden?

Vielen Dank für die schnelle Reaktion.

Fabian
RPi,
- USB RS485 Adapter für Stromzähler DRS155M und SDM630M-DC (B+G E-Tech)
- Viesmann KO2B Heizung mit selbstbau Optolink Adapter
- Mi-Light WiFi-Bridge V4, WW/CW LED-Birne

ChrisD

#134
Hallo,

Das Modul aus Beitrag 123 kann nur FC 3 verwenden und macht auch keinen Unterschied zwischen Input und Holding da es ursprünglich für Wago-SPSen entwickelt wurde. Ich habe das Modul jetzt so erweitert dass es folgendes Mapping von Registern zu Adressen und Bereichen vornimmt:

Register 0-30000 -> Adresse 0-29999 im Holdingbereich
Register 30001-39999 -> Adresse 0-9998 im Inputbereich
Register 40000 -> Adresse 39999 im Holdingbereich
Register 40001-49999 -> Adresse 0-9998 im Holdingbereich
Register 50000-65535 -> Adresse 50000-65535 im Holdingbereich

Dieses Verhalten kann über das Attribut 'disableRegisterMapping' abgeschaltet werden. In dem Fall werden die Register 1:1 auf die Adressen im Holdingbereich abgebildet (wie bisher). Zusätzlich ist es dann möglich über das Attribut 'registerType' alle Zugriffe 1:1 auf den Inputbereich umzuleiten. Das Attribut 'registerType' wird nur ausgewertet wenn 'disableRegisterMapping' auf 1 steht.

Grüße,

ChrisD

Edit 05.05.2014, Anhang gelöscht, aktuelle Version in folgenden Beiträgen