Modbus RTU im e!COCKPIT

Hüpf3R

Level-1
Beiträge
13
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich versuche mich momentan an einer Modbus RTU Verbindung zwischen 2 Controllern im e!COCKPIT. Dabei möchte ich, dass der Master die Daten an den Slave sendet. Im Slave und Master sind jeweils die gleichen Datenstrukturen enthalten.
Ich möchte am Ende eine Master-Datenstruktur versenden in welcher mehrere weitere Datenstrukturen enthalten sind die dann wiederum die Variablen enthalten. Die Master-Datenstruktur ist vom Typ "UNION".

So sehen meine Bausteine aus:

PROGRAM PRG_Modbus_Master
VAR

ModbusMaster : FbMbMasterSerial;
xConnect : BOOL;
xIsConnected : BOOL;
xError : BOOL;
xTrigger : BOOL;
tTimeOut: TIME := T#2S;
utQuery : typMbQuery;
utResponse : typMbResponse;
Data : ARRAY[0..124] OF WORD := Master_Datenstruktur;

END_VAR



utQuery.bUnitId :=1;
utQuery.awWriteData := Data;

ModbusMaster(
xConnect:= xConnect,
I_Port:= COM1,
udiBaudrate:= 9600,
usiDataBits:= 8,
eParity:= 1,
eStopBits:= 3,
eHandshake:= 0,
ePhysical:= RS485,
xIsConnected=> xIsConnected,
xError=> xError,
oStatus=> ,
eFrameType:= eMbFrameType.RTU,
tTimeOut:= tTimeOut,
utQuery:= utQuery,
xTrigger:= xTrigger,
utResponse:= utResponse);

------------------------------------------------------------------------------

PROGRAM PRG_Modbus_Slave
VAR

ModbusSlave : FbMbSimpleServerSerial;
xConnect : BOOL;
xIsConnected : BOOL;
xError : BOOL;
InputRegister : ARRAY[0..65000] OF WORD;
HoldingRegisters : ARRAY[0..65000] OF WORD;
axCoils : ARRAY [1..100] OF BOOL;
axDiscreteInputs : ARRAY [1..100] OF BOOL;
oMbAccessInfo : fbMbAccessInfo;

Data : Master_Datenstruktur;

END_VAR


ModbusSlave( xConnect:= xConnect,
I_Port:= COM2,
udiBaudrate:= 9600,
usiDataBits:= 8,
eParity:= 1,
eStopBits:= 3,
eHandshake:= 0,
ePhysical:= RS485,
eFrameType:= eMbFrameType.RTU,
xIsConnected=> xIsConnected,
xError=> xError,
oStatus=> ,
bUnitId:= 1,
axDiscreteInputs:= axDiscreteInputs,
axCoils:= axCoils,
awInputRegisters:= InputRegister,
awHoldingRegisters:= HoldingRegisters,
oMbAccessInfo=> oMbAccessInfo);

Data := InputRegister;

------------------------------------------------------------------------------

TYPE Master_Datenstruktur :
UNION
stStruktur1 : ST_Struktur1;
stStruktur2 : ST_Struktur2;
...
END_UNION
END_TYPE

------------------------------------------------------------------------------

TYPE ST_Struktur1 :
STRUCT
rDaten : REAL;
sDaten : STRING;
...
END_STRUCT
END_TYPE

-----------------------------------------------------------------------------

Das ist jetzt nur eine Beispielstruktur. Am Ende soll die Datenstruktur mehrere 100 Variablen enthalten.


Jetzt meine Frage dazu:
1. Kann das mit meiner Parametrierung so funktionieren oder muss ich da noch etwas dran verändern?
2. Ich bin mir nicht sicher ob ich das mit der Datenstruktur so machen kann. Ist das so richtig oder muss ich da anders rangehen?
3. Gibt es eine genaue Beschreibung wozu die einzelnen Variablen da sind, bzw. vielleicht ein Parametrierungsbeispiel?


Gruß
 
