ELTAKO Multisensor MS über TCP Gateway an Beckhoff SPS

Begonnen von ADS_0x1, 18. Dezember 2017, 22:18:57

Vorheriges Thema - Nächstes Thema

0 Mitglieder und 1 Gast betrachten dieses Thema.

ADS_0x1

Hallo zusammen,

da ich noch keinerlei Erfahrungen im Internet gefunden habe, dachte ich, ich lasse euch mal an meinen teilhaben - nicht ganz uneigennützig, denn für den geschriebenen Baustein stellt sich am Ende die Frage: Was kann man daran besser machen?

Aber zum 'Projekt' und der Historie:

Mein Architekt teilte mir beim Berechnen des sommerlichen Wärmeschutzes mit, dass mein Wunsch nach großen Fenstern einen gehörigen Nachteil hat (diese blöde Physik, wer hätte das gedacht...): Die Sonne heizt im Sommer mein Wohnzimmer-Esszimmer-Küche auf und es kann Schweine-warm werden. Aber ich solle mir keine Sorgen machen, man kann von Wa**** eine Steuerung für die Rollos kaufen und die fährt bei hoher Sonneneinstrahlung eben jene runter. Mööööp: Das Teil kostete mehr, als eine CX9020 bei ebay und ich hatte keinen Bock, meine Rollos runter zu fahren. Also habe ich die Rollos gegen Raffstoren 'geupgraded' und mir ne Beckhoff Steuerung gekauft. Anschließend habe ich dann eine gefühlte Ewigkeit nach einer Lösung gesucht, um irgendwie Sonneneinstrahlung, Wind, Regen und Temperatur messen zu können.

Dabei stößt man (auch dank dem Forum hier ;) ) schnell auf Komponenten wie die Elsner Wetterstation P03 oder die Thies clima Wetterstation Compact WSC11. Dazu benötigt man entweder eine SPS, die anstelle der RS232 Schnittstelle eine RS485/422 Buchse verbaut hat oder man hängt eine entsprechende Karte (bspw. KL6041) dran. Um darüber mit den Wetterstationen kommunizieren zu können, kommt noch die TwinCAT PLC Lib Serial Communication hinzu und nach dem Ende des Baus stellt man fest: Dafür ist jetzt kein Geld mehr da.

Ich hatte dann durch Zufall einen Adapter RS232/422/485 auf TCP im Internet gefunden, obwohl ich eigentlich nach einem DALI to Ethernet Gateway gesucht habe - denn die TCP Lizenz habe ich schon gekauft und wollte darüber DALI einbinden. Nach einigem Überlegen habe ich mir diesen bestellt und unter einer anderen Anforderung (Barcodeleser via RS232 über TCP auslesen) getestet. Und das lief so gut, dass ich mich an die RS485/422 Schnittstelle gewagt habe und mal die Wetterstation von Eltako bestellt habe - das ist glaube ich faktisch die Elsner P03 basic.

Jetzt habe ich folgende Komponenten erfolgreich im Einsatz:

- USR-TCP232-306 (~38 â,¬ bei Amazon, $36 in China)
- Eltako Multisensor MS (~200â,¬)
- TCP/IP Bibliothek (kostet je nach CPU-Modell)

Wichtig ist, dass der TCP/IP Server auch installiert ist und nicht nur die Lizenz übertragen wird ( ::) - hat etwas gedauert, bis ich das herausgefunden habe) - das habe ich nach einigem Herumprobieren herausgefunden, ich habe dann den zuletzt funktionierenden Code genommen (was dazu geführt hat, dass ich weder den OSCAT-Baustein für die Socketverbindung genommen habe, noch den FB_LocalClient von Beckhoff, denn ich dachte zu diesem Zeitpunkt, dass es an meiner Programmierung liegt und nicht an einem fehlendem Paket von Beckhoff).

Im Programmcode sieht es nun wie folgt aus:

Datenstruktur:
TYPE st_Wetterstation :
STRUCT
f_Temperatur : REAL; (* +/- Temperatur in Grad Celcius *)
n_SonneSued : USINT; (* Sonneneinstrahlung SÜD in klx *)
n_SonneWest : USINT; (* Sonneneinstrahlung WEST in klx *)
n_SonneOst : USINT; (* Sonneneinstrahlung OST in klx *)
b_Daemmerung : BOOL; (* Dämmerung erkannt ja/nein *)
n_Daemmerungswert : UINT; (* Tageslichtwert  in lx *)
f_Windgeschwindigkeit: REAL; (* Windgeshwindigkeit in m/s *)
b_Regen : BOOL; (* Regen erkannt ja/nein *)
END_STRUCT
END_TYPE


Deklarationspart
FUNCTION_BLOCK fb_Wetterstation
VAR_IN_OUT
io_Wetterdaten : st_Wetterstation;
END_VAR
VAR_INPUT
END_VAR
VAR_OUTPUT
END_VAR
VAR

enable : BOOL;
impulsgenerator : CLK_PRG;

nStep : USINT := 5;

h_Socket : T_HSOCKET;
s_b_connect : BOOL;
s_b_receive : BOOL;

s_s_RecBuffer : STRING[255];
s_n_AnzahlZeichen : UDINT;

s_b_disconnect : BOOL;

s_s_Wetterdatenstring : STRING[21];

(* Instanzen bilden: *)

TCP_Connect : FB_SocketConnect;
TCP_Receive : FB_SocketReceive;
TCP_Close : FB_SocketClose;

