# Wago "Modbus TCP"-Kommunikation mit 750-8100 (Master) und 852 (Slave) unter e!Cockpit



## aki09 (20 März 2018)

*Wago "Modbus TCP"-Kommunikation mit 750-8100 (Master) und 852 (Slave) unter e!Cockpit*

Hallo zusammen,

ich versuche gerade eine Modbus TCP Verbindung zwischen dem 750-8100 und dem 750-352 einzubinden, allerdings möchte ich das nicht über den Modbus-Konfigurator (siehe Handbuch) umsetzen, sondern über die Bibliothek "FbMbMasterTcp". Leider stehe ich gerade ein bisschen auf dem Schlauch, wie ich das genau umsetzen muss.

*Was habe ich bisher gemacht?
*- Funktionsbaustein "WagoAppPlcModbus.FbMbMasterTcp" in PLC_PRG (mit FUP) eingefügt. 
- Variablen deklariert (Master-Adresse: 192.168.1.100 / Slave-Adresse: 192.168.1.101)
- Auslesen des PT100 
-> Nicht klar, wie ich das umsetzen soll bzw. in welches Register der Wert geschrieben wird und wie man diesen dann ausliest.







_// Konfigurierung Modbus Master
    ModbusMaster01: WagoAppPlcModbus.FbMbMasterTcp;

    // Input ModbusMaster
    xConnect: BOOL := 1;
    sHost: STRING := '192.168.1.101';  --> sHost ist die Slave-Adresse?
    wPort: WORD := 502;
    utKeepAlive: typKeepAlive;
    eFrameType: eMbFrameType := ETHERNET;
    tTimeOut: TIME := t#5s;
    utQuery         : typMbQuery := (   bUnitId         := 1,       // Slaveaddress
                                        bFunctionCode   := 16#17,   // multiple read/write registers
                                        uiReadAddress   := 0,       // Startaddress
                                        uiReadQuantity  := 10,      // Quantity of wanted registers
                                        uiWriteAddress  := 512,      
                                        uiWriteQuantity := 0,       
                                        awWriteData     := [124(0)]
                                    )
    utResponse: typMbResponse;
    xTrigger: BOOL;

    // Output ModbusMaster
    xIsOpen: BOOL;
    xError: BOOL;
    oStatus: FbResult;

END_VAR
_
*Ziel:
*Auslesen der Temperatur eines PT100 (von 750-352) und Übertragung der Werte auf den Feldbuscontroller (750-8100). Übersteigt die Temperatur einen gewissen Wert, soll ein DO gesetzt werden.Das möchte ich mir gerne erstmal als simples Beispiel aufbauen, um die Kommunikation via Modbus und das Einbinden der Bibliotheken / Schreiben der Register zu verstehen.*

Fragen:
*- Wie lese ich die Daten vom Feldbuskoppler via Modbus auf dem Controller aus? Die Geschichte mit den Registern ist mir noch nicht so ganz klar.In welches Register werden die Eingangswerte der PT100 geschrieben? (Aufbau Slave "750-452": (1) 750-400, (2) 750-461, (3) 750-492, (4) 750-600)

- Die Variable sHost ist die Slave- oder die Master-Zieladresse? Was ist, wenn ich zwei Master und zwei Slaves habe. Wie kann ich festlegen, welcher Master mit welchem Slave verknüpft ist?Das interessiert mich, weil ich in der Zukunft gerne mehrere dieser Master-Slave Topologien in ein Gesamtnetzwerk aufbauen möchte.

- Gibt es detailliertere Tutorials oder Beispielprojekte für e!Cockpit, mit denen man sich die Einarbeitung erleichtern kann? 
Auf YouTube findet man häufig nur sehr oberflächliche Tutorials oder Tutorials mit CodeSys 2.3*.

*Vielen Dank im Voraus.


----------



## aki09 (26 März 2018)

Hallo zusammen,

ich habe mich in den letzten Tagen weiter mit dem Thema beschäftigt, bisher aber keine Lösung des Problems gefunden. Im Anhang habe ich nochmal die Projektdatei (Test123.ecp) angehängt - vielleicht macht das die Sache einfacher.

*Was möchte ich umsetzen?*
  Um mit dem System vertraut zu werden, möchte ich erstmal ein simples Projekt erstellen. Dabei möchte ich einen *PT100 Temperatursensor* (auf Feldbuskoppler -352, mit Analogeingangskarte „750-492“, Klemme AI1) auslesen und den Temperaturwerts *via Modbus TCP auf den Feldbuskoppler -8100 senden.* 
(!! Nicht mit Modbus-Konfigurator, sondern mit Projektbibliothek „WagoAppPlcModbus Library“ !!)


*Was wurde bereits gemacht?*
  Über *e!COCKPIT* habe ich bereits eine *„Modbus TCP“-Kommunikation* mit dem *Modbus-Konfigurator* aufgebaut. Das Auslesen eines PT100 Sensors vom Feldbuskoppler -352 hat einwandfrei funktioniert. Nun möchte ich aber gerne die *Modbus-Konfiguration* über die *Projektbibliothek* einbinden – da scheitere ich aber aktuell an der Umsetzung. Ich möchte die Modbus Kommunikation über die Bibliothek umsetzen, da die Slave-Zieladresse für die Steuerung variieren kann. Der Temperaturwert des PT100 auf Kanal 1 (siehe I/O-Systeme -> HM001 -> 2: __2AI__Pt100_RTD -> AI1) soll ausgelesen und via Modbus vom Feldbuscontroller eingelesen werden.


*Programmierung in „PLC_PROG“ *(Abbildung, siehe Anhang)
Netzwerk 1: Einlesen des Programmbausteins „PRG_FbMbMasterTCP“
  Netzwerk 2: Mappen der Analogeingangskarte „750-492“ auf die Modbus Registeradresse „PRG_FbMbMasterTCP.awInputRegisters_RO[300].1“ 
(-> Ist das überhaupt das richtige Register für das Einlesen eines AIs? Das Netzwerk 2 gilt nur als Gedankenstütze, wie ich vorgehen soll. Das Auslesen des Analogwertes kann ich natürlich nicht nur eine 1 Bit-Variable deklarieren)

  Netzwerk 3: Wertevergleich: Steigt die Temperatur auf größer 30°C -> Schalte digitale Ausgangsklemme auf Feldbuscontroller






*Fragen:*
1.       Wo stelle ich die Slave-Zieladresse für den Feldbuskoppler ein? Das ist mir aktuell überhaupt nicht klar – auch über die Dokumentation in e!COCKPIT oder die Bibliothek „WagoAppPlcModbus“ konnte ich da keine hilfreichen Details finden. Die Slave Adresse soll über _bUnitId_MbServer_Tcp_ (Standardmäßig „BYTE :=1;“) konfiguriert werden. Meine Zieladresse auf dem Slave ist „196.168.1.101“, der Controller hat die „.100“.

2.       Wie schreibe (vom Feldbuscontroller -> Koppler) bzw. lese ich (vom Koppler -> Feldbuscontroller) Werte aus? Die Begriffe _axDiscreteInputs, axCoils, awInputRegisters und awHoldingRegisters_ im "FbMbSimpleServerTcp"-Baustein, sind die Register in die ich schreiben/lesen kann. Aber wie schreibe ich einen Temperaturwert in das Register und lese es vom Controller wieder aus? 