Zuletzt bearbeitet:
Hallo,

was meinst Du mit Parametrierung?
Meinst Du diese Parameter?

udiBaudrate:= 9600,
usiDataBits:= 8,
eParity:= 1,
eStopBits:= 3,
eHandshake:= 0,
ePhysical:= RS485,
eFrameType:= eMbFrameType.RTU,

Die Parameter sind zumindest beim Master und Slave identisch.

Ich denke auf vieles wird dich der Compiler hinweisen, wenn Du dir die Mühe machst das mal zu übersetzen.

Zumindest fehlt noch einiges.
xConnect muss auf TRUE gesetzt werden, sonst passiert da nichts.
Beim Master muss das Trigger Bit
xTrigger angesteuert werden. Da kannst Du dich ja am Beispiel orientieren.

Die Query muss mit Daten gefüllt werden. Nur bUnitId reicht da nicht. Du musst angeben mit welchem Funktionscode gelesen/geschrieben werden soll und wie viele Register ab welcher Startadresse gelesen/geschrieben werden sollen.

Bei Enumerationen ist es lesbarer, wenn Du die Enumeration statt einer Zahl vorgibst.
Statt
eParity:= 1, liest sich eParity := WagoTypesCom.eTTYParity.None, einfach besser.

In einem Union deklarierte Variablen teilen sich den Speicher. Somit liegen stStruktur1 und stStruktur2 parallel im Speicher und beeinflussen sich gegenseitig.
Ich denke dies ist nicht gewünscht.

Falls Du mehrere Strukturen hast, müssen diese in einer Struktur gesammelt werden. Diese Struktur kann dann zusammen mit einem Array of Word in einem Union deklariert werden.
So gibt es einen Übergabepunkt. Die Struktur zur Verwendung im Programm und das Array für die Übergabe an den Master/Slave.

Da der Master maximal 123 Wörter schreiben und 125 Wörter lesen kann, ist dies die Begrenzung der Größe der Struktur.


Grüße

 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen Dank für die Antwort. Als Info dazu, ich arbeite zum ersten Mal mit Modbus RTU und dem Wago e!COCKPIT. Davor habe ich immer andere Automatisierungstools und Protokolle verwendet.
Hallo,
was meinst Du mit Parametrierung?
Meinst Du diese Parameter?

udiBaudrate:= 9600,
usiDataBits:= 8,
eParity:= 1,
eStopBits:= 3,
eHandshake:= 0,
ePhysical:= RS485,
eFrameType:= eMbFrameType.RTU,

Die Parameter sind zumindest beim Master und Slave identisch.

Genau die Parameter meinte ich + den Rest den man einstellen muss.



Ich denke auf vieles wird dich der Compiler hinweisen, wenn Du dir die Mühe machst das mal zu übersetzen.

Wenn ich schon die Hardware hätte würde ich mir ja "die Mühe" machen. Ich bin momentan leider gezwungen das Programm ohne Hardware zu schreiben, da diese noch nicht da ist. Sobald die Hardware da ist wird das Programm reingeladen, zu dem Zeitpunkt soll das Programm aber fertig sein.



Die Query muss mit Daten gefüllt werden. Nur bUnitId reicht da nicht. Du musst angeben mit welchem Funktionscode gelesen/geschrieben werden soll und wie viele Register ab welcher Startadresse gelesen/geschrieben werden sollen.

Ich hätte da jetzt folgendes stehen. Ich hoffe dass das so klappt (der Master soll meine Daten an den Slave senden):

utQuery.bUnitId :=1;
utQuery.bFunctionCode := 4;
utQuery.awWriteData := UN_DataUnion;
utQuery.uiWriteQuantity := 10;
utQuery.uiWriteAddress := 0;


In einem Union deklarierte Variablen teilen sich den Speicher. Somit liegen stStruktur1 und stStruktur2 parallel im Speicher und beeinflussen sich gegenseitig.
Ich denke dies ist nicht gewünscht.

