# Modbus RTU im e!COCKPIT



## Hüpf3R (12 Mai 2020)

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ß


----------



## Tobsucht (13 Mai 2020)

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


----------



## Hüpf3R (13 Mai 2020)

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.


Tobsucht schrieb:


> Hallo,
> was meinst Du mit Parametrierung?
> Meinst Du diese Parameter?
> 
> ...



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





Tobsucht schrieb:


> 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.





Tobsucht schrieb:


> 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;




Tobsucht schrieb:


> 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;





Tobsucht schrieb:


> 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.


----------



## Tobsucht (13 Mai 2020)

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).


----------



## Hüpf3R (13 Mai 2020)

Tobsucht schrieb:


> 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.




Tobsucht schrieb:


> 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?




Tobsucht schrieb:


> 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.


----------



## Thruser (13 Mai 2020)

Hallo,


Hüpf3R schrieb:


> 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ß


----------



## Hüpf3R (13 Mai 2020)

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.


----------



## Rüdesheim (13 Mai 2020)

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.


----------



## Thruser (13 Mai 2020)

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



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

Gruß


----------



## Hüpf3R (14 Mai 2020)

Rüdesheim schrieb:


> 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.
> ...


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.




Thruser schrieb:


> 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
> 
> ...



Also momentan kann ich da nur zwischen Modbus TCP  und UDP wählen:


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.


----------



## oliver.tonn (14 Mai 2020)

Rüdesheim schrieb:


> 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.
> ...


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.


----------



## oliver.tonn (14 Mai 2020)

Hüpf3R schrieb:


> 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 Inhalt der Bool Variablen füllt wird das Ganze​ kompakter.


----------



## Thruser (14 Mai 2020)

Hallo,


Hüpf3R schrieb:


> 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ß


----------



## Hüpf3R (14 Mai 2020)

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.


----------



## strgalt (18 Mai 2020)

Hi,

hantiere auch gerade mit einer Modbus RTU Kopplung.
Wo um Himmels Willen kann ich den COM-Port einstellen?

Danke


----------



## Hüpf3R (19 Mai 2020)

strgalt schrieb:


> Hi,
> 
> hantiere auch gerade mit einer Modbus RTU Kopplung.
> Wo um Himmels Willen kann ich den COM-Port einstellen?
> ...



Der Baustein enthält diese Variable:
I_Port:= 
der muss der jeweilige COM-Port zugewiesen werden.


----------



## strgalt (19 Mai 2020)

Hüpf3R schrieb:


> Der Baustein enthält diese Variable:
> I_Port:=
> der muss der jeweilige COM-Port zugewiesen werden.



Hi,
danke für die schnelle Rückmeldung.
Nutze den Modbus Configurator, keinen Baustein.
Oder habe ich da was übersehen?


----------



## dingo (19 Mai 2020)

@strgalt:

Bei "Einstellungen" neben dem Register "Produktkatalog":


----------



## strgalt (19 Mai 2020)

@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.


----------



## Hüpf3R (12 Juni 2020)

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.



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.


----------



## Thruser (12 Juni 2020)

Hallo,

wenn Du auf die Hauptstruktur über ein Array zugreifst, dann mach doch folgendes:

deklariere ein Sende/Empfangsarry von z.B. 121 Wörtern
in das erste Wort schreibst Du welcher Teil übertragen wird
dann kopierst du die ersten 120 Wörter in Dein Sendearray
dann versendest Du diese Daten
warten bis Rückmeldung alles versendet
dann den nächsten Teil nach dem gleichen Muster bis alle Teile versendet sind

Auf der Empfangsseite wartest Du bis neue Daten angekommen sind, z.B schauen ob sich das erste Wort geändert hat
dann die empfangenen Daten in den entsprechenden Abschnitt kopieren

Dabei solltest Du darauf achten, daß die Daten einigermaßen konsisten übertragen werden. Eventuell mußt Du dafür Padding Bytes einfügen, nicht daß Du von einer 32 Bit Variablen mit der ersten Übertragung die ersten 16 Bit überträgst und dann mit dem zweiten Schwung die nächsten 16 Bit, denn est könnte sein, daß die Variable sich inzwischen geändert hat.

Das wäre jetzt ein Beispiel. Könnte man auch Unterstrukturabhängig aufbauen, so daß Du jeweils eine Unterstruktur in einem Rutsch übermittelst.

Gruß 

PS. eventuell kannst du auch direkt Abschnittsweise auf das Array der Hauptstruktur zugreifen über den Adressoperator adr(Array[0]), und dann mit entsprechenden Offsets arbeiten Array[120] etc.


----------



## Hüpf3R (15 Juni 2020)

Danke so in der Art hat es funktioniert


----------

