# e!COCKPIT ModbusTCP Floatwert zu Real konvertieren



## RobbyJump (18 März 2020)

*[Gelöst] e!COCKPIT ModbusTCP Floatwert zu Real konvertieren*

Hallo zusammen,
ich denke dass es hier recht gut reinpasst da e!Cockpit etwas spezieller ist als das blanke Codesys.

Mein Problem ist aktuell das einlesen von Floats über Modbus TCP.
Verwendet wird ein 750-8207 zusammen mit mehreren Janitza UMG801
Der Zugriff und die Konfig über den Modbuskonfigurator (Netzwerkansicht) verlief wie gewohnt unproblematisch, nun kommt allerdings der Knackpunkt beim einlesen der Werte, da diese nur als Float vorliegen.
Probiert habe ich unteranderem schon den Datenpunkt als Real,LReal,Int,DWord und Word einzulesen mit dem ergebniss dass es in keine sinnvolles Format konvertiert wird.
Die Frage ist nun ob man es mit einem Baustein hinbekommt oder es andere Möglichkeiten ohne viel Aufwand (da es viele Datenpunkte werden) gibt mit denen man das Problem erschlagen kann.

Ich stehe etwas auf dem Schlauch, jeder Hinweis kann mir weiterhelfen

Danke
Gruß Robin


----------



## Tobsucht (18 März 2020)

Hallo Robin,

Janitza stellt die Werte als little Endian bereit. Somit müsstest Du die Bytereihenfolge tauschen.
Wago arbeitet z.B. mit der Reihenfolge 1234. Janitza mit 4321. Modbus definiert dass das High Byte zuerst gesendet werden muss. Also resultiert daraus 3412. Somit muss Du nur das High und Low Word tauschen.
Du könntest z.B eine Funktion schreiben. Mit einem Pointer to Array [0..1] of Word auf den Real Wert gelegt, kannst Du die Wörter tauschen.

Einige Janitza Geräte liefern die Werte auch im Big Endian an bestimmmten Modbus Adressen.

Grüße


----------



## RobbyJump (18 März 2020)

Hi Tobsucht,
das ist auf jeden fall ein guter Hinweis an dem ich mich mal probieren muss.
Wenn ich es also richtig verstehe müsste ich ein Dword mit der Bytereihenfolge 1234 generieren und dieses nur mit DWORD_TO_REAL wandeln?
Die Reihenfolge in Big-Endian ist aber 4321 und in Little 3412 so dass ich am besten die Little-Endian-Register (z.B. 19000+32768 ) als DWORD einlese um dann die Wortreihenfolge zu tauschen?



> Byte sequence
> The data in the modbus address list can be called up in the
> • Big-Endian (high-Byte before low-Byte) and in the
> • Little-Endian (low-byte before high-byte)
> ...



Hierbei ist gerade aufgefallen dass die Adressen ab 51768 nur den Wert 0 liefern (vom Janitzer Support bestätigt, ist ein Firmwarefehler).


Gibt es nicht eine schönere variante ohne alle Datenpunkte erst konvertieren zu müssen?
Sonnst muss ich für jeden Datenpunkt einen Wandlerbaustein (den man zum glück nur einmal bauen muss) nutzen und diesen manuell Verknüpfen (sind so Ca 250+ Datenpunkte)
Irgendwie nervig dass man nicht einfach Wert x100 als Int bekommen kann.

Danke schonmal

Gruß


----------



## PN/DP (18 März 2020)

DWORD_TO_REAL kannst Du bei Codesys-basierenden SPS nicht verwenden. Du müsstest die zwei Words in der richtigen Reihenfolge hintereinander in den Speicher legen und mit einem Pointer oder als UNION den REAL-Wert herauslesen.
https://www.sps-forum.de/gebaeudeau...bus-rtu-modul-pumpdrive-2-a-2.html#post715454

Harald


----------



## RobbyJump (19 März 2020)

Besten Dank für eure kompetente Unterstützung!

Wago hat mir durch ein Supportticket mitgeteit, dass REAL und FLOAT nach IEEE754 identisch sind und die PFC200 BigEndian (Motorolaformat) somit unterstützen.
In Zukunft wird es vermutlich auch möglich sein im Modbuskonfigurator (e!Cockpit) die Bytereihenfolge zu ändern.

Zudem habe ich aus anderen Gründe auch einmal mit Janitza gesprochen wobei mir bestätigt wurde das die 19000der Register des UMG801 in BigEndian vorliegen, die LittleEndian register hat das Gerät nicht (Fehler in der Anleitung).
Zukünftig wird es hier ggf. auch möglich sein die Register extra zu Konfigurieren.