Falls Du mehrere Strukturen hast, müssen diese in einer Struktur gesammelt werden. Diese Struktur kann dann zusammen mit einem Array of Word in einem Union deklariert werden.
So gibt es einen Übergabepunkt. Die Struktur zur Verwendung im Programm und das Array für die Übergabe an den Master/Slave.

In der Master_Datenstruktur habe ich das UNION wieder durch ein STRUCT ersetzt. Somit habe ich nun stStruktur1, stStruktur2, ... in einer Struktur gesammelt. Wenn ich jetzt das mit der UNION-Deklaration mache, heißt dies, dass es so aussehen muss:

TYPE UN_DataUnion:
UNION
Data : Array[0..123] OF WORD;
Storage : Master_Datenstruktur;
END_UNION
END_TYPE

Diese Union habe ich jetzt jeweils im Master und Slave- Controller erstellt. Im Master habe ich sie wie oben zu sehen bei Query übergeben. Beim Slave würde ich diese in das Inputregister schreiben:

InputRegister : UN_DataUnion;



Da der Master maximal 123 Wörter schreiben und 125 Wörter lesen kann, ist dies die Begrenzung der Größe der Struktur.


Grüße


Kann das so funktionieren?
Und was mache ich, wenn meine zu übergebende Datenstruktur zu groß wird. Muss ich diese dann aufteilen? Bzw. wie kann ich das Handhaben.
 
Zuletzt bearbeitet:
Das Schöne ist, dass Du auch ohne Hardware übersetzen kannst.
So werden viele Fehler in der Syntax auftauchen.
Ob das Programm nach dem erfolgreichen Übersetzen auch laufen wird ist noch eine andere Sache.

Wenn die Struktur zu groß ist, kannst Du z.B. die Strukturen einzeln übertragen.

Mit dem Funktionscode 4 (READ holding register) wirst du nicht schreiben können.
Besser wäre der Funktionscode 16 (WRITE multiple register).
 
Das Schöne ist, dass Du auch ohne Hardware übersetzen kannst.
So werden viele Fehler in der Syntax auftauchen.
Ob das Programm nach dem erfolgreichen Übersetzen auch laufen wird ist noch eine andere Sache.

Momentan klappt das übersetzten ohne Fehler. Mal sehen wie es dann mit der Hardware wird.


Wenn die Struktur zu groß ist, kannst Du z.B. die Strukturen einzeln übertragen.

Darüber habe ich auch schon einmal nachgedacht. Nur wie weiß mein Slave, welche Struktur gerade übertragen wird? Kann ich das mit übergeben, damit der Slave an die richtige stelle schreibt?


Mit dem Funktionscode 4 (READ holding register) wirst du nicht schreiben können.
Besser wäre der Funktionscode 16 (WRITE multiple register).


Ja da bin ich in meiner PDF um eine Tabelle verrutscht.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
Darüber habe ich auch schon einmal nachgedacht. Nur wie weiß mein Slave, welche Struktur gerade übertragen wird? Kann ich das mit übergeben, damit der Slave an die richtige stelle schreibt?

A) Jeder Struktur einen eindeutigen Index mitgeben - Handshake erforderlich, damit der Master weiß der Slave hat richtig empfangen und verarbeitet empfangen
B) Feste Reihenfolge - Auch hier Handshake erforderlich
C) Master schreibt an verschiedene Adressen

Müssen die Daten eigentlich nur einmal übertragen werden oder regelmäßig? Bei letzterem könntest Du auch den Konfigurator verwenden, dann kümmert sich der Controller um alles selbst. Eventuell müßtest Du nur etwas mit Deinen Strukturen tricksen.

Gruß
 
Ok dann versuche ich das mal.

Die Variablen innerhalb der Struktur können sich sekundlich ändern. In der Slave SPS sollen immer die aktuellsten Daten vorhanden sein. Daher sollen die Strukturen auch Zyklisch jede Sekunde übergeben werden.
Ich habe schon mal was über einen Konfigurator gelesen. Im e!COCKPIT konnte ich jedoch nur einen Modbus TCP Konfigurator finden.
 
