1wire embedded data systems / eds OW-Server an Beckhoff

Begonnen von ADS_0x1, 18. Dezember 2017, 23:23:07

Vorheriges Thema - Nächstes Thema

0 Mitglieder und 2 Gäste betrachten dieses Thema.

ADS_0x1

Hallo zusammen,

nachdem ich gerade den Eintrag zur Anbindung einer Wetterstation geschrieben habe, dachte ich, ich schreibe auch noch meine Erfahrungen mit meinem onewire Interface nieder. Ich habe mir dazu den OW-Server von embedded data systems zugelegt, da es nach ein wenig Recherche einer der günstigsten 'von der Stange' - Interfaces ist. Ich habe mich an dem Beispiel vom yahoo weatherstation Baustein von oscat orientiert. Prinzipiell funktioniert das ganze sehr gut, aber es gibt ein paar Nachteile:


  • Lediglich 22 1wire devices möglich - und das, obwohl 3 Linien aufgezogen werden können
  • 5 V Spannungsversorgung von extern benötigt, leider keine Unterstützung für 24 V
  • 7.205 Bytes bei 10 DS18B20 und einem iButton - bei den maximal möglichen 22 devices also doppelt so viel Speicher notwendig - das machen nicht alle CPUs mit

Dafür hat er aber auch einige Vorteile:


  • Web-Interface, das auch einfach zum Daten-Logging genutzt werden kann - ganz ohne Steuerung oder Software
  • Konfiguration und Inbetriebnahme sehr einfach durchführbar

In TwinCAT habe ich dazu folgenden Programmcode genutzt:

Datenstrukturen:
TYPE st_oneWireTemperatur :
STRUCT
romID : STRING[16];
name : STRING[32];
temperatur : REAL;
Zeit : DT;
END_STRUCT
END_TYPE


TYPE st_oneWireButton :
STRUCT
romID : STRING[16];
name : STRING[32];
active : BOOL;
Zeit : DT;
END_STRUCT
END_TYPE


Einen Baustein, der den romIDs Namen zuweist:

IF i_b_enable THEN
i := 0;
FOR i:=1 TO 30 BY 1 DO
IF io_a_Temperaturen[i].romID = '83041ABCD434FF28' THEN
io_a_Temperaturen[i].name := '1';
ELSIF io_a_Temperaturen[i].romID = '130417ABCD66FF28' THEN
io_a_Temperaturen[i].name := '2';
ELSIF io_a_Temperaturen[i].romID = 'FD041ABCD542FF28' THEN
io_a_Temperaturen[i].name := '3';
ELSIF io_a_Temperaturen[i].romID = '9F051ABCDAA7FF28' THEN
io_a_Temperaturen[i].name := '4';
ELSIF io_a_Temperaturen[i].romID = 'C9051ABCD840FF28' THEN
io_a_Temperaturen[i].name := '5';
ELSIF io_a_Temperaturen[i].romID = '1A051ABCD127FF28' THEN
io_a_Temperaturen[i].name := '6';
ELSIF io_a_Temperaturen[i].romID = 'C1041ABCD151FF28' THEN
io_a_Temperaturen[i].name := '7';
ELSIF io_a_Temperaturen[i].romID = '21041ABCD26CFF28' THEN
io_a_Temperaturen[i].name := '8';
ELSIF io_a_Temperaturen[i].romID = '3C051ABCD464FF28' THEN
io_a_Temperaturen[i].name := '9';
ELSIF io_a_Temperaturen[i].romID = '530517ABCD9FF28' THEN
io_a_Temperaturen[i].name := 'A';
ELSE
io_a_Temperaturen[i].name := 'kein Name vergeben';
END_IF;
END_FOR;
END_IF;


Und dann noch den Baustein für das Auslesen an sich:
Deklarationspart:
FUNCTION_BLOCK fb_onewireXML
VAR_IN_OUT
IP_C : IP_C; (* IP_Control Verwaltungsstruktur *)
S_BUF : NETWORK_BUFFER;
R_BUF : NETWORK_BUFFER;
io_a_Temperaturen : ARRAY[1..30] OF st_oneWireTemperatur;
io_iButton : st_oneWireButton;
END_VAR
VAR_INPUT
ACTIVATE : BOOL;
IP : DWORD;
i_DayTime : DATE_AND_TIME;
END_VAR
VAR_OUTPUT
BUSY : BOOL;
DONE : BOOL;
ERROR_C : DWORD;
ERROR_T : BYTE;
END_VAR
VAR
CTRL : XML_CONTROL;
XML_READER : XML_READER;
URL_DATA : url;
HTTP_GET : HTTP_GET;
last_state : BOOL;
value_int : INT;
value_real : REAL;
v_real : REAL;
state : INT;
s_n_runNo : USINT;
s_b_dataBlockActive : BOOL;
s_b_iButton_detected : BOOL;
END_VAR


Programmpart:
CASE state OF

00: IF ACTIVATE AND NOT last_state THEN
state := 20;
DONE := FALSE;
BUSY := TRUE;
ERROR_C := DWORD#0;
ERROR_T := BYTE#0;
s_n_runNo := 1; (* Zählvariable zurücksetzen *)
s_b_iButton_detected := FALSE;
END_IF;