3.       Gibt es eine detailliertere Dokumentation zur Konfigurierung von e!COCKPIT mit Modbus TCP, abgesehen von dem e!COCKPIT Handbuch oder der Dokumentation der Projektbibliothek? 


Vielen Dank im Voraus.


----------



## Thruser (26 März 2018)

Hallo,

ich kapier zwar nicht warum Du da alles zu Fuß machen willst und nicht mit dem Konfigurator.

Habe nur mal kurz in das Projekt geschaut. Du verwendest dort den falschen Baustein. Der Baustein FbMbSimpleServerTcp ist für einen Modbus Slave, daher kannst Du dort auch keine Adresse vorgeben. Bei Modbus spricht man normalerweise nicht von Master und Slave, sondern von Server (Slave) und Client (Master). Bei Wago haben die jetzt beides gemischt in der Biliothek. Wenn Du richtig schaust, siehst Du, daß FbMbSimpleServerTcp unter der Kategorie '30 Simple Modbus Slave' eingetragen ist. In der Dokumentation zum Baustein steht auch: This function block provides Modbus TCP Slave functionality
Du mußt FbMbMasterTcp nehmen und die Struktur typMbQuery entsprechend füllen

Auf welcher Adresse jetzt die einzelnen Eingänge liegen mußt Du aus dem Handbuch zum 352 rauslesen, Stichwort Modbus Register Mapping. Zum Testen der Adressen kannst Du ja auch erst einmal einen Windowsrechner nehmen und z.B. qmodmaster. Damit kann man mal schnell etwas von einem Modbus Slave/Server abfragen und testen.

Deine erste Temperatur müßtest Du aber über Funktionscode 4, Adresse 0 lesen können (%IW0). Die zweite Temperatur ist dann auf Adresse 1, der erste Stromeingang Adresse 2 und der 2. auf 4. 
Die beiden digitalen Eingänge liest Du über die Coil Funktionen ein (Funktionscode 2)

GruÃŸ


----------



## aki09 (28 März 2018)

Thruser schrieb:


> Hallo,
> 
> ich kapier zwar nicht warum Du da alles zu Fuß machen willst und nicht mit dem Konfigurator.



Ich möchte im weiteren Verlauf ein Netzwerk aus mehreren Feldcontrollern (z.B. 5) mit je einem Feldbuskoppler aufbauen. Ein System aus Feldbuscontroller (Client) und Koppler (Server) ist die Standard Konfiguration. Diese Systemkonfiguration soll standardmÃ¤ÃŸig laufen. Nun soll es aber möglich sein, dass man z.B. folgendes tut:
- 5 Systeme (jeweils 1x Controller (Client) + 1x Koppler (Server)) laufen
- 2 Systeme werden gestoppt und sollen umkonfiguriert werden, die anderen 3 Systeme sollen davon aber unberÃ¼hrt bleiben und weiter laufen 
- Neue Systemkonfiguration: 2 Feldbuscontroller (ein Controller parametriert als Client, einer als Server) + 1 Koppler (Server) kommunizieren jetzt miteinander (2. Koppler ist nicht im System)

Das alles möchte ich später über eine visualisierte BedieneroberflÃ¤che parametrierbar machen - die Slave-Zieladressen der Clients über die Weboberfläche neu konfigurieren.




> Habe nur mal kurz in das Projekt geschaut. Du verwendest dort den falschen Baustein. Der Baustein FbMbSimpleServerTcp ist für einen Modbus Slave, daher kannst Du dort auch keine Adresse vorgeben. Bei Modbus spricht man normalerweise nicht von Master und Slave, sondern von Server (Slave) und Client (Master). Bei Wago haben die jetzt beides gemischt in der Biliothek. Wenn Du richtig schaust, siehst Du, dass FbMbSimpleServerTcp unter der Kategorie '30 Simple Modbus Slave' eingetragen ist. In der Dokumentation zum Baustein steht auch: This function block provides Modbus TCP Slave functionality



Danke fÃ¼r den Hinweis - funktioniert jetzt zumindest mal ohne Fehler. Als sHost muss ich dann aber meinen Client (192.168.1.100) angeben, richtig? Den FbMbSimpleServerTcp Baustein benÃ¶tige ich also nur, wenn ich einen Feldbuscontroller als Slave parametrieren möchte?




> Du musst FbMbMasterTcp nehmen und die Struktur typMbQuery entsprechend füllen