Nach dem ich das Ganze dann noch einmal versucht habe, ist mir aufgefallen dass die Register um 1 verrutscht sind (Register 19000 in der Wago als 18999 implementieren)
...da hätte ich auch ehr drauf kommen können...

Auf jeden fall ist dieses Problem aus der Welt und doch alles recht einfach.
Anbei noch zwei Screenshoots aus meinem Testprogramm.





Danke euch nochmals
Gruß Robin


----------



## Oldsnap (12 Februar 2021)

Hallo Zusammen,

ich bin aktuell auch auf diese Problem gestoßen, als ich aus einem DEOS-Controller REAL-Werte lesen wollte (DEOS --> Slave, WAGO --> Master). DEOS verwendet offensichtlich das little endian Format. Nachdem sowohl WAGO als auch DEOS keine einstellbares Format anbieten, musste ich eine kleine Funktion zur Umwandlung schreiben, welche auch funktioniert. Leider bin ich nicht der Held, was das Programmieren anbetrifft und so hab ich es sicherlich viel zu umständlich fabriziert:


```
FUNCTION SWAP1 : REAL

VAR_INPUT
    rbig_endian:REAL;
END_VAR

VAR
    DW_R: DW_R_Union;
    R_DW: DW_R_Union;
END_VAR

R_DW.rUnion:=rbig_endian;

DW_R.dwUnion.16:=R_DW.dwUnion.0;
DW_R.dwUnion.17:=R_DW.dwUnion.1;
DW_R.dwUnion.18:=R_DW.dwUnion.2;
DW_R.dwUnion.19:=R_DW.dwUnion.3;
DW_R.dwUnion.20:=R_DW.dwUnion.4;
DW_R.dwUnion.21:=R_DW.dwUnion.5;
DW_R.dwUnion.22:=R_DW.dwUnion.6;
DW_R.dwUnion.23:=R_DW.dwUnion.7;

DW_R.dwUnion.24:=R_DW.dwUnion.8;
DW_R.dwUnion.25:=R_DW.dwUnion.9;
DW_R.dwUnion.26:=R_DW.dwUnion.10;
DW_R.dwUnion.27:=R_DW.dwUnion.11;
DW_R.dwUnion.28:=R_DW.dwUnion.12;
DW_R.dwUnion.29:=R_DW.dwUnion.13;
DW_R.dwUnion.30:=R_DW.dwUnion.14;
DW_R.dwUnion.31:=R_DW.dwUnion.15;

DW_R.dwUnion.0:=R_DW.dwUnion.16;
DW_R.dwUnion.1:=R_DW.dwUnion.17;
DW_R.dwUnion.2:=R_DW.dwUnion.18;
DW_R.dwUnion.3:=R_DW.dwUnion.19;
DW_R.dwUnion.4:=R_DW.dwUnion.20;
DW_R.dwUnion.5:=R_DW.dwUnion.21;
DW_R.dwUnion.6:=R_DW.dwUnion.22;
DW_R.dwUnion.7:=R_DW.dwUnion.23;

DW_R.dwUnion.8:=R_DW.dwUnion.24;
DW_R.dwUnion.9:=R_DW.dwUnion.25;
DW_R.dwUnion.10:=R_DW.dwUnion.26;
DW_R.dwUnion.11:=R_DW.dwUnion.27;
DW_R.dwUnion.12:=R_DW.dwUnion.28;
DW_R.dwUnion.13:=R_DW.dwUnion.29;
DW_R.dwUnion.14:=R_DW.dwUnion.30;
DW_R.dwUnion.15:=R_DW.dwUnion.31;


SWAP1:=DW_R.rUnion;
```

Ich hab dann mal versucht, das Ganze zu vereinfachen, in dem ich hier im Forum Codeschnipsel zu einem ähnlichen Problem geklaut habe. Leider tappe ich bei der Bitmaskierung immer völlig im Dunklen und hab das hier fabriziert ... erwartungsgemäß funktioniert es natürlich nicht:


```
FUNCTION SWAP2 : REAL

VAR_INPUT
    In:REAL;
END_VAR

VAR
    DW_R: DW_R_Union;
    R_DW: DW_R_Union;
    dw_in: DWORD;
END_VAR

R_DW.rUnion:=In;
dw_in:=R_DW.dwUnion;

    DW_R.dwUnion:=  SHR(dw_in AND 16#FF000000 ,24) OR 
                    SHR(dw_in AND 16#00FF0000 ,8 ) OR
                    SHL(dw_in AND 16#0000FF00 ,8 ) OR
                    SHL(dw_in AND 16#000000FF ,24) ;

SWAP2 := DW_R.rUnion;
```

