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, 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
Du musst FbMbMasterTcp nehmen und die Struktur typMbQuery entsprechend füllen
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)
Nein, sHost ist der Server/Slave, also der Koppler 352 (Master=Client=PFC100; Slave=Server=352).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?
Sieht so aus, aber wahrscheinlich wäre es leichter dies in der Gerätestruktur über den Modbus Slave Tab einzustellen.Den FbMbSimpleServerTcp Baustein benötige ich also nur, wenn ich einen Feldbuscontroller als Slave parametrieren möchte?
Sollte bei TCP/UDP Verbindung egal sein, wird normalerweise nur bei RTU verwendet. Die Slaveadresse wird wie schon oben geschrieben bei sHost angegeben.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
OKbFunctionCode := 16#04, // Read Input Registers
-> Das entspricht FC4.
OK, also Start mit dem ersten Register (erster analoger Kanal)uiReadAddress := 0, // Startaddress := %IW0
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.uiReadQuantity := 10, // Quantity of wanted registers -> Anzahl der IWs, also Anzahl der benötigen Register, die auf meinem Koppler ausgelesen werden müssen?!
OK, aber vielleicht besser auf 0 setzen.uiWriteAddress := 512, // not needed for FC4
OK, aber vielleicht besser auf 0 setzen.uiWriteQuantity := 10, // not needed for FC4
OKawWriteData := [124(0)] // not needed for FC4
Eigentlich sollten die Daten dort liegen. Meldet den xlsOpen True und xError wirklich False?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)!
Du mußt mehrere Query Strukturen ausführen und die dann nacheinander abarbeiten.Und wie konfiguriere ich die Zeilen, wenn ich z.B. sowohl FC16 (Write Multiple Registers) und FC4 (Read Input registers) nutzen möchte?
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ötigtMir 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.
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;
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
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ötigtMir 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.
schön zu lesen.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.
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.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 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.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?
Ja, wie oben beschrieben. Der erste analoge (komplexe) Kanal liegt auf Adresse 0.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?
Wahrscheinlich nicht, wäre aber vielleicht besser. Mußt Du ausprobieren.3. uiWriteAddress: Muss bei Funktionscode, bei denen nur Eingangsregister eingelesen werden, mit 0 deklariert werden!?
Fast richtig. Nicht das ganze Array, sondern nur ein ein Element davon. Daher die Zählvariable.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?
OK, schon mal gut.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).
Da ist jetzt im Text etwas durcheinander geraten.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 kann ich jetzt nicht helfen. Aber Du könntest auch die neue Firmware auf den Controller aufspielen. Die kannst Du beim Wago Support anfordern.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?
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.
dann lohnt sich der Aufwand wenigstensHallo Thruser,
vielen Dank nochmal für deine schnelle Antwort und die ganzen detaillierten Antworten auf meine Fragen.
Dann bin ich zumindestens nicht ganz auf dem Holzweg. Verstanden, check!
Ausprobiert, verstanden, check!
gleich mehrere Fehler!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
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
Muß ich mal selbst aufbauen am System, kann ich aber erst heute abend. awWriteData kommt mir noch etwas komisch vor.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
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;
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
// 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
//------------------------------------------------------------------------------------
// 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;
Du hast myInputRegisters wahrscheinlich nicht als VAR_INPUT deklariert. Das hatte ich damals auch bei der Jobliste.Zudem funktioniert das Auslesen der Temperatureingänge mit diesem Programmcode auch nicht. Fehler: "PLC_PRG: C0037: 'myInputRegisters' ist kein Eingang von 'ModbusSlave' (-> NOK)
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
[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]
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?