Und genau an dem Punkt hÃ¤nge ich gerade. Folgende Einstellungen habe ich hier vorgenommen:
_utQuery        : typMbQuery := (    bUnitId         := 0,       // Slaveaddress              -> Was muss hier hin? Slave Adresse ist: 192.168.1.101
                                        bFunctionCode   := 16#04,   // Read Input Registers      
                                         -> Das entspricht FC4. Und wie konfiguriere ich die Zeilen, wenn ich z.B. sowohl FC16 (Write Multiple Registers) und FC4 (Read Input registers) nutzen mÃ¶chte?
                                        uiReadAddress   := 0, // Startaddress := %IW0
                                        uiReadQuantity  := 10, // Quantity of wanted registers    -> Anzahl der IWs, also Anzahl der benÃ¶tigen Register, die auf meinem Koppler ausgelesen werden mÃ¼ssen?!
                                        uiWriteAddress  := 512,  // not needed for FC4
                                        uiWriteQuantity := 10,       // not needed for FC4
                                        awWriteData     := [124(0)] // not needed for FC4_
Mein Programm lÃ¤uft jetzt ohne Fehlermeldung (allerdings ohne das ich Werte auslesen kann) und ich habe vermutet, dass die Werte meiner Eingangskarte (PT100, %IW0) unter utresponse -> awData (ARRAY[0..124] -> ARRAY[0]) liegen. Da finde ich aber nur Null (ist auch mit awWriteData := [124(0)] initialisiert worden)!




> Deine erste Temperatur müßtest Du aber über Funktionscode 4, Adresse 0 lesen können (%IW0). Die zweite Temperatur ist dann auf Adresse 1, der erste Stromeingang Adresse 2 und der 2. auf 4.
> Die beiden digitalen EingÃ¤nge liest Du Ã¼ber die Coil Funktionen ein (Funktionscode 2)



Mir ist leider nicht ganz klar, wie ich Modbus-Nachrichten unter dem typMbQuery konfiguriere. Klar ist mir, dass Analog vor Digital kommt und Eingang vor Ausgang und das abhÃ¤ngig von der Hardwarereihenfolge, in die Register geschrieben wird. Leider fehlt mir aber das VerstÃ¤ndnis wie ich genau eine Modbus-Nachricht vom Server auslese (Wert vom PT100) und einen Ausgang auf dem Client schreibe (z.B. AO). Auch Anwendungsbeispiele konnte ich dazu nicht in der Dokumentation finden. 

Mein Ã¼berarbeites Projekt habe ich nochmal im Anhang beigefÃ¼gt. 

Beste GrÃ¼ÃŸe und danke schon fÃ¼r die Starthilfe.


----------



## Thruser (28 März 2018)

Hi,




aki09 schrieb:


> Danke fÃ¼r den Hinweis - funktioniert jetzt zumindest mal ohne Fehler. Als sHost muss ich dann aber meinen Client (192.168.1.100) angeben, richtig?


Nein, sHost ist der Server/Slave, also der Koppler 352 (Master=Client=PFC100; Slave=Server=352).




> Den FbMbSimpleServerTcp Baustein benötige ich also nur, wenn ich einen Feldbuscontroller als Slave parametrieren möchte?


Sieht so aus, aber wahrscheinlich wäre es leichter dies in der Gerätestruktur über den Modbus Slave Tab einzustellen.




> Und genau an dem Punkt hÃ¤nge ich gerade. Folgende Einstellungen habe ich hier vorgenommen:
> utQuery : typMbQuery := ( bUnitId := 0, // Slaveaddress -> Was muss hier hin? Slave Adresse ist: 192.168.1.101


Sollte bei TCP/UDP Verbindung egal sein, wird normalerweise nur bei RTU verwendet. Die Slaveadresse wird wie schon oben geschrieben bei sHost angegeben.


> bFunctionCode := 16#04, // Read Input Registers
> -> Das entspricht FC4.


OK


> uiReadAddress := 0, // Startaddress := %IW0


OK, also Start mit dem ersten Register (erster analoger Kanal)


> uiReadQuantity := 10, // Quantity of wanted registers -> Anzahl der IWs, also Anzahl der benÃ¶tigen Register, die auf meinem Koppler ausgelesen werden mÃ¼ssen?!


Versuch erst einmal mit einem Register. Dann langsam steigern. Bei 10 Registern müßte es auch einen Fehler geben, btw. darauf solltest Du auch auswerten. Bei Dir dürften da zur Zeit max. nur 5 Register ansprechbar sein: 0 und 1 die beiden Kanäle der Temperaturkarte, 2 und 3 die 4-20mA KArte und 4 dann noch zusätzlich die digitale Eingangskarte. Wenn ich es richtig lese werden die dig. Ein-/Ausgänge nämlich auch nochmal als Register hinten drangehangen, sodaß an auch über die Registerfunktionen  und nicht nur über die Coilfunktionen darauf zugreifen kann.


> uiWriteAddress := 512, // not needed for FC4


OK, aber vielleicht besser auf 0 setzen.


> uiWriteQuantity := 10, // not needed for FC4


OK, aber vielleicht besser auf 0 setzen.


> awWriteData := [124(0)] // not needed for FC4


OK


> Mein Programm lÃ¤uft jetzt ohne Fehlermeldung (allerdings ohne das ich Werte auslesen kann) und ich habe vermutet, dass die Werte meiner Eingangskarte (PT100, %IW0) unter utresponse -> awData (ARRAY[0..124] -> ARRAY[0]) liegen. Da finde ich aber nur Null (ist auch mit awWriteData := [124(0)] initialisiert worden)!


Eigentlich sollten die Daten dort liegen. Meldet den xlsOpen True und xError wirklich False?

Ich weiß nicht ob die Pt100 Karte noch speziell über I/O Check konfiguriert wird, wurde das gemacht?

Versuche sonst mal mit dem Programm qModMaster (https://sourceforge.net/projects/qmodmaster/) unter Windows auf den Koppler zuzugreifen. Damit kann man schnell mal die verschiedenen Adressen ausprobieren und man sieht auch wann es Fehlermeldungen gibt.


Ansonsten versuch auch erst mal die Adresse 16#2000 (8192) mit Anzahl 9 Registern zu lesen. Als Antwort solltest Du folgendes erhalten:
16#0000
16#FFFF
16#1234
16#AAAA
16#5555
16#7FFF
16#8000
16#3FFF
16#4000


siehe auch Handbuch Kapitel 11.2.5.6 Konstantenregister




> Und wie konfiguriere ich die Zeilen, wenn ich z.B. sowohl FC16 (Write Multiple Registers) und FC4 (Read Input registers) nutzen mÃ¶chte?


Du mußt mehrere Query Strukturen ausführen und die dann nacheinander abarbeiten.




> Mir ist leider nicht ganz klar, wie ich Modbus-Nachrichten unter dem typMbQuery konfiguriere. Klar ist mir, dass Analog vor Digital kommt und Eingang vor Ausgang und das abhÃ¤ngig von der Hardwarereihenfolge, in die Register geschrieben wird. Leider fehlt mir aber das VerstÃ¤ndnis wie ich genau eine Modbus-Nachricht vom Server auslese (Wert vom PT100) und einen Ausgang auf dem Client schreibe (z.B. AO). Auch Anwendungsbeispiele konnte ich dazu nicht in der Dokumentation finden.


Wie gesagt, Du mußt mehrere Query Strukturen erstellen. Am besten über ein Array. Nennen wir das mal eine Jobliste. Zusätzlich wird dann eine dazugehörige Antwortliste benötigt

```
Joblist: ARRAY[0..1] of typMbResponse :=  //JOB 0 Konstantenregister lesen
                                          (bUnit := 0,
                                           bFunctionCode := 16#04,
                                           uiReadAddress := 16#2000,
                                           uiReadQuantity := 9,
                                           uiWriteAddress := 0,
                                           uiWriteQuantity := 0,
                                           awWriteData := [124(0)]
                                          ), //JOB 1 MAC Adresse lesen
                                          (bUnit := 0,
                                           bFunctionCode := 16#04,
                                           uiReadAddress := 16#1031,
                                           uiReadQuantity := 3,
                                           uiWriteAddress := 0,
                                           uiWriteQuantity := 0,
                                           awWriteData := [124(0)]
                                           )
                                          ;
ResponseList: ARRAY[0..1] of typMbResponse;
```


Dazu benötigst Du dann eine Zahlvariable, z.B. i, dann rufst Du den Baustein mit Joblist_ als utQuery und ResponseList als utResponse auf. Zusätzlich mußt Du dann die Variable i nach erfolgreichem Ausführen des Bausteins (fallende Flanke xTrigger) inkrementieren und dann wenn alle Jobs durch sind wieder auf 0 setzen.


So müßte das klappen, habe meine Controller gerade nicht aufegbaut um das zu testen. Hatte vorletzte Woche nur mit einem 352 und qModMaster etwas rumgespielt.


Gruß

/EDIT: Array Initialisierung berichtigt_


----------



## aki09 (11 April 2018)

Hallo nochmal,

@Thruser: Deine Anleitung hat super funktioniert und somit konnte ich die Temperaturwerte vom Feldbuskoppler bereits einlesen. Wie das funktioniert habe ich nun auch verstanden.
Nun habe ich mich in den letzten 2 Wochen mit anderen Themen auseinandersetzen müssen und somit konnte ich erst seit gestern wieder weitermachen. Zur Zeit versuche ich die Ausgangsklemmen
meines Feldbuskopplers anzusteuern. Dafür habe ich eine DO-Klemme hinzugefügt - die Umsetzung hat aber bisher überhaupt nicht funktioniert.



> Ansonsten versuch auch erst mal die Adresse 16#2000 (8192) mit Anzahl 9 Registern zu lesen. Als Antwort solltest Du folgendes erhalten:
> 16#0000
> 16#FFFF
> 16#1234
> ...



Check!



> > Mir ist leider nicht ganz klar, wie ich Modbus-Nachrichten unter dem typMbQuery konfiguriere. Klar ist mir, dass Analog vor Digital kommt und Eingang vor Ausgang und das abhÃ¤ngig von der Hardwarereihenfolge, in die Register geschrieben wird. Leider fehlt mir aber das VerstÃ¤ndnis wie ich genau eine Modbus-Nachricht vom Server auslese (Wert vom PT100) und einen Ausgang auf dem Client schreibe (z.B. AO). Auch Anwendungsbeispiele konnte ich dazu nicht in der Dokumentation finden.
> 
> 
> Wie gesagt, Du mußt mehrere Query Strukturen erstellen. Am besten über ein Array. Nennen wir das mal eine Jobliste. Zusätzlich wird dann eine dazugehörige Antwortliste benötigt



Die grundsätzliche Idee habe ich verstanden. Es gibt Konstantenregister und andere feste Register (z.B. MAC Adresse lesen o.Ä.), die feste HEX Werte zurückgeben (mit QModmaster probiert - hat geklappt!). 
Wie ich den Aufbau der Job- und Responselist aufbauen soll, ist mir aber unklar.

*Meine Fragen dazu:*
1. bFunctionCode := 16#04 ist ein Eingangsregister, bei dem man eine Anzahl von Eingangsworten lesen kann (d.h. Analogeingänge! Oder auch Digitaleingänge?!). 
Die Funktion 01 liest Ein- und Ausgangsbits. Entsprechend lediglich Digitalein- und Ausgänge? Habe ich das so richtig verstanden?

2. uiReadAddress: Wenn ich keine Konstantregister oder MAC-Adressen auslesen will (wie in deinem Beispiel) - muss ich hier die Adressen meiner Eingangskarten (Digital-/Analogeingänge) festlegen, die ich später über die Funktion auslesen will?

3. uiWriteAddress: Muss bei Funktionscode, bei denen nur Eingangsregister eingelesen werden, mit 0 deklariert werden!?

4. Du meintest, dass ich je ein Array "Joblist" und "ReponseList" erstellen sollte und dann mittels Laufvariable den Baustein aufrufen soll. 
Das Array Joblist muss ich also an den Baustein utQuery vom Masterbaustein - das Array Responselist entsprechend an utResponse des Bausteins deklarieren? 

5. Was ich grundsätzlich nicht verstehe ist, dass ich die Werte für die Analogwerte wirklich einfach auslesen kann (über PRG_FbMbMasterTCP.utResponse.awData[0] für Kanal 1 bzw. awData[0] für Kanal 2 der 1. Analogeingangskarte). Jetzt habe ich auf dem Feldbuskoppler eine DO Karte. Ich habe versucht mit Kanal 1 der Digital-Ausgangskarte versucht das Register RG_FbMbMasterTCP.awWriteData[512].0 auf 1 zu setzen (Kanal 1 sollte eingeschaltet werden, Startadresse des Schreibregisters habe ich mit 512 angegeben). Das funktioniert aber nicht (Fehlermeldung: utQuery ist kein Eingang von 'PRG_FbMbMasterTCP'). Was mache ich falsch? 

6. Ich habe ein Problem mit der Firmware zwischen Projekt- und Controller. Jedes mal, wenn ich das Projekt neu öffne, muss ich die Firmware unter den Gerätedaten des Controllers wieder auf 20(09) ändern (siehe Anhang). Wie kann ich das Problem beheben?


Wieder eine Menge an Fragen, die sich angesammelt haben. Ich hoffe trotzdem verständlich.


----------



## Thruser (11 April 2018)

HAllo,


aki09 schrieb:


> Hallo nochmal,
> 
> 
> @Thruser: Deine Anleitung hat super funktioniert und somit konnte ich die Temperaturwerte vom Feldbuskoppler bereits einlesen. Wie das funktioniert habe ich nun auch verstanden.


schön zu lesen.



> Die grundsätzliche Idee habe ich verstanden. Es gibt Konstantenregister und andere feste Register (z.B. MAC Adresse lesen o.Ä.), die feste HEX Werte zurückgeben (mit QModmaster probiert - hat geklappt!).
> Wie ich den Aufbau der Job- und Responselist aufbauen soll, ist mir aber unklar.


Ich versuch mal etwas fertig zu machen. Sehe aber  auch gerade das ich da oben ein Fehler gemacht habe. Die Joblist muß natürlich als typMbQuery angelegt werden, nicht als typMbResponse.




> Meine Fragen dazu:
> 1. bFunctionCode := 16#04 ist ein Eingangsregister, bei dem man eine Anzahl von Eingangsworten lesen kann (d.h. Analogeingänge! Oder auch Digitaleingänge?!).
> Die Funktion 01 liest Ein- und Ausgangsbits. Entsprechend lediglich Digitalein- und Ausgänge? Habe ich das so richtig verstanden?


Ich habe es jetzt einmal ausprobiert, mit der Funktion lassen sich sowohl die Analog- und Digitaleingänge lesen. Als erstes kommen die komplexen (unter anderen analogen) Eingänge, dahinter dann die digitalen. Es ist egal ob man FC 3 oder FC 4 nimmt, s.a. Handbuch, mit beiden Funktionscodes werden dieselben Daten gelesen.


Ich habe hier jetzt einen Aufbau: 750-352 (Koppler), 750-602 (Einspeiseklemme), 750-400 (2 DI), 750-501 (2 DO), 750-472 (2 AI), 750-600 (Endklemme)

Mit FC 3/FC 4, Adresse 0 und Länge 3 Register bekommt man an Adresse 0 den ersten Kanal von der 472 (IW0), an Adresse 1 den zweiten Kanal (IW1) und an Adresse 2 dann die beiden digitalen Eingänge von der 400 (IW2). Der erste digitale Eingang liegt da auf Bit 0 der zweite auf Bit 1.
Auch den Zustand der Ausgänge läßt sich mit den beiden Funktionscodes auslesen. Die 501 liegt auf Adresse 512 (QW0).


Zusätzlich kann man die digitalen Ein- und Ausgänge noch über die Coil- (Bitzugriff-) Funktionen ansprechen. So kann man die Eingänge der 400 über FC 1/FC 2 ab Adresse 0 lesen. Adresse 0 ist der erste digitaler Eingang, Adresse 1 der zweite. Auch die Ausgänge lassen sich lesen, die 501 liegt auf den Adressen 512 und 513.


Ähnliches gilt für die Ausgänge. Die digitalen Ausgänge können über FC 6/FC 16 (Registerzugriff, 501 auf Adresse 512) und FC 5/FC 15 (Bitzugriff, Adresse 0 und 1) gesetzt werden.


Kannst Du auch schön mit qModMaster ausprobieren.




> 2. uiReadAddress: Wenn ich keine Konstantregister oder MAC-Adressen auslesen will (wie in deinem Beispiel) - muss ich hier die Adressen meiner Eingangskarten (Digital-/Analogeingänge) festlegen, die ich später über die Funktion auslesen will?


Ja, wie oben beschrieben. Der erste analoge (komplexe) Kanal liegt auf Adresse 0.



> 3. uiWriteAddress: Muss bei Funktionscode, bei denen nur Eingangsregister eingelesen werden, mit 0 deklariert werden!?


Wahrscheinlich nicht, wäre aber vielleicht besser. Mußt Du ausprobieren.



> 4. Du meintest, dass ich je ein Array "Joblist" und "ReponseList" erstellen sollte und dann mittels Laufvariable den Baustein aufrufen soll.
> Das Array Joblist muss ich also an den Baustein utQuery vom Masterbaustein - das Array Responselist entsprechend an utResponse des Bausteins deklarieren?


Fast richtig. Nicht das ganze Array, sondern nur ein ein Element davon. Daher die Zählvariable. 



> 5. Was ich grundsätzlich nicht verstehe ist, dass ich die Werte für die Analogwerte wirklich einfach auslesen kann (über PRG_FbMbMasterTCP.utResponse.awData[0] für Kanal 1 bzw. awData[0] für Kanal 2 der 1. Analogeingangskarte).


OK, schon mal gut.



> Jetzt habe ich auf dem Feldbuskoppler eine DO Karte. Ich habe versucht mit Kanal 1 der Digital-Ausgangskarte versucht das Register RG_FbMbMasterTCP.awWriteData[512].0 auf 1 zu setzen (Kanal 1 sollte eingeschaltet werden, Startadresse des Schreibregisters habe ich mit 512 angegeben). Das funktioniert aber nicht (Fehlermeldung: utQuery ist kein Eingang von 'PRG_FbMbMasterTCP'). Was mache ich falsch?


Da ist jetzt im Text etwas durcheinander geraten.
Du hast eine Variable utQuery vom Typ typMbQuery und eine Variable utResponse vom Typ typMbResponse erstellt? Die mußt Du dann den jeweiligen Eingängen vom FbMbMasterTCP zuweisen.


Wenn Du als Adresse 512 angibst (sollte die richtige sein für den Registerzugriff), dann mußt Du das Bit utQuery.awWriteData[0].0 setzen. Nicht awWriteData[512].0. 


Jetzt kommt es noch darauf an, über welchen Funktionscode Du den Ausgang ansprechen willst. Bit- oder Registerzugriff, ein oder mehrere Zugriffe mit einem Auftrag. Gerade bei den Bitzugriffen mußt Du das mal ausprobieren wo Du welche Daten reichschreiben mußt, da ich gerade nicht weiß wie Wago da die Daten im awWriteData organisiert. Beim Funktionscode 15 müssen die Daten nämlich Byteweise organsisiert werden. D.h. bis 8 Coils wird nur 1 Byte gesendet, bis 16 Coils 2 Byte also 1 Word, 24 Coils dann 3 Byte (1,5 Word) etc. Wago hat das Array aber auf Word aufgebaut, so daß man mal ausprobieren muß wo denn jetzt das Bit für den ersten Kanal abgelegt werden muß im unteren Teil awWriteData[0].0 oder im oberen awWriteData[0].8. Das müßte man aber auch aus der Antwort von mehreren Coils lesen ableiten können. 



> 6. Ich habe ein Problem mit der Firmware zwischen Projekt- und Controller. Jedes mal, wenn ich das Projekt neu öffne, muss ich die Firmware unter den Gerätedaten des Controllers wieder auf 20(09) ändern (siehe Anhang). Wie kann ich das Problem beheben?


Da kann ich jetzt nicht helfen. Aber Du könntest auch die neue Firmware auf den Controller aufspielen. Die kannst Du beim Wago Support anfordern.


Ich hoffe das hilft erst einmal. Wenn ich dazu komme versuche ich die Woche mal selbst ein Beispielprogramm zu schreiben und hier dann einfügen.


Gruß


----------



## aki09 (12 April 2018)

Hallo Thruser,

vielen Dank nochmal für deine schnelle Antwort und die ganzen detaillierten Antworten auf meine Fragen.



> Ich habe es jetzt einmal ausprobiert, mit der Funktion lassen sich  sowohl die Analog- und Digitaleingänge lesen. Als erstes kommen die  komplexen (unter anderen analogen) Eingänge, dahinter dann die  digitalen. Es ist egal ob man FC 3 oder FC 4 nimmt, s.a. Handbuch, mit  beiden Funktionscodes werden dieselben Daten gelesen.
> 
> 
> Ich habe hier jetzt einen Aufbau: 750-352 (Koppler), 750-602  (Einspeiseklemme), 750-400 (2 DI), 750-501 (2 DO), 750-472 (2 AI),  750-600 (Endklemme)
> ...



Dann bin ich zumindestens nicht ganz auf dem Holzweg. Verstanden, check!



> Kannst Du auch schön mit qModMaster ausprobieren.



Ausprobiert, verstanden, check!  


Ich habe jetzt folgendes im Quellcode implementiert:

*FOR-Schleife für Zählvariable
*// Ausprobiert, bei fallender Flanke geht iIncrement aber nicht auf TRUE. Der Rest soll den 1. Ansatz zeigen.

_FOR iCounter := 0 TO 1 BY 1 DO_
_FTRIG_0 (CLK:= xTxTrigger);
   iIncrement := FTRIG_0.Q
IF iIncrement := TRUE THEN_
_utQuery := Joblist ;                             // Hier muss ich utQuery mit dem __jeweiligen Array von Joblist ([0] oder [1]) _initialisieren, richtig?
   Responselist_ := utResponse;              // Rückmeldung wird ins Array Responselist geschrieben
   iCounter := iCounter + 1;                
END_IF;
END_FOR;
iCounter := 0;                                         // Nach Schleifendurchlauf, starte bei iCounter = 0_


*Initialisierung von Joblist und Responselist
*// Da erscheint einiges an Fehlermeldungen. Das soll aber mein aktuelles Verständnis zeigen 
_Joblist  : ARRAY[0..1] OF typMbQuery := (  bUnitId         := 0,       // Slaveaddress
                                   bFunctionCode   := 16#04,   // Read Input Registers
                                   uiReadAddress   := 0,       // Startaddress
                                   uiReadQuantity  := 5,      // Quantity of wanted registers --> 2x Analogeingänge mit IW0-4, 1x Digitaleingang IW5
                                   uiWriteAddress  := 0,       // not needed for FC4
                                   uiWriteQuantity := 0,       // not needed for FC4
                                   awWriteData     := [124(0)] // not needed for FC4
),                                   
(  
                                   bUnitId         := 0,       // Slaveaddress
                                   bFunctionCode   := 16#05,   // Read Input Registers
                                   uiReadAddress   := 0,       // Startaddress
                                   uiReadQuantity  := 0,      // Quantity of wanted registers
                                   uiWriteAddress  := 512,       // not needed for FC4
                                   uiWriteQuantity := 1,       // not needed for FC4 --> 1x Digitalausgang IW0
                                   awWriteData     := [124(0)] // not needed for FC4
                                   );                        
Responselist  : ARRAY[0..1] OF typMbResponse;


_Ich weiß nicht ob ich einfach nur ein schlechter Programmierer bin, mir die Erfahrung mit der Feldbuskommunikation fehlt oder ich das auf die fehlenden Anwendungsbeispiele für die Kommunikation über e!COCKPIT schieben soll. Vielleicht auch alles zusammen! Aktuell schaffe ich es zumindest nicht, mir aus dem Netz nützliche Informationen zu schnappen, trotz durchwälzen von Datenblättern usw.Die Hilfe hier ist auf jeden Fall gerade gold Wert.Beste Grüße, 
aki09


----------



## Thruser (12 April 2018)

Hi,


aki09 schrieb:


> Hallo Thruser,
> 
> vielen Dank nochmal für deine schnelle Antwort und die ganzen detaillierten Antworten auf meine Fragen.
> 
> ...


dann lohnt sich der Aufwand wenigstens 



> FOR-Schleife für Zählvariable
> // Ausprobiert, bei fallender Flanke geht iIncrement aber nicht auf TRUE. Der Rest soll den 1. Ansatz zeigen.
> 
> FOR iCounter := 0 TO 1 BY 1 DO
> ...


gleich mehrere Fehler!

Code am besten in CODE Tags (der Knop mit der Raute #) schreiben wegen der Darstellung. Die kursive Darstellung hilft aber auch schon einmal.

Dann die for Schleife zu verwenden ist falsch. Die wird in jedem Zyklus komplett abgearbeitet. Dazu kommt, daß das inkrementieren von iCounter so nicht notwendig ist, da der Wert autoinkrementiert wird durch die 'BY 1' Anweisung. So wird gleich von 0 auf 2 hochgezählt, die for Schleife wird also sogar nur einmal durchlaufen. Auch muß am Ende iCounter nicht unbedingt explizit auf 0 gesetzt werden, das passiert bereits bei der Initialisierung der Schleife, außer Du verwendest sie noch einmal.

Beim If statement machst Du auch eine Zuweisung und keinen Vergleich iIncrement = TRUE wäre richtig. Ich vermute jetzt aber auch noch, daß Du iIncrement als Integer deklariert hast, wegen des kleinen i am Anfang. Verwenden tust Du die Variable aber als bool.


```
FTRIG_0 (CLK:= xTxTrigger);       //auf fallende Flanke warten
IF FTRIG_0 THEN                   //wenn fallende Flanke kommt
  iCounter = iCounter +1;         //Jobcounter erhöhen
  IF iCounter > maxJobs THEN      //alle Jobs durch, dann wieder beim ersten anfangen
    iCounter := 0;
  END_IF
  xTxTrigger := TRUE;             //nächste Anfrage/Job starten
END_IF

FbMbMasterTCP Bausteinaufruf (utQuery=>Joblist[iCounter], utResponse=>Responselist[iCounter], ...) //genauen Aufruf mußt Du gucken
```
versuch das mal so. Als maxJobs mußt Du die höchste Zahl der Jobs verwenden, hier in dem Beispiel 1. Man könnte auch mit Modulo arbeiten, dann fällt da die weitere If Abfrage weg.



> Initialisierung von Joblist und Responselist
> // Da erscheint einiges an Fehlermeldungen. Das soll aber mein aktuelles Verständnis zeigen
> Joblist : ARRAY[0..1] OF typMbQuery := ( bUnitId := 0, // Slaveaddress
> bFunctionCode := 16#04, // Read Input Registers
> ...


Muß ich mal selbst aufbauen am System, kann ich aber erst heute abend. awWriteData kommt mir noch etwas komisch vor.



> Ich weiß nicht ob ich einfach nur ein schlechter Programmierer bin, mir die Erfahrung mit der Feldbuskommunikation fehlt oder ich das auf die fehlenden Anwendungsbeispiele für die Kommunikation über e!COCKPIT schieben soll. Vielleicht auch alles zusammen! Aktuell schaffe ich es zumindest nicht, mir aus dem Netz nützliche Informationen zu schnappen, trotz durchwälzen von Datenblättern usw.Die Hilfe hier ist auf jeden Fall gerade gold Wert.Beste Grüße,
> aki09



Sagen wir mal fehlende Erfahrung, obwohl mit der for Schleife hast Du Dich ziemlich vertan. Rest ist einfach daß man Erfahrung benötigt wie man mit Aktionen arbeitet, die über mehrere Zyklen gehen. Zusätzlick kommt hier jetzt noch dazu wie man die verschiedenen Baugruppen am entfernten Knoten anspricht.

Gruß


----------



## Thruser (12 April 2018)

So,

habe etwas Zeit gefunden das jetzt mal umzusetzen.

Meine Kofiguration (Module) ist noch die gleiche wie oben. Mein Master ist eine 750-8204, mit der 750-8101 sollte es aber keinen Unterschied machen. Ich schreibe das jetzt mal direkt hier in ST rein, anstatt es als Projekt hochzuladen.

PLC_PRG:

```
PROGRAM PLC_PRG
VAR
    Ain1:WORD;
    Ain2:WORD;
    Din1:BOOL;
    Din2:BOOL;
    Din:WORD;
    
END_VAR

// Unterprogramm ModbusMaster aufrufen
ModbusMaster();

//Antworten der Eingänge auf lokale Variable kopieren
Ain1:=ModbusMaster.Responseliste[0].awData[0];
Ain2:=ModbusMaster.Responseliste[0].awData[1];
Din:=ModbusMaster.Responseliste[0].awData[2];
Din1:=ModbusMaster.Responseliste[0].awData[2].0;
Din2:=ModbusMaster.Responseliste[0].awData[2].1;

//Setzen von Ausgängen am entfernten Knoten
//um mehrere Ausgänge zu setzen etwas rumgespiele:
//beide digitalen Eingänge = 0 (0) -> erster Ausgang = 1, zweiter = 0 (1)
//erster dig. Eingang =1, zweiter = 0 (1) -> erster Ausgang = 0, zweiter = 1 (2)
//erster dig. Eingang =1, zweiter = 1 (3) -> erster Ausgang = 0, zweiter = 0 (4->0)
//erster dig. Eingang =0, zweiter = 1 (2) -> erster Ausgang = 1, zweiter = 1 (3)

ModbusMaster.Jobliste[1].awWriteData[0]:=Din+1;
```

ModbusMaster (PRG)

```
PROGRAM ModbusMaster
VAR_INPUT
    Jobliste: ARRAY [0..1] OF typMbQuery := [ 
        ( //JOB 0, Eingänge lesen (Registerzugriff, inklusive digitale Eingänge)
            bUnitId:= 1, 
            bFunctionCode:= 4, 
            uiReadAddress:= 0, 
            uiReadQuantity:= 3, 
            uiWriteAddress:= 0, 
            uiWriteQuantity:= 0, 
            awWriteData:= [125(0)]
        ),
        ( //JOB 1, Ausgänge schreiben (Registerzugriff)
            bUnitId:= 1, 
            bFunctionCode:= 16, 
            uiReadAddress:= 0, 
            uiReadQuantity:= 0, 
            uiWriteAddress:= 512, 
            uiWriteQuantity:= 1, 
            awWriteData:= [125(0)]
        )];
END_VAR
VAR
    iJobCounter:INT:=0;
    xTrigger:BOOL:=TRUE;
    FB_F_Trig:F_Trig;
    
    Responseliste:ARRAY [0..1] OF typMbResponse;
            
    FB_ModbusMasterTCP:FbMbMasterTcp;
END_VAR

//Aufruf des Kommunikationsbausteins
FB_ModbusMasterTCP (xConnect:=TRUE, 
                    sHost:='192.168.12.114', 
                    wPort:=502, 
                    eFrameType:=eMbFrameType.Ethernet, 
                    tTimeOut:=T#2S, 
                    utQuery:=Jobliste[iJobCounter], 
                    xTrigger:=xTrigger, 
                    utResponse:=Responseliste[iJobCounter]);

//Auf Jobende warten
FB_F_Trig (clk:=xTrigger);

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

Um die Daten in das ModbusMaster Programm schreiben zu können mußte ich die Jobliste als Input deklarieren. Für den JobCounter wollte ich keine If Abfrage nehmen, daher der Modulo. Bei mehr Jobs muß der Moduloteiler entsprechend angepaßt werden.

Da läßt sich noch so einiges verbessern, gerade auch mit den zuweisen der Daten, aber so hast Du jetzt mal ein Anhaltspunkt.

Wie Du siehst habe ich weiter oben auch ein Fehler mit dem Jobliste Array gemacht. Das ganze mußte mit in die eckigen Klammern. So läuft es aber bei mir.

Gruß


----------



## aki09 (13 April 2018)

Hallo Thruser,

vielen Dank für die ganze Mühe, die du dir hier gemacht hast. Ich habe mir deine Programmierung mal angesehen und auf meine Steuerung übertragen - funktioniert einwandfrei. 
Mit dem Grundkonzept bin ich zumindest jetzt auf einem Stand, um eigenständig daran weiterarbeiten zu können. 

Vielen Dank dafür und ein schönes Wochenende!


----------



## aki09 (20 April 2018)

** hier sollte eigentlich keine neue Antwort stehen **


----------



## aki09 (26 April 2018)

Hallo nochmal,

mittlerweile bin ich schon ein paar Schritte weiter. Die Kommunikation zwischen Feldbuscontroller und Koppler läuft und ist soweit programmiert. Jetzt möchte ich eine weitere Modbus Verbindung zwischen einem "Nicht-Wago"-Modbusteilnehmer und dem Controller aufbauen und dafür die Eingänge und Ausgänge des Controllers schreiben / lesen.

Dafür habe ich den Baustein "mySimpleTCPServer" verwendet in denen die folgenden Variablen initialisiert werden:

```
// Programm: ModbusSlave
    //------------------------------------------------------------------------------------
    myDiscreteInputs    :   ARRAY[0..20] OF BOOL;       // Modbus bit address 0 .. 20
    myCoils             :   ARRAY[0..20] OF BOOL;       // Modbus bit address 0 .. 20
    myInputRegisters    :   ARRAY[0..20] OF WORD;    // Modbus word address 0 .. 20
    myHoldingRegisters  :   ARRAY[0..20] OF WORD;       // Modbus word address   0 .. 20
    //------------------------------------------------------------------------------------
```

Soweit ich verstanden habe, kann ich die Klemmen des Controllers mit den 4 Registern (DiscreteInputs, Coils, InputRegisters, HoldRegisters) deklarieren. Dafür habe ich folgenden Code geschrieben:


```
// Digitale Ausgänge
IoConfig_Globals_Mapping.xOut11 := ModbusSlave.myCoils[0];
IoConfig_Globals_Mapping.xOut12 := ModbusSlave.myCoils[1];
IoConfig_Globals_Mapping.xOut21 := ModbusSlave.myCoils[2];
IoConfig_Globals_Mapping.xOut22 := ModbusSlave.myCoils[3];

// Analoge Eingänge
xTemp11 := INT_TO_WORD(IoConfig_Globals_Mapping.xTemp11);     --> hab versucht mir das "zurechtzufuschen", aber auch das klappt nicht :eek:
ModbusSlave.myInputRegisters[0] := IoConfig_Globals_Mapping.xTemp11;
```

1. Die digitalen Ausgänge kann ich damit schreiben *(-> OK)*. Ist diese Art der Initialisierung denn auch so vorgesehen, dass ich das myCoil[0..3] mit je einem Digitalausgang der Klemmen verknüpfe?
2. Beim Auslesen der Temperaturen ist die Klemmenvariable "IoConfig_Globals_Mapping.xTemp11" als INT deklariert (vorzeichenbehaftet, Klemme ist ein AI für Temperatur TYP K Messung), das Register  "ModbusSlave.myInputRegisters[0]" aber als WORD. Ich kann mir beim besten Willen nicht vorstellen, dass man die Klemmen mit den Registern auf diese Art und Weise deklariert. Zudem funktioniert das Auslesen der Temperatureingänge mit diesem Programmcode auch nicht. Fehler:_ "PLC_PRG: C0037: 'myInputRegisters' ist kein Eingang von 'ModbusSlave' _*(-> NOK)

*Beste Grüße


----------



## Thruser (27 April 2018)

Hallo,

groß weiterhelfen kann ich Dir da jetzt auch erst einmal nicht. Das müßte ich mir auche erst einmal ansehen. Habe sowieso noch nicht viel mit e!cockpit gemacht.

Sehe ich das richtig, daß Du jetzt von einem anderen Gerät per Modbus auf den Controller zugreifen willst? Der Controller jetzt also als Slave/Server arbeiten soll?

Ich würd da jetzt mal den entsprechenden Teil des Konfigurator nehmen.

Ansonsten kann ich mir das erst zum Ende nächster Wocheansehen.



aki09 schrieb:


> Zudem funktioniert das Auslesen der Temperatureingänge mit diesem Programmcode auch nicht. Fehler:_ "PLC_PRG: C0037: 'myInputRegisters' ist kein Eingang von 'ModbusSlave' _*(-> NOK)*


Du hast myInputRegisters wahrscheinlich nicht als VAR_INPUT deklariert. Das hatte ich damals auch bei der Jobliste. 

Gruß


----------



## aki09 (8 Mai 2018)

Hallo zusammen,

mittlerweile bin ich ein paar Schritte weitergekommen. Jetzt fehlt mir aber noch ein Punkt, für die ich noch keine Lösung gefunden habe.

*Rückfrage zum gleichzeitigen Schreiben/Lesen von Modbus Registern*
Ich benötige ein Feedback für die digitalen/analogen Ausgänge von meinem externen Endgerät. Hintergrund ist, dass ich sowohl vom Endgerät, als auch vom PFC die Ausgänge steuern möchte und dafür ein Feedback ("Ist der Ausgang jetzt an oder aus?!") benötige. Im Handbuch habe ich dafür folgenden Hinweis gefunden:

Hinweis: _„Lesen und Schreiben der Ausgänge bei FC1 bis FC4 auch durch Hinzuaddieren eines Offsets möglich! Bei den *Lesefunktionen (FC1 ... FC4) können Sie zusätzlich die Ausgänge schreiben und zurücklesen*, indem Sie für Adressen in dem Bereich  [0 hex ... FF hex] ein Offset von 200hex (0x0200) und für Adressen in dem Bereich [6000 hex ... 62FChex] ein Offset von 1000hex (0x1000) zu der MODBUS-Adresse hinzu addieren“

_*Meine Frage:
*Wie schaffe ich es, dass ich auf *ein Modbus Register* *sowohl lesen, als auch schreiben* kann? Wie ist das mit dem Offset genau gemeint? Mein Lösungsansatz, bei dem ich aber ab Adresse 0 schreibe und ab Adresse 512 lese (=> Offset von 0 + 512 = 512?):


```
PROGRAM ModbusMaster
VAR
FB_ModbusMasterTCP:FbMbMasterTcp;
       Jobliste: ARRAY [0..1] OF typMbQuery := [ 
        ( //JOB 0, Eingänge lesen (Registerzugriff, inklusive digitale Eingänge)
            bUnitId:= 1, 
            bFunctionCode:= 4,   // FC4 'Read Input Registers' -> Lesen der digitalen und analogen Eingänge
            uiReadAddress:= 0, 
            uiReadQuantity:= 3, 
            uiWriteAddress:= 0, 
            uiWriteQuantity:= 0, 
            awWriteData:= [125(0)]
        ),
        ( //JOB 1, Ausgänge schreiben/lesen (Registerzugriff)
            bUnitId:= 1, 
            bFunctionCode:= 1,     // FC1 'Force Multiple Coils' -> Schreiben & Lesen der Register
            uiReadAddress:= 512, 
            uiReadQuantity:= 4, 
            uiWriteAddress:= 0, 
            uiWriteQuantity:= 4, 
            awWriteData:= [125(0)]
        )];
        Responseliste:ARRAY [0..1] OF typMbResponse;
    
        // Um Array "Jobliste" und "Responseliste" zu triggern     
        iJobCounter:INT:=0;
        xTrigger:BOOL:=TRUE;
        FB_F_Trig:F_Trig;
END_VAR
```

Bei der Modbus Verbindung zwischen meinem externen Endgerät und einem 352 lässt sich das alles in einem Register (schreiben/lesen) umsetzen. Von meinem externen Endgerät wird das auch sofort erkannt, wenn der Zustand sich ändert. Was muss ich dafür beim PFC tun, damit ich das Feedback dort auch bekomme?


----------



## aki09 (11 Mai 2018)

Ich sehe da aktuell ein Problem beim Aufbau des Netzwerks, wodurch ich *Probleme mit dem "Feedback" der digitalen und analogen Ausgänge bekomme*. Ich vermute, dass ich bei dem derzeitigen Programmaufbau grundlegend etwas  falsch gemacht habe und ich da erstmal ansetzen muss. 

*Was habe ich gemacht?*
3 POUs habe ich derzeit eingebunden (PLC_PRG,  ModbusMaster mit dem ModbusMasterTCP-Funktionsbaustein, ModbusSlave mit  dem SimpleTCPServer-Funktionsbaustein). 

Folgende Idee steckt dahinter:
- Externes Endgerät soll als *Master* gegenüber PFC und FC arbeiten
- PFC soll als* Slave* zum externen Endgerät arbeiten und als *Master *zum Koppler
- Sowohl PFC, als auch Endgerät sollen Ausgänge steuern (Endgerät ist höher priorisiert!)

Was funktioniert und was nicht?
- Das Lesen der digitalen/analogen Eingänge funktioniert einwandfrei * -> OK*
- Das Schreiben der digitalen/analogen Ausgänge (auf PFC) funktioniert, wenn ich vom Endgerät aus schreibe und/oder vom PFC (über e!COCKPIT). In e!COCKPIT sehe ich auch die Zustandsänderung des Ausgangs, wenn ich vom Endgerät aus schalte.* -> OK
*- Auf externem Endgerät sehe ich *keine Zustandsänderung*, wenn ich von e!COCKPIT die Ausgänge ansteuere. Weder bei den Ausgängen des PFCs, noch bei den des FCs (Feedback fehlt!).* -> NOK*
- Das Schreiben vom Endgerät auf FC funktioniert prinzipiell, allerdings setzt mein Programm den Ausgang direkt wieder zurück. Ich habe keine Ahnung, wie ich das verhindern kann. * -> NOK*

Ich verzweifle gerade wirklich an diesem fehlendem Feedback und das ich vom externen Endgerät nicht die Ausgänge des FCs vernünftig anteuern kann.

Ansatz zur Problemlösung
Ich würde versuchen vom Endgerät in Zukunft in die Merker-Register zu schreiben (als Ausgänge deklariert), die über e!COCKPIT auszulesen und dann von dort aus in die Modbus-Prozessabbilder zu schreiben? Am Montag möchte ich das mal versuchen. Beim FC fängt MX0 bei 0x3000 bzw 12288 an. Wieso finde ich das im Manual vom PFC nicht?


----------



## aki09 (17 Mai 2018)

*Problem gelöst:*
Externes Endgerät als Master und PFC als Slave konfiguriert. Verbindung zwischen generischem Modbus Master (externem Endgerät) via Feldbuskonfigurator machen (mit Linie verbinden).
Über SimpleServerTCP lassen sich die Modbus Adressen, je nach Deklarierung der Arrays (InputRegister[0..10] = Modbusadresse 0..10) in die passenden Adressregister schreiben.

Mein Beispiel Code:

```
[I]// Deklarierung von Modbus Telegrammen (von externem Endgerät) in Merker-Register (Adressbereich 0..20)[/I]
[I]// Eingehende Modbus Telegramme von MSE (Master)[/I]
[I]Merker1_IN_REG := ModbusSlave.myHoldingRegisters[0];[/I]
[I]Merker2_IN_REG := ModbusSlave.myHoldingRegisters[1];[/I]
[I]Merker3_IN_COIL := ModbusSlave.myCoils[0];[/I]
[I]Merker4_IN_COIL := ModbusSlave.myCoils[1];[/I]
 
[I]// Deklarierung von Modbus Telegrammen (von Endgerät) in Merker-Register (Adressbereich 0..20)[/I]
[I]// Ausgehende Modbus Telegramme von PFC (Slave), Anfrage von MSE (Master)[/I]
[I]ModbusSlave.myInputRegisters[0] := Merker1_OUT_REG;[/I]
[I]ModbusSlave.myInputRegisters[1] := Merker2_OUT_REG;[/I]
[I]ModbusSlave.myDiscreteInputs[0] := Merker3_OUT_COIL;[/I]
[I]ModbusSlave.myDiscreteInputs[1] := Merker4_OUT_COIL;[/I]
```


----------



## Sugarman (21 Februar 2019)

Servus,
es passt zwar nicht  ganz zu der ersten Frage, aber habe ein ähnliches Problem mit der PFC 200.

Habe ein Problem mit der Modbusauslesung von digitalen Werten.

     Habe eine CPU 750-8202 und mehrere Feldbuskoppler 750-352.



  Die analogen Werte können über Modbus TCP oder UDP ausgelesen werden, die digitalen Werte können nicht abgefragt werden.



  Habe den Koppler 750-342 mit dem 750-352 ausgetauscht und es funktioniert.



  Habe den selben Versuch mit Codesys V2.3 gestartet, und ich kann alles lesen.

  Alle Eingänge können gelesen werden und Ausgänge kann ich über die Koppler schreiben.



  Sobald ich die Koppler mit E-Cockpit auslesen möchte kommen nur die Zustände von dem Koppler 342.

  Die Koppler 352 zeigen mir alle Zustände über IO-Check an, aber im Programm werden keine Zustände angezeigt.


Die einzelnen Variablen sind über den Modbus Konfigurator veröffentlicht und sind als Read Only deklariert.


  Das kann doch nicht sein?

  Worin liegt der Fehler?

Danke im Voraus, Tom


----------