Vielleicht hat jemand Lust, sich den Stuss mal anzusehen und hat eine Idee für mich, wie man's richtig machen kann.

Ach ja ... das ist das Union:


```
TYPE DW_R_Union :
UNION
    rUnion: REAL;
    dwUnion: DWORD;    
END_UNION
END_TYPE
```

Grüße Holger


----------



## Oberchefe (12 Februar 2021)

Wenn deine Bit-Logik funktioniert, sollte das so gehen:


```
FUNCTION SWAP2 : REAL

VAR_INPUT
    In:REAL;
END_VAR

VAR
    DW_R: DW_R_Union;
    R_DW: DW_R_Union;
    dw_in: DWORD;
END_VAR

R_DW.rUnion:=In;
dw_in:=R_DW.dwUnion;

    DW_R.dwUnion:=  SHR(dw_in AND 16#FFFF0000 ,16) OR 
                    SHL(dw_in AND 16#0000FFFF ,16) ;

SWAP2 := DW_R.rUnion;
```

Alternativ geht's auch mit Pointer:


```
FUNCTION ByteSwap : REAL
VAR_INPUT
    In: REAL;
END_VAR
VAR
    dwOut: REAL;
    pbSource: POINTER TO INT;
    pbDest: POINTER TO INT;
END_VAR
```



```
pbSource := ADR(In);
pbDest := ADR(dwOut)+2;

pbDest^:=pbSource^;

pbSource := ADR(In)+2;
pbDest := ADR(dwOut);

pbDest^:=pbSource^;

ByteSwap := dwOut;
```


----------



## Oldsnap (12 Februar 2021)

Hey Oberchefe,

