# CodeSys 3.5 / Raspberry / Socket-Connection auf Wärmepumpe



## philipp75 (5 November 2019)

Hi,

ich versuche Dummy-artig über einen Raspberry meine Wärmepumpe (Luxtronic Steuerung) per Socket-Connection abzufragen (Temperaturen etc.). Leider bin ich ein absoluter CodeSys Newbie.
Über z.B. PHP ziemlich simpel per:


```
$sBuff = 0;
$socket = socket_create(AF_INET, SOCK_STREAM,0);
$connect = socket_connect($socket, "192.168.50.107", 8888) || exit("socket_connect fehlgeschlagen"); 


// 3004 = Werte auslesen, 3003 Parameter setzen
$msg = pack('N*',3004); 
$send=socket_write($socket, $msg, 4); //3004 senden
$msg = pack('N*',0);
$send=socket_write($socket, $msg, 4); //0 senden 
socket_recv($socket,$Test,4,MSG_WAITALL);  // Lesen, sollte 3004 zurückkommen
socket_recv($socket,$Test,4,MSG_WAITALL); // Status
socket_recv($socket,$Test,4,MSG_WAITALL); // Länge der nachfolgenden Werte
$Test = unpack('N*',$Test);


print_r($Test);
socket_close($socket);
```


Mein zusammengestöpseltes Programm per "irgendwo im Netz gefundene Vorlage" sieht so aus:
Deklarationen:

```
PROGRAM PLC_PRG
VAR_INPUT
   xSend        : BOOL := FALSE;
   sDataSend    : STRING;
END_VAR
VAR
   strIPAddr    : NBS.IP_ADDR;
   uiPort        : UINT;
   
   nwClient        : NBS.TCP_Client;   
   nwTCPWrite    : NBS.TCP_Write;
   nwTCPRead    : NBS.TCP_Read;
   
   sDataRecv    : STRING := '';            // Final result value
   sDataBuff    : STRING(4095);            // Buffer for composing result string
   xBusy        : BOOL := FALSE;
   xSuccess        : BOOL := FALSE;
   xError        : BOOL := FALSE;
   iErrorCode    : INT := 0;   
   
   iStep        : INT := 0;
END_VAR
```

Programm PLC_PRG