20: (* URL for DNS UND HTTP-GET *)
(* Example: http://weather.yahooapis.com/forecastrss?p=94089&u=c *)
URL_DATA:=STRING_TO_URL(STR:='http://10.1.1.15/details.xml',
DEFAULT_PROTOCOL:='',
DEFAULT_PATH:=''
);
  URL_DATA.QUERY := '';
state := 60;

60: IF HTTP_GET.DONE THEN
state := 80;
CTRL.START_POS := HTTP_GET.BODY_START;
CTRL.STOP_POS := HTTP_GET.BODY_STOP;
CTRL.COMMAND := WORD#2#10000000_00001100; (* ONLY TEXT AND ATTRIBUTE *)
CTRL.WATCHDOG  := T#1ms;

ELSIF (HTTP_GET.ERROR > DWORD#00) THEN
(* Fehlerbehandlung *)
ERROR_C := HTTP_GET.ERROR;
ERROR_T := BYTE#02;
state := 100;
END_IF;

80: XML_READER(CTRL:=CTRL,BUF:=R_BUF.BUFFER); (* XML Daten seriell lesen *)
IF CTRL.TYP < 98 THEN (* nur auswerten wenn kein Watchdog durchlauf *)
value_int := 0;
value_real := 0.0;
(* automatische konvertierung in real und int ausführen *)
IF LEN(CTRL.VALUE) <= 20 THEN
v_real:=FLOAT_TO_REAL(FLT:=CTRL.VALUE);
IF CHK_REAL(v_real) = 0 THEN (* !!! prüft auf gültigen REAL Wert, ansonsten stürzt Twincat ab !!!!  *)
value_real := v_real;
value_int := REAL_TO_INT(value_real);
END_IF;
END_IF;

(* Daten aus XML holen: *)
IF s_n_runNo >30 THEN
s_n_runNo := 1;
END_IF;

IF CTRL.ELEMENT = 'Name' AND CTRL.VALUE = 'DS18B20' THEN
s_b_dataBlockActive := TRUE;
END_IF;

IF s_b_dataBlockActive AND CTRL.ELEMENT = 'ROMId' THEN
io_a_Temperaturen[s_n_runNo].romID := CTRL.VALUE;
io_a_Temperaturen[s_n_runNo].Zeit := i_DayTime;
END_IF;

IF s_b_dataBlockActive AND CTRL.ELEMENT = 'Temperature' THEN
io_a_Temperaturen[s_n_runNo].temperatur := value_real;
s_b_dataBlockActive := FALSE;
s_n_runNo := s_n_runNo + 1;
END_IF;

IF NOT s_b_dataBlockActive AND CTRL.ELEMENT = 'Family' AND CTRL.VALUE = '01' THEN
s_b_iButton_detected := TRUE;
END_IF;

IF s_b_iButton_detected AND CTRL.ELEMENT = 'ROMId' THEN
io_iButton.active := TRUE;
io_iButton.romID := CTRL.VALUE;
io_iButton.Zeit := i_DayTime;
END_IF


ELSIF CTRL.TYP = 99 THEN (* EXIT - letztes Element gelesen *)
DONE  := TRUE;

IF NOT s_b_iButton_detected AND io_iButton.active THEN
io_iButton.active := FALSE;
io_iButton.romID := '';
END_IF;

state := 100;
END_IF;

100:
(* UNLOCK HTTP DATA *)
IF (NOT HTTP_GET.DONE) THEN
state := 0;
BUSY := FALSE;
DONE := ERROR_T = BYTE#0;
END_IF;

END_CASE;

(* ------------- HTTP_GET --------------- *)
HTTP_GET( IP_C:=IP_C,
S_BUF:=S_BUF,
R_BUF:=R_BUF,
IP4:=IP,
GET:=state=60,
MODE:=BYTE#2, (* HTTP 1.1 mit persistenter Verbindung *)
UNLOCK_BUF:=state=100,
URL_DATA:=URL_DATA
);

(* nicht oder nicht direkt verwendete Parameter *)
(* STRING := HTTP_GET.HTTP_STATUS;
UINT :=HTTP_GET.HEADER_START;
UINT :=HTTP_GET.HEADER_STOP;
UINT :=HTTP_GET.BODY_START;
UINT :=HTTP_GET.BODY_STOP;
BOOL :=HTTP_GET.DONE;
BYTE :=HTTP_GET.ERROR;
*)
(* -------------------------------------*)

last_state := ACTIVATE;


Ist halt noch ein wenig Misch-Masch aus Vorlage und meiner Art zu Programmieren.

In der Vorlage wird ja immer mit den festen Offsets gearbeitet, das habe ich nicht gemacht, da ich nicht weiß, in welcher Reihenfolge die jeweiligen 1wire devices ausgelesen werden, unterschiedliche Familien haben unterschiedliche viele Elemente, daher bin ich jetzt auf den Element-Vergleich gegangen. Ich habe bei dieser Implementierung keine besondere Erhöhung der Zykluszeit festgestellt, dennoch glaube ich, dass ich nicht gerade optimal programmiert habe (durch die Textvergleiche). Gibt es hier eine Möglichkeit, das ganze sauberer bzw. Ressourcen-schonender zu machen? Ich habe keine Probleme mit der Performance oder Zykluszeit, aber dennoch interessiere ich mich für die Verbesserungen.

Wie groß kann ich auf der CX9020 den Empfangsbuffer einstellen? Ich habe den jetzt auf 8192 Byte hochgeschraubt, damit ich das XML komplett eingelesen bekomme, wenn ich noch 11 Sensoren dran hänge, habe ich Angst, dass irgendwann in die Knie geht.

Könnt ihr eventuell ein anderes Interface empfehlen, das etwas sparsamer mit dem Datenhandling umgeht? Was natürlich mal wieder toll war: Eine Woche, nachdem ich den eds erhalten habe kommt die E-Mail, dass der 1-Wire Controller 1 / intelligente Systemschnittstelle von esera wieder verfügbar ist. der kostet ~60 â,¬ mehr, kann dafür allerdings 30 1wire Geräte, hat eine 24 V Versorgung und es existiert eine Bibliothekt für Wago.

Kommentare gerne gesehen - vielen Dank!

Viele Grüße!