Hallo,
Ich kenne zwar e!Cockpit nicht aber ich glaube, dass dein
Ansatz mit der Struktur nicht funktionieren wird.
Du kannst im Modbusprotokoll nur Register und Coils übertragen.
1 Register = 1Word, 1Coil = 1 Bit.
1 Realwert (32 Bit) muss dann z.B. auf zwei Register gepackt
werden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

habe gerade auf die aktueller Version aktualisiert (1.7.0.2), aber auch die davor hat mir vorher auch den Konfigurator für serielle Verbindung angezeigt. Zumindest für die interne Schnittstelle

2020-05-13 14_33_11-Neues Projekt - e!COCKPIT.png
Auch Strukturen kann man damit freigeben, wir dann als Array of Word behandelt.

Gruß
 
Hallo,
Ich kenne zwar e!Cockpit nicht aber ich glaube, dass dein
Ansatz mit der Struktur nicht funktionieren wird.
Du kannst im Modbusprotokoll nur Register und Coils übertragen.
1 Register = 1Word, 1Coil = 1 Bit.
1 Realwert (32 Bit) muss dann z.B. auf zwei Register gepackt
werden.
Problem ist, ich kann die Daten die ich versenden muss leider nicht beeinflussen. Ich bekomme diese nunmal als Datenstruktur. Daher habe ich gehofft diese so direkt weiterleiten zu können.


Hallo,

habe gerade auf die aktueller Version aktualisiert (1.7.0.2), aber auch die davor hat mir vorher auch den Konfigurator für serielle Verbindung angezeigt. Zumindest für die interne Schnittstelle

Anhang anzeigen 49729
Auch Strukturen kann man damit freigeben, wir dann als Array of Word behandelt.

Gruß

Also momentan kann ich da nur zwischen Modbus TCP und UDP wählen:
2020-05-14_07h37_03.png
und bevor jemand fragt, ja die Steuerung selbst ist nicht Modbus RTU fähig. Dafür haben beide Steuerungen die serielle Schnittstellenkarte 750-652.
Vielleicht klappt es mit dem Konfigurator wenn ich irgendwann die Hardware habe? Oder habe ich da irgendwas falsch gemacht? Muss man die Erweiterungskarte vielleicht irgendwie aktivieren?


Ansonsten wäre einem Kollegen noch die Frage zu dem unterschied vom Array OF WORD und Array OF BOOL aufgekommen. Die Input und Holding Register vom Slave sind ja vom Typ ARRAY OF BOOL, während axDiscreteInputs und axCoils vom Typ ARRAY OF BOOL sind. Seine Frage wäre da jetzt ob es einen großen unterschied macht wenn ich meine Daten in ein ARRAY OF BOOL packe oder in ein ARRAY OF WORD. Bzw. was allgemein der Unterschied ist.
 
Hallo,
Ich kenne zwar e!Cockpit nicht aber ich glaube, dass dein
Ansatz mit der Struktur nicht funktionieren wird.
Du kannst im Modbusprotokoll nur Register und Coils übertragen.
1 Register = 1Word, 1Coil = 1 Bit.
1 Realwert (32 Bit) muss dann z.B. auf zwei Register gepackt
werden.
Wieso sollte sein Vorhaben eine Struktur via Modbus versenden zu wollen nicht funktionieren? Man kann doch via Modbus auch mehrere Register gleichzeitig übertragen. Man muss halt nur darauf achten, dass beide Steuerungen die selbe Byte-Reihenfolge haben sprich beide Little Endian oder Big Endian, was hier ja der Fall ist, ansonsten muss man die Daten noch anpassen.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ansonsten wäre einem Kollegen noch die Frage zu dem unterschied vom Array OF WORD und Array OF BOOL aufgekommen. Die Input und Holding Register vom Slave sind ja vom Typ ARRAY OF BOOL, während axDiscreteInputs und axCoils vom Typ ARRAY OF BOOL sind. Seine Frage wäre da jetzt ob es einen großen unterschied macht wenn ich meine Daten in ein ARRAY OF BOOL packe oder in ein ARRAY OF WORD. Bzw. was allgemein der Unterschied ist.
Naja, ein Bool nimmt 1 Byte ein, bei 4 Bool Variablen bräuchtet Ihr dann zwei Register á 16 Bit. Wenn Ihr Euren Modbus-Bereich so umkonfiguriert, dass statt Bools dort Bytes oder Words stehen und Ihr dann jedes Bit einzeln mit dem I
nhalt der Bool Variablen füllt wird das Ganze
kompakter.
 