```
// If the send command is active and the system is busy, ignore the send request 
IF xSend THEN
   IF xBusy THEN
       xSend := FALSE;
   END_IF   
END_IF


IF xSend THEN
   strIPAddr.sAddr := '192.168.50.107';
   uiPort := 8888;
   
   // Init status variables
   xSend := FALSE;
   xBusy := TRUE;
   xSuccess := FALSE;
   xError := FALSE;
   iErrorCode := 0;   
   sDataRecv := '';
   iStep := 0;
   nwClient(xEnable := FALSE);
   nwTCPRead(xEnable := FALSE);
   nwTCPWrite(xExecute := FALSE);
END_IF 


IF xBusy THEN
   CASE iStep OF
      0: // Prepare connection
         nwClient(xEnable := TRUE, ipAddr := strIPAddr, uiPort := uiPort, udiTimeOut := 10000000);   
         IF nwClient.xActive THEN
            // Successfuly connected
            iStep := 1;            // JUMP TO SEND DATA
         ELSIF nwClient.xError THEN
            // Error shile connection creation
            nwClient.xEnable := FALSE;
            xError := TRUE;
            iErrorCode := 1;      // Error creating connection: reason - 1
            xBusy := FALSE;
         ELSIF NOT nwClient.xActive AND NOT nwClient.xError AND nwClient.xDone THEN
            nwClient.xEnable := FALSE;
            xError := TRUE;      
            iErrorCode := 2;      // Error creating connection: reason - 2
            xBusy := FALSE;
         END_IF
         
      1,2: // Send data part 1
         IF nwTCPWrite.xBusy = FALSE AND nwTCPWrite.xError = FALSE AND nwTCPWrite.xDone = FALSE THEN 
            IF iStep = 1 THEN
                sDataSend := '3004';
            ELSE
                sDataSend := '0';
            END_IF
            
            nwTCPWrite.hConnection := nwClient.hConnection;
            nwTCPWrite.pData := ADR(sDataSend);
            nwTCPWrite.szSize := INT_TO_UDINT(LEN(sDataSend));
            nwTCPWrite.udiTimeOut := 10000000;
            nwTCPWrite.xExecute := TRUE;
         END_IF
      
         IF nwTCPWrite.xDone = TRUE THEN
            // Data IS SENT!
            nwTCPWrite.xExecute := FALSE;
            IF iStep = 1 THEN
                iStep := 2; // SEND WAITs     
            ELSE
                iStep := 3; // JUMP TO READ
            END_IF
            
         ELSIF nwTCPWrite.xError THEN
            // Send data ERROR
            nwTCPWrite.xExecute := FALSE;
            xError := TRUE;      
            iErrorCode := 3;      // Data write error
            xBusy := FALSE;
         ELSE
            nwTCPWrite();   
         END_IF   
        
      3: // Read data
         nwTCPRead(xEnable := TRUE, hConnection := nwClient.hConnection, szSize := SIZEOF(sDataBuff), pData := ADR(sDataBuff));
                  
         IF nwTCPRead.xReady AND nwTCPRead.eError = 0 THEN
            IF nwTCPRead.szCount > 0 THEN
               sDataRecv := sDataBuff;
            ELSE
               sDataRecv := '';
            END_IF
            xError := FALSE;
            iErrorCode := 0;      // No error
            xSuccess := TRUE; 
            xBusy := FALSE;
         ELSIF nwTCPRead.eError <> 0 THEN
            xError := TRUE;
            iErrorCode := 4;      // Data read error 
            xBusy := FALSE;
         END_IF
   END_CASE;
END_IF
```

Die Verbindung scheint zu klappen, ich bekomme einen Connection Handler. Das schreiben scheint er auch zu machen, aber es kommt nichts oder etwas unerwartetes zurück - siehe Screenshot aus dem online Modus.



1. Vermutung:
Im PHP Skript sende ich eine Zahl - 3004, die per "pack()" Befehl in einen binären Wert gewandelt wird - im CodeSys ist das Zeug mangels besserem Wissen als String deklariert.
Der Parameter "N*" in PHP bedeutet: vorzeichenloser Long-Typ (immer 32 Bit, Byte-Folge Big Endian), der "*" für Wiederholung bis ans Ende der Daten

Laut CodeSys debugger sendet er für pData den DWORD Wert "3061857692". 


2. Vermutung:
Genereller Ablauf ist untauglich?


1000 Dank für Eure Hilfe,
Philipp


----------



## plcSniffer (5 November 2019)

Die State Machine ist generell so in Ordnung. Wenn eine Zahl gesendet werden muss, sollte man dies auch machen. Deklariere deinen String in UDINT um, oder du deklarierst ein Byte Array an und legst deine Werte dort ab. Fürs debuggen empfiehlt sich wireshark.


----------



## philipp75 (5 November 2019)

Hi, vielen Dank - werde ich so versuchen.

Geschieht die Konvertierung in eine binäre Zeichenfolge implizit durch die Socketverbindung oder muss die Zahl noch nach binär konvertiert werden?
Im PHP Skript wandelt der Befehl PACK('N*', ...) die Zeichenfolge extra nach binär - habe auch gesehen, dass ich im PHP Skript die Zahl 3004 als String "3004" übergeben kann und bekomme richtige Ergebnisse zurück - interessiert PHP wohl herzlich wenig...


Danke!
Phil


----------



## plcSniffer (5 November 2019)

Darum kümmert sich der Baustein beim senden. Du übergibst dem Baustein ja die Adresse der Variable. Wie schon geschrieben, mit einem angelegten Byte Array geht es ganz einfach. pack macht unter PHP oder Python auch nichts anderes.


