# e!Cockpit Modbus-TCP



## Pari (11 Juni 2018)

Hallo,

ich bin neu hier und versuche seit einiger Zeit mit einer Wago PFC200 eine elektronische Last anzusteuern. Zur Kommunikation dient ein zusätzliches Modul an der Last. Dieses erlaubt die Kommunikation via Modbus TCP (auf dem Port 502) und Modbus RTU (auf dem Standard-Port 5025 und allen anderen). Die Daten selbst werden über ein Patchkabel übertragen.
Ich arbeite in e!Cockpit mit dem Baustein _FbMbMasterTcp,_ um mit Modbus TCP die Last anzusteuern, was soweit auch ganz gut funktioniert. Nun möchte ich ein Programm (ModbusTCP_1), das sich um die wiederkehrenden Standardanfragen kümmert. Dieses wird regelmäßig von PLC_PRG aufgerufen und bekommt nur die Daten mit, die übergeben werden. Die Register sind schon im Programm deklariert. In PLC_PRG werden die Daten verarbeitet bzw. weitergeleitet, was auch klappt. 


Nun habe ich zwei Probleme, die ich nicht gelöst bekomme:

Problem 1: Ich kann einzelne Register nicht auslesen. Konkret geht es hier um die aktuellen Messwerte der Last. Ich empfange das Echo, welches besagt, dass ich die Register auslesen möchte, awData bleibt jedoch leer (0).

 Problem 2 (ist vermutlich simpler): Ich möchte in drei unterschiedliche Register nacheinander verschiedene Werte schreiben und anschließend drei weitere Register auslesen. Das Ganze soll noch mit einer IF-Bedingung kombiniert werden, sodass die nächste Anfrage nur gesendet wird, wenn die Anfrage, die zuletzt gesendet wurde, in utResponse zu finden ist. Eigentlich dachte ich, ich könnte das recht einfach lösen, aber alle Versuche und auch ein Lösungsvorschlag mit einer Joblist, aus einem anderen Beitrag hier, haben bei mir nur zu Fehlern geführt (zumal ich die Register nicht auslesen kann, siehe erstes Problem).

Die Variablen hätte ich wie folgt deklariert:

```
PROGRAM ModbusTCP_1
VAR_INPUT
    // Sollwerte von U I und P  werden übergeben und sollen nacheinander gesendet werden.
    // Wurde ein Wert gesendet soll erst die Antwort (ein Echo) überprüft werden
    // im Anschluss werden die Werte gemessen und zurück ans Hauptprogramm gegebenausgegeben. 
    
    wertU:        WORD;
    wertI:        WORD;
    wertP:        WORD;
    // Istwert Strom lesen: (regNr:=16#03, readAdd:= 16#01FB, readQua:=1, writAdd:=0, writQua:=0);
    // Istwert Spannung lesen: (regNr:=16#03, readAdd:= 16#01FC, readQua:=1, writAdd:=0, writQua:=0);
    // Istwert Leistung lesen: (regNr:=16#03, readAdd:= 16#01FD, readQua:=1, writAdd:=0, writQua:=0);
END_VAR
VAR_OUTPUT
    messWertU:        WORD;
    messWertI:        WORD;
    messWertP:        WORD;
END_VAR
VAR
    // Spannung    
    regNrU:        BYTE := 16#06;
    writAddU:    WORD := 16#01F4;
    writQuaU:    WORD := 1;
    // Strom
    regNrI:        BYTE := 16#06;
    writAddI:    WORD := 16#01F5;
    writQuaI:    WORD := 1;
    // Leistung
    regNrP:        BYTE := 16#06;
    writAddP:    WORD := 16#01F6;
    writQuaP:    WORD := 1;
        
    myTcpMaster     : FbMbMasterTcp :=  (  xConnect        := TRUE,
                                           sHost        := '192.168.1.6',    // IP of the remote server
                                           wPort        := 502,                // port at the remote server

                                            utKeepAlive    := (    xEnable      := TRUE, // use keep alive
                                                                tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                                                tInterval    := T#2S, //  Interval between two successive KA-Packets
                                                                udiProbes    := 5     //  Number of KA retry before giving up
                                                            ),
                                                            
                                            eFrameType  := eMbFrameType.ETHERNET,
                                            tTimeOut    := T#30MS
                                        );

    utQuery         : typMbQuery := (   bUnitId         := 0,            // Slaveaddress
                                        bFunctionCode   := 0,            // write input registers
                                        uiReadAddress   := 0,            // Startaddress
                                        uiReadQuantity  := 0,            // Quantity of wanted registers
                                        uiWriteAddress  := 0,            // Startadresse
                                        uiWriteQuantity := 0,            // Quantity of wanted registers
                                        awWriteData        := [124(0)]        // array to write
                                    );

    xTxTrigger      : BOOL;             (* Set this variable once for start a job. This variable will be automaticly reset by the master if the job is done.*)
    utResponse      : typMbResponse;    (* After the job is done you can find at this structure the result.*)
    tonDelay        : TON := (PT := T#30MS); // This is the silence time between two requests
END_VAR
```
Im Programm wollte ich bFuctionCode bis awWriteData immer mit den passenden Informationen überschreiben.