(* Fehlerbits und -Nummern *)
s_b_ConnectBusy : BOOL;
s_b_ConnectError : BOOL;
s_n_ConnectError : UDINT;


s_b_ReceiveBusy : BOOL;
s_b_ReceiveError : BOOL;
s_n_ReceiveError : UDINT;


s_b_DisconnectBusy : BOOL;
s_b_DisconnectError : BOOL;
s_n_DisconnectError : UDINT;

t_f_tempREAL : REAL;
END_VAR


Und der Coding-Part:
impulsgenerator( PT := T#1750ms,
Q => enable);

CASE nStep OF
5:
(* Idle *)
IF enable AND h_Socket.handle = 0 THEN
nStep := 10;
ELSIF enable AND h_Socket.handle <> 0 THEN
nStep := 20;
END_IF;
10:
(* Verbindungsstatus prüfen *)
IF h_Socket.handle = 0 OR TCP_Connect.bError THEN
IF s_b_connect THEN
s_b_connect := FALSE;
ELSE
s_b_connect := TRUE;
END_IF;
ELSIF h_Socket.handle > 0 THEN
nStep := 20;
END_IF;

20:
(* Verbunden, Receive toggeln *)
IF NOT s_b_receive THEN
(* Receive-Flanke bilden *)
s_b_receive := TRUE;
ELSE
s_b_receive := FALSE;
END_IF;

s_b_connect := FALSE;

(* Wenn etwas Empfangen wurde, weiterschalten *)
IF s_n_AnzahlZeichen > 60 THEN
(* Daten wurden empfangen, in den Re-Init springen *);
nStep := 30;
ELSIF TCP_Receive.bError THEN
(* In den disconnect springen *)
nStep := 100;
END_IF;
30:
(* es wurden Daten empfangen *)
nStep := 5;
100:
(* Fehler während des Empfangens, socket schließen und neu verbinden *)
s_b_disconnect := TRUE;
nStep := 5;

END_CASE;



TCP_Connect(
sSrvNetId := '',
sRemoteHost := '10.1.1.16' ,
nRemotePort := 4000,
bExecute := s_b_connect,
tTimeout := T#45s,
bBusy => s_b_ConnectBusy,
bError => s_b_ConnectError,
nErrId => s_n_ConnectError,
hSocket => h_Socket);

TCP_Receive(
sSrvNetId := '',
hSocket := h_Socket,
cbLen := 255,
pDest := ADR(s_s_RecBuffer),
bExecute := s_b_receive,
tTimeout := T#5s,
bBusy => s_b_ReceiveBusy,
bError => s_b_ReceiveError,
nErrId => s_n_ReceiveError,
nRecBytes => s_n_AnzahlZeichen);

TCP_Close(
sSrvNetId := '',
hSocket := h_Socket,
bExecute := s_b_disconnect,
tTimeout := T#5s,
bBusy => s_b_DisconnectBusy ,
bError => s_b_DisconnectError,
nErrId => s_n_DisconnectError);

s_b_disconnect := FALSE;

(* Auswertung der empfangenen Daten *)

IF FIND(s_s_RecBuffer, 'W') > 0 AND enable THEN
(* 'W' als Startzeichen für Wetterdaten gefunden *)
s_s_Wetterdatenstring := MID(s_s_RecBuffer, 21, FIND(s_s_RecBuffer, 'W'));

(* Werte zuweisen *)
(* W+22.8000000N11000.0N *)

io_Wetterdaten.f_Temperatur := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,5,2 ));

t_f_tempREAL := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,2,7 ));
IF CHK_REAL( t_f_tempREAL) = 0 THEN
io_Wetterdaten.n_SonneSued := INT_TO_USINT(REAL_TO_INT( t_f_tempREAL  ));
END_IF;

t_f_tempREAL := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,2,9 ));
IF CHK_REAL( t_f_tempREAL) = 0 THEN
io_Wetterdaten.n_SonneWest :=  INT_TO_USINT(REAL_TO_INT( t_f_tempREAL  ));
END_IF;

t_f_tempREAL := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,2,11 ));
IF CHK_REAL( t_f_tempREAL) = 0 THEN
io_Wetterdaten.n_SonneOst :=  INT_TO_USINT(REAL_TO_INT( t_f_tempREAL  ));
END_IF;

IF MID(s_s_Wetterdatenstring,1,13 ) = 'J' THEN
io_Wetterdaten.b_Daemmerung := TRUE;
ELSE
io_Wetterdaten.b_Daemmerung := FALSE;
END_IF;

t_f_tempREAL := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,3,14 ));
IF CHK_REAL( t_f_tempREAL) = 0 THEN
io_Wetterdaten.n_Daemmerungswert :=  REAL_TO_INT( t_f_tempREAL  );
END_IF;

io_Wetterdaten.f_Windgeschwindigkeit := FLOAT_TO_REAL( FLT := MID(s_s_Wetterdatenstring,4,17 ));

IF MID(s_s_Wetterdatenstring,1,21 ) = 'J' THEN
io_Wetterdaten.b_Regen := TRUE;
ELSE
io_Wetterdaten.b_Regen := FALSE;
END_IF;

END_IF;


Was mir nicht gefällt, ist die Zerhackung des Strings, um an die einzelnen Werte zu kommen. Habt ihr hier einen Verbesserungsvorschlag?

Ansonsten kann ich berichten, dass dieser Setup mit dem Code auf meiner CX9020 läuft  ;)

Viele Grüße!