2 Word zu einem Real verbinden

Das passt alles.
Das einzige was nicht passt ist das Rot unterstrichene. Diese Variablen findet er nicht.
Dies aus dem Grund dass diese Variablen nur lokal in der Funktion vorkommen. Lokale Variablen kann man von aussen nicht sehen. Es hätte bei diesem Beispiel auch gar keinen Sinn.

Du musst anstelle von tempU.uaInWord[0] und [1] die Modbusvariablen angeben. Das heisst die zwei WORD die du von deinem Gerät auslesen kannst. Die Funktion "word2_in_Real" nimmt diese als eingänge entgegen und wandelt diese in eine REAL um. Die Zahl steht dann in fMyRealFromModbus.

Zusammengefasst: Die Zeile 2 in PLC_PRG anpassen. Die tempU.uaInword mit den Modbusvariablen wechseln.
 
Der Sinn einer Function ist doch aber, dass ich einen Programmbaustein habe, den ich beliebig oft verwenden kann. Oder habe ich das falsch verstanden?
Meine Überlegung war, dass ich in das Array zwei Word (also ein zusammengehöriges Pärchen) schreiben kann. Und das so, dass ich einen Baustein mir zwei Eingängen und einem Ausgang habe. Wenn ich mit der Function nur ein Bariablenpaar konvertieren kann, dann schreibe ich das doch besser an der Stelle in meinen Programmcode, wo ich es tatsächlich brauche. , Das ist mir, so wie das PN/DP in #10 vorgeschlagen hat, gelungen, finde ich aber eher umständlich, wenn es um mehrere Variablen geht, die mir als zwei (oder mehr) word übergeben werden.
Oder habe ich das unten falsch verstanden?
Du musst anstelle von tempU.uaInWord[0] und [1] die Modbusvariablen angeben. Das heisst die zwei WORD die du von deinem Gerät auslesen kannst. Die Funktion "word2_in_Real" nimmt diese als eingänge entgegen und wandelt diese in eine REAL um. Die Zahl steht dann in fMyRealFromModbus.

Zusammengefasst: Die Zeile 2 in PLC_PRG anpassen. Die tempU.uaInword mit den Modbusvariablen wechseln.
 
Verliere nicht dein eigentliches Ziel aus den Augen vor lauter Details mit der Union. ;)
Du willst vermutlich mehrere Real-Werte aus Registern von Modbus-Geräten lesen. Mit Hilfe der Function kann das dann so übersichtlich aussehen:
Code:
VAR
  Device1_Value_A : REAL;
  Device1_Value_B : REAL;
  Device1_Value_C : REAL;

  MB_buffer : ARRAY[0..99] OF WORD; //Empfangspuffer für Werte von Modbus
END_VAR

//6 Register (oder mehr) mit den 3 REAL-Werten aus einem Modbus-Gerät (Device1) lesen
LeseModbusRegister(..., Empfangspuffer := MB_buffer, ...); //(irgendeine Funktion)

//die Werte aus den gelesenen 6 Registern in 3 REAL-Variablen speichern
Device1_Value_A := word2_in_real(word1 := MB_buffer[0], word2 := MB_buffer[1]);
Device1_Value_B := word2_in_real(word1 := MB_buffer[2], word2 := MB_buffer[3]);
Device1_Value_C := word2_in_real(word1 := MB_buffer[4], word2 := MB_buffer[5]);
...
Hier kann man gut den Vorteil der Übersichtlichkeit sehen, wenn man den Kopier-Code einmalig in einer Function programmiert (word2_in_real(...)), und für jede gewünschte Variable die Function aufruft, anstatt jedesmal einzeln die 2 Words über die Union zu kopieren.
 
Zuletzt bearbeitet:
Jetzt verstehe ich überhaupt nicht mehr!
Brauche ich die Union überhaupt nicht?
wird das alles in der Fuction abgehandelt?

LeseModbusRegister(..., Empfangspuffer := MB_buffer, ...); //(irgendeine Funktion)
oder muss ich die ... durch das in der Union angelegte Array ersetzen?
Wie kommen die Word aus den Modbusregistern in das MB_Buffer Array?
wo stehen dann die Ergebnisse?