Codebeispiel 1 funktioniert schon bestens. Danke für Deine Hilfe, auch wenn ih jetzt noch verwirtter bin. Ich muss ja bei der Umwandlung little in big endian die Bytereihenfolge von 1234 in 4321 wandeln. Mit meinem Tippel-Tappel Bit-Austauschen passiert ja genau das. Das mit dem Maskieren uns schieben hab ich offensichtlich aber echt nicht gerafft. Ich dachte mir das so:

                    SHR(dw_in AND 16#FF000000 ,24) OR // maskiert Byte 1 und schiebt es 24 Bit nach rechts, also an Stelle von Byte 4
                    SHR(dw_in AND 16#00FF0000 ,8 ) OR // maskiert Byte 2 und schiebt es 8 Bit nach rechts, also an Stelle von Byte 3
                    SHL(dw_in AND 16#0000FF00 ,8 ) OR // maskiert Byte 3 und schiebt es 8 Bit nach links, also an Stelle von Byte 2
                    SHL(dw_in AND 16#000000FF ,24) ;  // maskiert Byte 4 und schiebt es 8 Bit nach links, also an Stelle von Byte 1

Bei Deinem Code werden für mich theoretisch die Bytes in den geschobenen Wortwerten nicht getauscht, da käme für mich jetzt 
eine Bytereihenfolge von 3412 raus ?! Aber es funktioniert ja tadellos ... wo ist nur mein Denkfehler ???

Grüße Holger


----------



## Oberchefe (12 Februar 2021)

> Ich muss ja bei der Umwandlung little in big endian die Bytereihenfolge von 1234 in 4321 wandeln.



Deine Bitlogik sagt aber was anderes. Die macht aus AABBCCDD ein CCDDAABB


----------



## PN/DP (12 Februar 2021)

Wenn schon mit UNION oder Pointer gearbeitet wird, warum dann noch so unverständliches umständliches Maskieren und Schieben? Dann kann man doch ganz übersichtlich alle 4 Bytes einzeln in beliebiger Folge an die richtigen Stellen kopieren:

```
TYPE R_B4_Union :
UNION
    rUnion: REAL;
    abUnion: ARRAY[0..3] OF BYTE;
END_UNION
END_TYPE

...

VAR
    uIn: R_B4_Union;
    uOut: R_B4_Union;
END_VAR

uIn.rUnion := In;
uOut.abUnion[1] := uIn.abUnion[3];
uOut.abUnion[0] := uIn.abUnion[2];
uOut.abUnion[3] := uIn.abUnion[1];
uOut.abUnion[2] := uIn.abUnion[0];

SWAP2 := uOut.rUnion;
```
Das läßt sich auch für andere Tauschereien easy weiternutzen/anpassen.

Harald


----------



## Oldsnap (12 Februar 2021)

Oberchefe schrieb:


> Deine Bitlogik sagt aber was anderes. Die macht aus AABBCCDD ein CCDDAABB



Stimmt ... Du hast recht, ich hatte an der Bitlogik so lange rumgebastelt, bis es gepasst hatte ... 1234 -->  4321 der erste Versuch, nach Literatur ... war's in meinem Falle aber nicht. Vielen Dank nochmal ... dass lag der Fehler gar nicht bei der Bitmaske.

Grüße Holger


----------



## Oldsnap (12 Februar 2021)

PN/DP schrieb:


> Wenn schon mit UNION oder Pointer gearbeitet wird, warum dann noch so unverständliches umständliches Maskieren und Schieben? Dann kann man doch ganz übersichtlich alle 4 Bytes einzeln in beliebiger Folge an die richtigen Stellen kopieren:
> 
> ```
> TYPE R_B4_Union :
> ...




Genial ! Danke !!!


----------



## EddyNew (17 Februar 2021)

Hallo RobbyJump,

hast Du die Werte einfach als REAL Adresse minus 1 ausgelesen? 

Stehe momentan vor dem gleichen Problem und bekomme keine plausiblem Werte raus bei einem Janitza UM6G04.




RobbyJump schrieb:


> Besten Dank für eure kompetente Unterstützung!





RobbyJump schrieb:


> Wago hat mir durch ein Supportticket mitgeteit, dass REAL und FLOAT nach IEEE754 identisch sind und die PFC200 BigEndian (Motorolaformat) somit unterstützen.
> In Zukunft wird es vermutlich auch möglich sein im Modbuskonfigurator (e!Cockpit) die Bytereihenfolge zu ändern.
> 
> Zudem habe ich aus anderen Gründe auch einmal mit Janitza gesprochen wobei mir bestätigt wurde das die 19000der Register des UMG801 in BigEndian vorliegen, die LittleEndian register hat das Gerät nicht (Fehler in der Anleitung).
> ...


----------



## PN/DP (17 Februar 2021)

Hast Du auch 4 Bytes gelesen?
Hast Du die Bytes, die Du vom UMG604 erhältst, mit dem Janitza Modbus Diagnose Test Client verglichen? Hast Du mit dem Tool auch mal 2 Register (2 Word, 4 Byte) vorher und 2 Register nachher gelesen und mit Deinen Antwort-Bytes verglichen? Möglicherweise siehst Du da Deine Bytes und wie die getauscht werden müssen, damit ein plausibler Wert rauskommt.

Harald


----------



## EddyNew (18 Februar 2021)

Habe jetzt eine Lösung gefunden.
Ich schreibe den Modbus-Wert in ein REAL. Über eine Funktion konvertiere ich die Rohdaten in den passenden REAL-Wert.



> Code:
> FUNCTION fb_Janitza_FLOAT_TO_REAL : REAL
> VAR_INPUT
> Input_Real : REAL;
> ...





PN/DP schrieb:


> Hast Du auch 4 Bytes gelesen?
> Hast Du die Bytes, die Du vom UMG604 erhältst, mit dem Janitza Modbus Diagnose Test Client verglichen? Hast Du mit dem Tool auch mal 2 Register (2 Word, 4 Byte) vorher und 2 Register nachher gelesen und mit Deinen Antwort-Bytes verglichen? Möglicherweise siehst Du da Deine Bytes und wie die getauscht werden müssen, damit ein plausibler Wert rauskommt.
> 
> Harald


----------



## PN/DP (18 Februar 2021)

Au, das schmerzt... das kann man im SPS-Fachforum so nicht stehenlassen 
Besser so:

```
[COLOR="#008000"](* 32 Bit Byteorder 3412 zu 1234 tauschen = H/L-Words tauschen *)[/COLOR]
FUNCTION fc_Janitza_FLOAT_TO_REAL : REAL
VAR_INPUT
  Input_Real : REAL;
END_VAR
VAR
  dwTemp : DWORD;
  pReal : POINTER TO REAL;
END_VAR

pReal := ADR(dwTemp);       [COLOR="#008000"]//Pointer auf das DWORD setzen[/COLOR]
pReal^:= Input_Real;        [COLOR="#008000"]//Input-Wert in das DWORD speichern[/COLOR]

dwTemp := ROL(dwTemp, 16);  [COLOR="#008000"]//die beiden Words des DWORD tauschen[/COLOR]

fc_Janitza_FLOAT_TO_REAL := pReal^;  [COLOR="#008000"]//das DWORD als REAL zurückgeben[/COLOR]
```

Harald


----------