Ich muss dazu sagen, ich hatte mit Wago und Modbus noch nie etwas am Hut und bin, was das angeht, absoluter Anfänger. Verschiedene Dokus und das ein oder andere Video hatten mich bei meinen Problemen leider nicht groß weitergebracht. 
Bin für jeden Tipp dankbar.


Viele Dank.


----------



## Thruser (13 Juni 2018)

Hallo,

hast Du denn schon einmal das auch in dem anderen Beitrag erwähnte qmodmaster oder ähnliches ausprobiert?

Wie fragst Du denn die Daten im Programm ab, könntest Du eventuell mal den Teil des Codes zeigen? Da Du die Daten hier jetzt einzeln abfragst mußt Du auch erst jedesmal abwarten bis ein Auftrag fertiggestellt ist. Mit der Jobliste wären das bereits drei einzelne Jobs.

Da die drei Adressen aufeinanderfolgen solltest Du versuchen die Daten in einem Rutsch, d.h. mit einem Auftrag, zu lesen (readQua:=3). Das minimiert die Ablaufzeit und die zu übertragende Datenmenge erheblich..

Gruß

PS: Warum benutzt Du nicht den Modbuskonfigurator von e!cockpit?


----------



## Pari (22 Juni 2018)

Hallo Thruser,

danke für die Antwort. Das Problem mit dem Leerbleiben der Response hat sich erledigt, ich bekomme nun eine Antwort, weiß beim besten Willen nicht wo der Fehler war und warum es zuvor nicht funktioniert hat. Ich glaube manchmal spinnt auch einfach nur die Last. qModMaster kannte ich schon. 

Danke für den Tipp, mehrere Register gleichzeitig zu lesen, das funktioniert tadellos. Mehrere Register gleichzeitig beschreiben lässt die Last aber nicht zu, daher müsste ich das über die Joblist erledigen. Bei dieser verstehe ich jetzt aber nicht genau wie diese funktioniert bzw. wie sie programmiert wird. Ich weiß, dass ich in den Variabeln ein Array erstellen muss vom Typ utQuery, mehr habe ich bisher aber nicht verstanden. 

Hier mal der Quelltext zur Jobliste:

```
PROGRAM Test
// Test mit Joblist
VAR
    Jobliste: ARRAY [0..3] OF typMbQuery :=  [//JOB 1 Spannung schreiben
                                          (    bUnitId         := 0,            // Slaveaddress
                                            bFunctionCode   := 16#06,        // write input registers
                                            uiReadAddress   := 0,            // Startaddress
                                            uiReadQuantity  := 0,            // Quantity of wanted registers
                                            uiWriteAddress  := 16#01F4,        // Startadresse
                                            uiWriteQuantity := 1,            // Quantity of wanted registers
                                            awWriteData        := [124(0)]        // array to write
                                          ), //JOB 2 Strom schreiben
                                          (    bUnitId         := 0,            // Slaveaddress
                                            bFunctionCode   := 16#06,        // write input registers
                                            uiReadAddress   := 0,            // Startaddress
                                            uiReadQuantity  := 0,            // Quantity of wanted registers
                                            uiWriteAddress  := 16#01F5,        // Startadresse
                                            uiWriteQuantity := 1,            // Quantity of wanted registers
                                            awWriteData        := [124(0)]        // array to write
                                          ),//JOB 3 Leistung schreiben
                                          (    bUnitId         := 0,            // Slaveaddress
                                            bFunctionCode   := 16#06,        // write input registers
                                            uiReadAddress   := 0,            // Startaddress
                                            uiReadQuantity  := 0,            // Quantity of wanted registers
                                            uiWriteAddress  := 16#01F6,        // Startadresse
                                            uiWriteQuantity := 1,            // Quantity of wanted registers
                                            awWriteData        := [124(0)]        // array to write
                                          ),//JOB 4 Werte auslesen
                                          ( bUnitId         := 0,            // Slaveaddress
                                            bFunctionCode   := 16#03,        // write input registers
                                            uiReadAddress   := 16#01FB,        // Startaddress
                                            uiReadQuantity  := 3,            // Quantity of wanted registers
                                            uiWriteAddress  := 0,            // Startadresse
                                            uiWriteQuantity := 0,            // Quantity of wanted registers
                                            awWriteData        := [124(0)]        // array to write
                                           )];
    Responseliste:ARRAY [0..3] OF typMbResponse;
    iCounter: INT := 0;
    xTrigger:BOOL:=TRUE;
    FB_ModbusMasterTCP:FbMbMasterTcp;
    
END_VAR

===============================================================================

FB_ModbusMasterTCP    (    xConnect    := TRUE,
                        sHost        := '192.168.1.6',    // IP of the remote server
                        wPort        := 502,                // port at the remote server

                        utKeepAlive    := (    xEnable      := TRUE, // use keep alive
                                            tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                               tInterval    := T#2S, //  Interval between two successive KA-Packets
                                            udiProbes    := 5     //  Number of KA retry before giving up
                                         ),
                                    
                        eFrameType  := eMbFrameType.ETHERNET,
                        tTimeOut    := T#30MS,
                        utQuery:=Jobliste[iJobCounter], 
                        xTrigger:=xTrigger, 
                           utResponse:=Responseliste[iJobCounter]);
                     );
                
// Fehler --> Weiß nicht weiter
```

Das ist noch so ziemlich mein einziges Problem, wobei ich die Response, der ausgelesenen Werte, noch gerne in einem kleinen Array abspeichern würde.


Grüße


----------



## Thruser (22 Juni 2018)

Hi,

schau Dir noch mal das Beispiel von hier an: https://www.sps-forum.de/wago/91573...r-und-852-slave-unter-cockpit.html#post689957

Da ist eigentlich schon alles dabei.

Ansonsten müssen die Sollwerte in
Jobliste[0].awWriteDate[0] für Spannung
Jobliste[1].awWriteDate[0] für Strom
Jobliste[2].awWriteDate[0] für Leistung





Pari schrieb:


> // Fehler --> Weiß nicht weiter


Zumindest ein Fehler ist das iCounter und iJobCounter verwendet wird.



Pari schrieb:


> Das ist noch so ziemlich mein einziges Problem, wobei ich die Response, der ausgelesenen Werte, noch gerne in einem kleinen Array abspeichern würde.



Die Daten stehen doch schon in einem Array: Responseliste[3].awData[0] bis Responseliste[3].awData[2] 

Gruß


----------



## Pari (22 Juni 2018)

Danke,

irgendwie habe ich gerade festgestellt, dass sich fast alle meine Fragen von selbst aufgelöst haben. Muss wohl auch mal sein,  obwohl man sich dann schön dämlich vorkommt. Hatte den Beitrag bestimmt  schon zweimal gelesen, da ich das mit der Jobliste ja aus diesem hatte.