Immer, wenn ich geglaubt habe, dass ich etwas kapiert habe, taucht wieder was auf, was ich nicht blicke.
 
Brauche ich die Union überhaupt nicht?
Die brauchst du schon, die verwendest du innerhalb der Funktion

Wie kommen die Word aus den Modbusregistern in das MB_Buffer Array?
Das war nur ein Beispiel weil wir nicht wissen, wie das Ding bei dir heißt. Die Modbus Lese Ergebnisse hast du ja schon irgendwo als WORD stehen, und genau die musst du hier angeben.
wo stehen dann die Ergebnisse?
Das Ergebnis steht in der Variablen links vom Funktionsaufruf, im Beispiel oben in Device1_Value_A usw.

Die Funktion macht genau aus einem WORD -Paar eine Real-Variable. Sollen es mehrere sein kann man den Aufruf natürlich in eine Schleife packen (zumindest wenn sowohl die WORD als auch die REAL in einem Array liegen.
 
Das war nur ein Beispiel weil wir nicht wissen, wie das Ding bei dir heißt. Die Modbus Lese Ergebnisse hast du ja schon irgendwo als WORD stehen, und genau die musst du hier angeben.
Ich habe in #19 screenshots von Union, Function und Aufruf in PLC_PRG gepostet.
Ich bin, auch weil verschiedene Leute mir unterschiedliche Lösungsansätze vorgeschlagen haben, inzwischen massiv verunsichert. Zumindest bekomme ich das alles nicht unter einen Hut.
Wäre es vielleicht möglich, mir unter Berücksichtigung der in #19 gezeigten Scteenshots zu erklären, was da noch fehlt oder falsch ist? Grundsätzlich sollte das doch gar nicht weit von der Lösung entfernt sein.
Wenn ich den in #10 gezeigten Code in ein Programm packe, dann rennt das. Allerdings will ich ein Ding, das, einmal geschrieben, beliebig oft aufgerufen werden kann, und nur mit den gegebenen words befüllt werden muss.
 
wenn du in #19 in der allerletzten Zeile änderst zu
Code:
fMyRealFromModbus := word2_in_real(inWord1 := word1UL1, inWord 2 := word2UL2);

sollte es doch funktionieren?
 
Jetzt verstehe ich überhaupt nicht mehr!
Ich will versuchen, alle Einzelteile zusammen zu fassen. Doch da ich dein Wago-Programmiersystem und deinen vorhandenen Programmcode nicht kenne, kann ich dir nicht genau sagen, wo du was klicken musst, und wie deine Variablennamen lauten. Da musst du einfach mitdenken und ggf. deine Variablen einsetzen.

Irgendwo in deinem Wago-Programmiersystem erklärst du den Datentyp mit der UNION:
Code:
TYPE U_Realwords :
  UNION
    uReal  : REAL;
    uaWord : ARRAY[0..1] OF WORD;
  END_UNION
END_TYPE

Als nächstes die FUNCTION, die 2 Words (Register) entgegennimmt und als Real-Wert zurückgibt:
Code:
FUNCTION word2_in_real : REAL
VAR_INPUT
  word1 : WORD;
  word2 : WORD;
END_VAR
VAR
  tempU : U_Realwords;
END_VAR

//die 2 Input-Words in die Union speichern
tempU.uaWord[0] := word1;
tempU.uaWord[1] := word2;
//und als Real herauslesen und als Funktionswert zurückgeben
word2_in_real := tempU.uReal;

Z.B. im PROGRAM PLC_PRG den Code für das Einlesen mehrerer Realwerte aus Modbus-Registern aufrufen:
Code:
VAR
  Device1_Value_A : REAL;
  Device1_Value_B : REAL;
  Device1_Value_C : REAL;

  MB_buffer : ARRAY[0..99] OF WORD; //Beispiel Empfangspuffer für Werte von Modbus
END_VAR

//6 Register (oder mehr) mit den 3 REAL-Werten aus einem Modbus-Gerät (Device1) lesen
LeseModbusRegister(..., Empfangspuffer := MB_buffer, ...); //(irgendeine Funktion)

// "LeseModbusRegister" ist nur ein Platzhalter für die unbekannte Funktion,
// mit der das Programm die Register per Modbus in das Array MB_buffer einliest
// hier muss der tatsächliche Programmaufruf stehen!
// Oder im nachfolgenden Code die Namen der tatsächlichen Modbus-Variablen anpassen.

//die Werte aus den per Modbus gelesenen Registern an die Function übergeben,
//und deren Rückgabewerte in 3 REAL-Variablen speichern (linke Zuweisung)
Device1_Value_A := word2_in_real(word1 := MB_buffer[0], word2 := MB_buffer[1]);
Device1_Value_B := word2_in_real(word1 := MB_buffer[2], word2 := MB_buffer[3]);
Device1_Value_C := word2_in_real(word1 := MB_buffer[4], word2 := MB_buffer[5]);

siehe deinen Beitrag #8: Du liest die Modbus-Register anscheinend in einem separaten Programmteil "Modbustest" und legst die Werte dort in viele einzelne globale Variablen?
Dann lass mich raten, dann müsste der zu deinem Programm passende Code vielleicht so aussehen:
Code:
VAR
  Device1_Value_A : REAL;
  Device1_Value_B : REAL;
  Device1_Value_C : REAL;
END_VAR

Device1_Value_A := word2_in_real(word1 := word1UL1, word2 := word2UL1);
Device1_Value_B := word2_in_real(word1 := word1UL2, word2 := word1UL2);
Device1_Value_C := word2_in_real(word1 := word1UL3, word2 := word1UL3);

Das (für Strukturierung sinnvolle) Auslagern des Programmcodes aus PLC_PRG in einen FUNCTION_BLOCK zeige ich hier lieber nicht,
das würde wohl die nächste never ending Geschichte ... das wäre dann nicht mehr Hobby, sondern artet dann in Arbeit aus, wofür ich dann Geld nehmen müsste ;)