```
// 3004 big endian
anTxBuf[0] := 0x00;
anTxBuf[1] := 0x00;
anTxBuf[2] := 0x0b;
anTxBuf[3] := 0xbc;

nwTCPWrite.pData := ADR(anTxBuf);
```


----------



## philipp75 (9 November 2019)

Hallo,

die Ablaufsteuerung seitens der WP sieht folgendes vor:

(1) Client sendet 3004 -> Empfange 3004
(2) Client sendet 0 -> Empfange 0
(3) WP sendet 3004   -  Bestätigung
(4) WP sendet Statuswert (0 = OK)
(5) WP sendet Anzahl der zu erwartenden Datenfelder
(6) WP sendet Datenfeld 1
(7) WP sendet Datenfeld 2
(8) ...


Mein letzter Code sieht so aus. Die beiden Sendebefehle an die WP scheinen rauszugehen (keine Errorwerte in nwTCPWrite), beim Lesen bekomme ich einen "TCP_RECEIVE_ERROR". Wie würde man das debuggen?
Ich vermute dass es daran liegt:

```
IF nwTCPRead.xReady AND NOT nwTCPRead.xError THEN
```
Der Wert xReady wird nicht erreicht und nach ca. 1 Sekunde wirds dann ein xError.

Es müßten ja vielleicht wenigstens die ersten 3 Werte des Ergebnisarraysgefüllt sein, also die ersten 3 Returns der WP (Abfolge 3-5 in obigem Protokoll)



```
PROGRAM PLC_PRG
VAR_INPUT
   xSend        : BOOL := FALSE;
END_VAR
VAR
   strIPAddr    : NBS.IP_ADDR;
   uiPort        : UINT;
   readCount    : INT;
   sDataSend    : UDINT;
   nwClient        : NBS.TCP_Client;   
   nwTCPWrite    : NBS.TCP_Write;
   nwTCPRead    : NBS.TCP_Read;
   vals            : ARRAY[1..203] OF STRING(4);
   sDataBuff    : STRING(4);
   xBusy        : BOOL := FALSE;
   iStep        : INT := 0;
   iErrorCode   : INT := 0;
END_VAR
```



```
// If the send command is active and the system is busy, ignore the send request 
IF xSend THEN
   strIPAddr.sAddr := '192.168.50.107';
   uiPort := 8888;
   
   // Init status variables
   xSend := FALSE;
   xBusy := TRUE;
   readCount := 0;
   iStep := 0;
   iErrorCode := 0;
   nwClient(xEnable := FALSE);
   nwTCPRead(xEnable := FALSE);
   nwTCPWrite(xExecute := FALSE);
END_IF 


IF xBusy THEN
   CASE iStep OF
      0: // Prepare connection
         nwClient(xEnable := TRUE, ipAddr := strIPAddr, uiPort := uiPort);   
         IF nwClient.xActive THEN // Successfuly connected
            iStep := 1;           // JUMP TO SEND DATA
         ELSIF nwClient.xError THEN // Error while connection creation
            nwClient.xEnable := FALSE;
            xBusy := FALSE;
            iErrorCode := iStep;
         END_IF
         
      1: // Send data part 1
           sDataSend := 3004;
         nwTCPWrite(hConnection := nwClient.hConnection, pData := ADR(sDataSend), szSize := SIZEOF(sDataSend), udiTimeOut := 1000000, xExecute := TRUE);
         
         IF nwTCPWrite.xError THEN
            nwTCPWrite.xExecute := FALSE;
            xBusy := FALSE;
            iErrorCode := iStep;
         END_IF
         
         IF nwTCPWrite.xDone THEN
             iStep := 2;
         END_IF
         
      2: // Send data part 2
         sDataSend := 0;
         nwTCPWrite(hConnection := nwClient.hConnection, pData := ADR(sDataSend), szSize := SIZEOF(sDataSend), udiTimeOut := 1000000, xExecute := TRUE);
         IF nwTCPWrite.xError THEN
            nwTCPWrite.xExecute := FALSE;
            xBusy := FALSE;
            iErrorCode := iStep;
         END_IF
         
         IF nwTCPWrite.xDone THEN
             iStep := 3;
         END_IF
             
      3: // Read data
         IF readCount < 203 AND nwClient.xActive AND nwClient.hConnection <> CAA.gc_hINVALID THEN
            nwTCPRead(xEnable := TRUE, hConnection := nwClient.hConnection, szSize := SIZEOF(sDataBuff), pData := ADR(sDataBuff));
            IF nwTCPRead.xError THEN 
                nwTCPRead.xEnable := FALSE;
                xBusy := FALSE;
                iErrorCode := iStep;
            END_IF
            
            IF nwTCPRead.xReady AND NOT nwTCPRead.xError THEN
                readCount := readCount + 1;
                vals[readCount] := sDataBuff;
                xBusy := TRUE;
                // im dritten Byte steht die Anzahl der zu erwartenten Felder
                //IF readCount > 3 AND readCount = STRING_TO_UDINT(vals[3]) THEN
                //    xBusy := FALSE;
                //END_IF
            END_IF
        ELSE
            xBusy := FALSE;
        END_IF
   END_CASE;
END_IF
```