----------



## Pari (27 Juni 2018)

Hey Thruser,

hast Du noch einen Tipp, wie man auf die Zeiten, die zwischen den einzelnen Requests vergehen, bei der Jobliste besser eingehen kann? Ich habe den Verdacht, dass meine Last nicht alle Anfragen bearbeiten kann und würde daher gerne 30ms Zeit zwischen jedem Request lassen? Der FB_F_Trig löst ja direkt nach einer Anfrage aus oder verstehe ich das falsch? Ich hätte das Ganze jetzt so versucht: 


//Auf Jobende warten
FB_F_Trig (clk:=xTrigger); // Bei Flanke startet
delay(IN := FB_F_Trig.Q, PT := T#30MS);  // TON

//Jobende erfolgt, nächsten Job starten
IF delay.Q THEN 
    xTrigger :=TRUE;
    iJobCounter := (iJobCounter+1) MOD 4;
END_IF

bei dem Versuch gibt es jedoch garkeine Rückmeldung mehr.


Grüße


----------



## Thruser (27 Juni 2018)

Hallo,


Pari schrieb:


> Hey Thruser,
> 
> hast Du noch einen Tipp, wie man auf die Zeiten, die zwischen den einzelnen Requests vergehen, bei der Jobliste besser eingehen kann? Ich habe den Verdacht, dass meine Last nicht alle Anfragen bearbeiten kann und würde daher gerne 30ms Zeit zwischen jedem Request lassen? Der FB_F_Trig löst ja direkt nach einer Anfrage aus oder verstehe ich das falsch? Ich hätte das Ganze jetzt so versucht:
> 
> ...


FB_F_Trig ist immer nur einen Zyklus aktiv, daher funktioniert das nicht.

Den kannst Du aber rausschmeissen:

```
delay(IN:=not xTrigger, PT := T#30MS);
```

Gruß


----------



## Pari (27 Juni 2018)

Sprich ich muss mein Programm mehrfach aufrufen und die Zeit zwischen zwei Requests kann ich nicht genauer einstellen?


Gruß


----------



## Thruser (27 Juni 2018)

Hallo,



Pari schrieb:


> Sprich ich muss mein Programm mehrfach aufrufen und die Zeit zwischen zwei Requests kann ich nicht genauer einstellen?


Nein und Nein.

Wenn Du mit dem TON Timer arbeiten willst, darfst Du nicht mit der Flanke arbeiten, da die Flankenauswertung nur ein Impuls ist, der Timer aber ein dauerhaftes Signal benötigt. Daher einfach das negierte xTrigger Signal für den Timer verwenden.


```
delay(IN := not xTrigger, PT := T#30MS); // TON


//Jobende erfolgt, nächsten Job starten
IF delay.Q THEN 
  xTrigger :=TRUE;
  iJobCounter := (iJobCounter+1) MOD 4;
END_IF
```

Dadurch muß xTrigger für 30ms false sein, bevor es wieder auf true gesetzt wird.

Gruß


----------



## Pari (30 August 2018)

Hallo,

hab wieder ein altes Problem mit meinem geliebten Modbus.  Meine beiden Programme wurden bisher zyklisch aufgerufen, nun sollen  diese einmalig aufgerufen werden. Anbei mal zwei Screenshots, einmal ein  Programm für einzelne Sonderbefehle und ein Programm mit einer  Jobliste. Das Programm mit den Sonderbefehlen muss ich dreimal aufrufen,  damit meine Last einen Befehl bekommt, meine Jobliste muss ein  vielfaches davon aufgerufen werden.  
Versuche das Problem mit  Schleifen oder Timer zu lösen, schlugen fehl. Gibt es hier eine  Möglichkeit das Problem ohne zyklischen Aufruf zu lösen?   


Grüße


----------



## Thruser (10 September 2018)

Hi,  

Du könntest zwar mal folgendes versuchen:

```
(* JOB 0 *)
xTrigger := true;
FB_ModbusMasterTCP    ( xConnect    := TRUE,
                        sHost        := '192.168.1.6',    // IP of the remote server
                        wPort        := 502,                // port at the remote server


                        utKeepAlive    := ( xEnable      := TRUE, // use keep alive
                                            tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                            tInterval    := T#2S, //  Interval between two successive KA-Packets
                                            udiProbes    := 5     //  Number of KA retry before giving up
                                         ),
                                    
                        eFrameType  := eMbFrameType.ETHERNET,
                        tTimeOut    := T#30MS,
                        utQuery:=Jobliste[0], 
                        xTrigger:=xTrigger, 
                        utResponse:=Responseliste[0]);
                     );
WHILE xTrigger DO
FB_ModbusMasterTCP();
END_WHILE;

(* JOB 1 *)
xTrigger := true;
FB_ModbusMasterTCP    ( xConnect    := TRUE,
                        sHost        := '192.168.1.6',    // IP of the remote server
                        wPort        := 502,                // port at the remote server


                        utKeepAlive    := ( xEnable      := TRUE, // use keep alive
                                            tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                            tInterval    := T#2S, //  Interval between two successive KA-Packets
                                            udiProbes    := 5     //  Number of KA retry before giving up
                                         ),
                                    
                        eFrameType  := eMbFrameType.ETHERNET,
                        tTimeOut    := T#30MS,
                        utQuery:=Jobliste[1], 
                        xTrigger:=xTrigger, 
                        utResponse:=Responseliste[1);
                     );
WHILE xTrigger DO
FB_ModbusMasterTCP();
END_WHILE;

(* JOB 2 *)
xTrigger := true;
FB_ModbusMasterTCP    ( xConnect    := TRUE,
                        sHost        := '192.168.1.6',    // IP of the remote server
                        wPort        := 502,                // port at the remote server


                        utKeepAlive    := ( xEnable      := TRUE, // use keep alive
                                            tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                            tInterval    := T#2S, //  Interval between two successive KA-Packets
                                            udiProbes    := 5     //  Number of KA retry before giving up
                                         ),
                                    
                        eFrameType  := eMbFrameType.ETHERNET,
                        tTimeOut    := T#30MS,
                        utQuery:=Jobliste[2], 
                        xTrigger:=xTrigger, 
                        utResponse:=Responseliste[2]);
                     );
WHILE xTrigger DO
FB_ModbusMasterTCP();
END_WHILE;

(* JOB 3 *)
xTrigger := true;
FB_ModbusMasterTCP    ( xConnect    := TRUE,
                        sHost        := '192.168.1.6',    // IP of the remote server
                        wPort        := 502,                // port at the remote server


                        utKeepAlive    := ( xEnable      := TRUE, // use keep alive
                                            tMaxIdleTime := T#5S, //  Maximum time of inactivity
                                            tInterval    := T#2S, //  Interval between two successive KA-Packets
                                            udiProbes    := 5     //  Number of KA retry before giving up
                                         ),
                                    
                        eFrameType  := eMbFrameType.ETHERNET,
                        tTimeOut    := T#30MS,
                        utQuery:=Jobliste[3], 
                        xTrigger:=xTrigger, 
                        utResponse:=Responseliste[3]);
                     );
WHILE xTrigger DO
FB_ModbusMasterTCP();
END_WHILE;
```

Da müßtest Du aber in eine Zykluszeitüberschreitung laufen.

Es hat schon seinen Grund, daß einige Dinge zyklisch aufgerufen werden müssen bzw. über mehrere Zyklen verteilt werden. 

Du wirst Dir Bedingungen überlegen müssen wann Du welche Modbuskommunikation verwenden willst und diese dann entsprechend bedingt, z.B. mit If Abfrage, aufrufen müssen. 

Bei dem Beispiel mit der Jobliste mußt Du dann die Zeile mit dem MOD rausnehmen, so das diese nur einmal abgearbeitet wird. Dafür dann eine Abfrage machen wenn alle Jobs abgearbeitet sind die Bedingung, die zur Abarbeitung der Jobliste geführt hat, zurücksetzen.

Gruß


----------