PS: lies dir den gesamten Thread nochmal durch. Jetzt wird vielleicht einiges klarer.
 
siehe deinen Beitrag #8: Du liest die Modbus-Register anscheinend in einem separaten Programmteil "Modbustest" und legst die Werte dort in viele einzelne globale Variablen?
Dann lass mich raten, dann müsste der zu deinem Programm passende Code vielleicht so aussehen:
Dann wären die Aufrufe der FUNCTION in diesem (uns unbekannten) Programmteil direkt am besten aufgehoben. Anstatt dem umkopieren der empfangenen Register-Words in viele einzelne (unnütze) Word-Variablen direkt mit der FUNCTION word2_in_real gleich in Real-Variablen kopieren.
 
siehe deinen Beitrag #8: Du liest die Modbus-Register anscheinend in einem separaten Programmteil "Modbustest" und legst die Werte dort in viele einzelne globale Variablen?
Der Programmteil Modbustest enthält irgendwelche Spielereien, die mit der Thematik um die es hier geht, nichts zu tun haben. (z.B. ein Taktgeber, mit dem ich auf einem Modbus RTU Board ein Relais schalten lasse.)
Die Variablen bilde ich, indem ich Modbus E/A Abbild die Adressen, in die Codesys die Werte schreibt, auf Variablen mappe. Die stehen dann global zur Verfügung.
1741592253335.png

Das nur zur Beantwortung deiner Frage.

Hier bei mir war mal wieder der Fehler ca 60cm vor dem Monitor.
In deinem letzten Post - und, wenn ich mir die vergangenen anschaue schon vorher mehrfach, hast Du angedeutet, dass das grundsätzlich passen sollte, aber eben noch aufgerufen werden muss.
Dann wären die Aufrufe der FUNCTION in diesem (uns unbekannten) Programmteil direkt am besten aufgehoben.