Hallo,
und bevor jemand fragt, ja die Steuerung selbst ist nicht Modbus RTU fähig. Dafür haben beide Steuerungen die serielle Schnittstellenkarte 750-652.
Vielleicht klappt es mit dem Konfigurator wenn ich irgendwann die Hardware habe? Oder habe ich da irgendwas falsch gemacht? Muss man die Erweiterungskarte vielleicht irgendwie aktivieren?
das war nicht klar, daß der PFC nicht die RS Schnittstelle onboard hat. Dann funktioniert das nicht. Auf meinem Bild erfolgt dir Verbindung der beiden Controller über die rechte Schnittstelle (RS, daher auch in schwarz), Du hast Sie verbunden über die linke Schnittstelle (ETH, in grau). Dann wirst Du das tatsächlich von Hand machen müssen.

Da kannst Du die Strukturen wie es Dir beliebt übertragen.

Gruß
 
Also wenn ich das richtig verstanden habe übergebe ich dem Master die Daten-UNION. In einer Schleife setzte ich dann die uiWriteAddress weiter damit der Slave weiß wo er in seine UNION schreiben muss. Bei uiWriteQuantity schreibe ich rein wie viele Bytes auf einmal übertragen werden soll. Also würde ich z.B. bei uiWriteQuantity eine 10 zuweisen und die uiWriteAddress bei 0 beginnen lassen und dann immer um 10 weitersetzen.

Oder als zweite Überlegung, ich erstelle mehrere Daten-UNIONs im Master welche jeweils einen Teil der Daten enthalten. Lasse im Slave alles so wie es ist und setze auch hier einfach die Adresse immer weiter.
 
@dingo
Danke für die Antwort, leider kann ich hier nix finden (siehe Screenshots).

Habe aber heute endlich jemanden bei Wago erreichen können.
Die Antwort: Der Modbus Configurator wird vermutlich auch die nächsten Jahre nicht funktionieren!
Der COM-Port ist fix auf COM1 eingestellt und kann nicht geändert werden, daher funktioniert er nur noch bei ausgewählten Controllern und es sind auch keine Klemmen mehr nutzbar.
Traurig, aber scheinbar wahr.
Wieder ein weiterer, grober Rückschritt seit e!Cockpit.

screenshot1.jpgscreenshot2.jpg
 
Hallo, meine Hardware ist endlich angekommen wodurch ich jetzt mal ein paar Tests machen konnte.
Eine Übertragung habe ich zustande gebracht. Nun hänge ich daran wie ich die Gesamte Struktur übertrage. Mittlerweile sieht die Struktur so aus:
Ich habe eine Hauptstruktur welche 4 Unterstrukturen instanziert. Diese 4 Unterstrukturen instanzieren jeweils ungefähr 8 weitere Strukturen in denen immer 4 Variablen angelegt werden. Hier nochmal mit einem schlecht gezeichneten Bild aus Paint falls es bei der Verdeutlichung hilft.
StrukturSyntax.jpg

Mit Sizeof erhalte ich eine Größe von 524 für die gesamte Struktur. Die ersten 120 Bytes sind kein Problem die kriege ich übertragen, aber wie komme ich an die restlichen ran? Ich nutze Momentan eine Union wo ich die Hauptstruktur instanziert habe und ein Array of Word eingefügt habe.
 
Zurück
Oben