So habe ich es auch versucht - gleiches Ergebnis:

```
sDataSend[0] := 16#00;
sDataSend[1] := 16#00;
sDataSend[2] := 16#0B;
sDataSend[3] := 16#BC;
```


----------



## philipp75 (10 November 2019)

Habs hinbekommen:

Deklaration - Programm

```
PROGRAM PLC_PRG
VAR_INPUT
   xSend		: BOOL := FALSE;
END_VAR
VAR
   strIPAddr	: NBS.IP_ADDR;
   uiPort		: UINT;
   sDataSend    : ARRAY[0..3] OF BYTE;
   nwClient		: NBS.TCP_Client;   
   nwTCPWrite	: NBS.TCP_Write;
   //nwTCPWrite2	: NBS.TCP_Write;
   nwTCPRead	: NBS.TCP_Read;
   (* Relevante Werte *)
   tempAussen	: UDINT;  // Index 18
   tempVorlauf	: UDINT;  // Index 13
   tempRuecklauf: UDINT;  // Index 14
   tempVorlaufS : UDINT;  // Index 15
   status		: UDINT;
   wLowWord     : WORD;
   wHighWord	: WORD; 
   sDataBuff	: ARRAY[0..203] OF DWORD;
   temp			: DWORD;
   xBusy		: BOOL := FALSE;
   iStep		: INT := 0;
   iErrorCode   : INT := 0;
   FB_CONVERT   : FB_BYTESWAP_DWORD;
END_VAR
```

Programm