Weil ich von ST so viel Ahnung habe, wie eine Kuh vom tanzen, habe ich immer irgendwie versucht, in PLC_PRG die Funktion aufzurufen. Ich bin das so gewohnt, statt dem Taskmanager die Aufrufe so zu machen, dass ich die auszuführenden Programme in PLC_PRG eintrage. Jetzt habe ich einfach mal word2_to_real im Programm aufgerufen und es klappt. Weil ich ein fauler Mensch bin, in CRC, da muss man weniger denken.
1741593974376.png
Und dann bin ich noch über ein zweites Problem gestolpert, meine Modbuswerte sind weg. Ich lese da einen Shelly Zähler mit Modbus aus, die Werte visualisiere ich in einem SCADA-System und der mag es offensichtlich nicht, wenn da zwei gleichzeitig zugreifen wollen. Sollte bei Modbus TCP eigentlich kein Problem sein. Ich teste das einfach mit einem Gerät, das zuverlässig Modbuswerte liefert. Das ist der Grund, warum im Screenshot zwei händisch eingegebene Werte stehen.

Aus meiner Sicht ist damit das Thema gelöst. Kann man das irgendwo als gelöst markieren?

Und nochmal vielen herzlichedn Dank, an alle, die mir hier mit Informationen und Ratscvhlägen weitergeholfen haben.
 
Und dann bin ich noch über ein zweites Problem gestolpert, meine Modbuswerte sind weg. Ich lese da einen Shelly Zähler mit Modbus aus, die Werte visualisiere ich in einem SCADA-System und der mag es offensichtlich nicht, wenn da zwei gleichzeitig zugreifen wollen.
In deiner Steuerung solltest du beliebig oft auf die gezeigten Variablen (*) lesend zugreifen können, davon bekommt das Modbusgerät gar nichts mit und darf daher auch nicht stören. Oder macht deine Wago-Steuerung ein Problem, wenn mehrmals auf %IW zugegriffen wird? Kommt dadurch das Einlesen vom Modbus für das Mapping durcheinander?

(*) word1UL1, word2UL1, word1IL1, word2IL1, word1PL1, word2PL1
 
Das ist ein Missverständnis
Ich greife auf den Shelly mehrmals zu. Das sollte - habe ich zumindest mehrfach so gelesen - kein Problem sein (Modbus TCP). Mein Wechselrichter macht das beispielweise problemlos mit. Ich hatte das mit den fehlenden Werten nur erwähnt, als Erklärung dafür, dass ich im abgebildeten Netzwerk nicht die Variablen (word1UL1, word2UL1, word1IL1, word2IL1, word1PL1, word2PL1) verwende, sondern feste Werte. Aus der Codesys-Seite ist das jetzt am Rennen. Ich hatte zu viel in die in den Beispielen gezeigten Varianten, wie man jetzt die Functrion aufruft, hineininterpretiert.
Dann wären die Aufrufe der FUNCTION in diesem (uns unbekannten) Programmteil direkt am besten aufgehoben. Anstatt dem umkopieren der empfangenen Register-Words in viele einzelne (unnütze) Word-Variablen direkt mit der FUNCTION word2_in_real gleich in Real-Variablen kopieren.
Das war die Lösung, und auch dass ich mich zwischendurch um was anderes, als den Codesyskram gekümmert habe;)
 
Nimm einfach OSCAT Funktionen, ist am übersichtlichsten und wenn du nicht so im Thema bist musst dich nicht mit Pointern etc. beschäftigen.
Einmal kurz erklärt:
DWORD_OF_WORD: macht aus zwei Words ein DWORD, falls der Wert seltsam ist tausche mal w1 und w0.
DW_TO_REAL: Nimmt das DWORD und macht ein Real daraus

C-ähnlich:
realwert := OBASIC.DW_TO_REAL(OBASIC.DWORD_OF_WORD(w1:=wordnr1,w0:=wordnr2))
 
Das ist schon richtig, dass das mit oscat bequemer ist. Ich hatte das als "Notnagel" auch im Hinterkopf. Allerdings war der Hintergrund (auch), dass ich das kapieren wollte. Und ich denke, dass mir das halbwegs gelungen ist.
Das wäre, wenn da nicht meine Begiffsstutzigkeit im Weg gewesen wäre, auch eine wirklich leichte Übung gewesen.
 
Zurück
Oben