```
// If the send command is active and the system is busy, ignore the send request 
IF xSend THEN
   strIPAddr.sAddr := '192.168.50.107';
   uiPort := 8888;
   
   // Init status variables
   xSend := FALSE;
   xBusy := TRUE;
   iStep := 0;
   iErrorCode := 0;
   nwClient(xEnable := FALSE);
   nwTCPRead(xEnable := FALSE);
   nwTCPWrite(xExecute := FALSE);
END_IF 


IF xBusy THEN
   CASE iStep OF
      0: // Prepare connection
        nwClient(xEnable := TRUE, ipAddr := strIPAddr, uiPort := uiPort, udiTimeOut := 10000000);
        IF nwClient.xActive THEN // Successfuly connected
            iStep := 1;           // JUMP TO SEND DATA
        ELSIF nwClient.xError THEN // Error while connection creation
            nwClient.xEnable := FALSE;
            xBusy := FALSE;
			iErrorCode := iStep;
		ELSIF NOT nwClient.xActive AND NOT nwClient.xError AND nwClient.xDone THEN
            nwClient.xEnable := FALSE;
            xBusy := FALSE;      
            iErrorCode := 11;      // Error creating connection: reason - 2
        END_IF
         
      1: // Send data part 1
	  	 sDataSend[0] := 16#00;
		 sDataSend[1] := 16#00;
		 sDataSend[2] := 16#0B;
		 sDataSend[3] := 16#BC;
		 IF nwTCPWrite.xBusy = FALSE AND nwTCPWrite.xError = FALSE AND nwTCPWrite.xDone = FALSE THEN
			nwTCPWrite.hConnection := nwClient.hConnection;
            nwTCPWrite.pData := ADR(sDataSend);
            nwTCPWrite.szSize := INT_TO_UDINT(SIZEOF(sDataSend));
            nwTCPWrite.udiTimeOut := 10000000;
			nwTCPWrite.xExecute := TRUE;
		 END_IF 
		 
		 IF nwTCPWrite.xDone = TRUE THEN
            nwTCPWrite.xExecute := FALSE;
			nwTCPWrite(xExecute := FALSE);
			// NEXT PACKAGE
            iStep := 2;         
         ELSIF nwTCPWrite.xError THEN
            nwTCPWrite.xExecute := FALSE; 
            iErrorCode := 1;      // Data write error
            xBusy := FALSE;
         ELSE
            nwTCPWrite();   
         END_IF
         
	  2: // Send data part 2
	     sDataSend[0] := 16#00;
		 sDataSend[1] := 16#00;
		 sDataSend[2] := 16#00;
		 sDataSend[3] := 16#00;
		 
		
		 IF nwTCPWrite.xBusy = FALSE AND nwTCPWrite.xError = FALSE THEN
			nwTCPWrite.hConnection := nwClient.hConnection;
            nwTCPWrite.pData := ADR(sDataSend);
            nwTCPWrite.szSize := INT_TO_UDINT(SIZEOF(sDataSend));
            nwTCPWrite.udiTimeOut := 10000000;
			nwTCPWrite.xExecute := TRUE;
		 END_IF 
		 
		 IF nwTCPWrite.xDone = TRUE THEN
            nwTCPWrite.xExecute := FALSE;
            iStep := 3;         // NEXT PACKAGE
         ELSIF nwTCPWrite.xError THEN
            nwTCPWrite.xExecute := FALSE;
            iErrorCode := 2;      // Data write error
            xBusy := FALSE;
         ELSE
            nwTCPWrite();   
         END_IF
		 
		 	
      3: // Read data
		nwTCPRead(xEnable := TRUE, hConnection := nwClient.hConnection, szSize := SIZEOF(sDataBuff), pData := ADR(sDataBuff));
		IF nwTCPRead.xReady AND nwTCPRead.eError = 0 THEN
			FB_CONVERT(VAR_IN := sDataBuff[0], VAR_OUT => status);
			FB_CONVERT(VAR_IN := sDataBuff[18], VAR_OUT => tempAussen);
			FB_CONVERT(VAR_IN := sDataBuff[13], VAR_OUT => tempVorlauf);
			FB_CONVERT(VAR_IN := sDataBuff[14], VAR_OUT => tempRuecklauf);
			FB_CONVERT(VAR_IN := sDataBuff[15], VAR_OUT => tempVorlaufS);
			
			//valString := val;
			xBusy := FALSE;
			nwTCPRead.xEnable := FALSE;
			nwClient.xEnable := FALSE;
			
		END_IF


   END_CASE;
END_IF
```

Deklaration - Funktionsbaustein

```
FUNCTION_BLOCK FB_BYTESWAP_DWORD
VAR_INPUT
	VAR_IN : DWORD;
END_VAR
VAR_OUTPUT
	VAR_OUT: UDINT;
END_VAR
VAR
	wHighWord : WORD;
	wLowWord : WORD;
END_VAR
```

Funktionsbaustein

```
(*
Switching 32-bit DWORD
IN:  00 00 0B BC
OUT: BC 0B 00 00
*)
wLowWord := DWORD_TO_WORD(VAR_IN);
wHighWord := DWORD_TO_WORD(SHR(VAR_IN, 16));
wLowWord := ROL(wLowWord,8);
wHighWord := ROL(wHighWord,8);
VAR_OUT := (65536 * wLowWord + wHighWord);
```


----------

