# PC WorX und erster Versuch eines FB in ST



## Portisch (4 Januar 2012)

Hallo,

ich möchte einen RS232_Receive in einem FB programmieren. Als FBD (Graphisch) habe ich es schon geschafft.
Nun möchte ich es aber in ST umsetzen.

Also habe ich mir einen neuen FB angelegt und das mal reingeschrieben:

Variabeln:

```
CMD_INPUT BYTE VAR   0 0 0 0 0 0 0 
Data_Count BYTE VAR   4 0 0 0 0 0 0 
BIT0 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT1 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT2 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT3 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT4 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT5 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT6 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT7 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT8 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT9 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT10 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT11 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT12 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT13 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT14 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
BIT15 BOOL VAR_OUTPUT   FALSE 0 0 0 0 0 0 
Receive_Data RS232_Data VAR    0 0 0 0 0 0 
RS232_RECEIVE_DATA RS232_RECEIVE VAR    0 0 0 0 0 0 
BYTE_TO_BITS BYTE_TO_BITS VAR    0 0 0 0 0 0
```

ST Code:

```
RS232_RECEIVE_DATA(REQUEST:=FALSE,DATA:=Receive_Data);
Receive_Data := RS232_RECEIVE_DATA.DATA;

IF RS232_RECEIVE_DATA.RECEIVE_BUFFER_COUNT = BYTE_TO_INT(Data_Count) THEN
 RS232_RECEIVE_DATA.REQUEST := TRUE;
END_IF;

IF RS232_RECEIVE_DATA.DONE THEN
 IF (Receive_Data[0] = Data_Count) AND (Receive_Data[1] = CMD_INPUT) THEN
  BYTE_TO_BITS(IN:=Receive_Data[2]);
  BIT0 := BYTE_TO_BITS.B0;
  BIT1 := BYTE_TO_BITS.B1;
  BIT2 := BYTE_TO_BITS.B2;
  BIT3 := BYTE_TO_BITS.B3;
  BIT4 := BYTE_TO_BITS.B4;
  BIT5 := BYTE_TO_BITS.B5;
  BIT6 := BYTE_TO_BITS.B6;
  BIT7 := BYTE_TO_BITS.B7;
  BYTE_TO_BITS(IN:=Receive_Data[3]);
  BIT8 := BYTE_TO_BITS.B0;
  BIT9 := BYTE_TO_BITS.B1;
  BIT10 := BYTE_TO_BITS.B2;
  BIT11 := BYTE_TO_BITS.B3;
  BIT12 := BYTE_TO_BITS.B4;
  BIT13 := BYTE_TO_BITS.B5;
  BIT14 := BYTE_TO_BITS.B6;
  BIT15 := BYTE_TO_BITS.B7;
 END_IF;
END_IF;
```

Der Ablauf geht so:
Erst wird gewartet, bis 4 Bytes im RECEIVE_BUFFER_COUNT drinnen sind. Dann soll der REQUEST auf TRUE gesetzt werden um die Daten in meine Receive_Data : Array[0..7] of Byte zu kopieren.

Per DONE wird dann angezeigt, wann das Kopieren fertig ist.
Danach sollen einfach die BIT0-BIT15 gesetzt werden.

Per RS232 kommen diese Daten rein:
Byte 0: Complete len (4)
Byte 1: Command, CMD_INPUT = 0
Byte 2: Data BITs 0..7
Byte 3: Data BITs 8..15

Wenn ich nun Funktionsblock ausführe reagiert er nur einmal und dann gar nicht mehr.
Ich glaube das RS232_RECEIVE_DATA.REQUEST := TRUE; löst das kopieren der Daten nicht aus und somit steigt der Buffer_count immer höher.

Kann hier jemand einen Anfänger etwas helfen!?


----------



## Portisch (5 Januar 2012)

Ich habe es nun zum Laufen gebracht:


```
IF NOT RS232_INIT_DONE THEN
 RETURN;
END_IF;
RS232_RECEIVE_1(REQUEST:=RS232_RECEIVE_1.BUFFER_NOT_EMPTY ,DATA:=Receive_Buffer);
Receive_Buffer:=RS232_RECEIVE_1.DATA;
IF RS232_RECEIVE_1.ERROR THEN
 Error := TRUE;
 Status := RS232_RECEIVE_1.STATUS;
END_IF;
 
IF RS232_RECEIVE_1.DONE THEN
 BYTE_TO_BITS_1(IN:=Receive_Buffer[2]);
 BIT0:=BYTE_TO_BITS_1.B0;
 BIT1:=BYTE_TO_BITS_1.B1;
 BIT2:=BYTE_TO_BITS_1.B2;
 BIT3:=BYTE_TO_BITS_1.B3;
 BIT4:=BYTE_TO_BITS_1.B4;
 BIT5:=BYTE_TO_BITS_1.B5;
 BIT6:=BYTE_TO_BITS_1.B6;
 BIT7:=BYTE_TO_BITS_1.B7;
END_IF;
```

Jedoch scheint der FB von RS232_RECEIVE ein Problem zu haben.
Es geht eine Zeit lang gut, aber danach bleibt RS232_RECEIVE_1.BUFFER_NOT_EMPTY auf TRUE obwohl der Buffer leer ist.
hier hilft dann nur ein Kaltstart damit es wider geht...


----------



## Mobi (9 Januar 2012)

Morgen,

warum weist du überhaupt DATA doppelt zu?

```
RS232_RECEIVE_1(REQUEST:=RS232_RECEIVE_1.BUFFER_NOT_EMPTY ,DATA:=Receive_Buffer); 
Receive_Buffer:=RS232_RECEIVE_1.DATA;
```

Und nutzt du die RS232-Schnittstelle von dem ILC oder hast du eine RS232-Klemme?
Weil dann musst du nämlich andere Bausteine nehmen.


----------



## Portisch (9 Januar 2012)

Mobi schrieb:


> Morgen,
> 
> warum weist du überhaupt DATA doppelt zu?
> 
> ...


Doppelt, da es PC WorX so verlangt. DATA ist ein VAR_IN_OUT.
Wenn ich die untere Zeile weglasse gibt es einen Fehler.

Ich benutze die eingebaute RS232 der ILC - keine extra Klemme.


----------



## Mobi (9 Januar 2012)

Ich hab nun auch mal was probiert, kann es aber nicht testen.


```
EN    BOOL    VAR_INPUT                0    0    0    0    0    0    
BIT0    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT1    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT2    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT3    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT4    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT5    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT6    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT7    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT8    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT9    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT10    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT11    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT12    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT13    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT14    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
BIT15    BOOL    VAR_OUTPUT                0    0    0    0    0    0    
RS232_RECEIVE_1    RS232_RECEIVE    VAR                0    0    0    0    0    0    
RS232_INIT_1    RS232_INIT    VAR                0    0    0    0    0    0    
xRequest    BOOL    VAR                0    0    0    0    0    0    
arrReceiveData    ARRAY_OF_BYTE_0_7    VAR                0    0    0    0    0    0
```


```
RS232_INIT_1(ENABLE:=EN,PARAMETER:=(* ANY *));

RS232_RECEIVE_1(REQUEST:=xRequest,DATA:=arrReceiveData);
arrReceiveData := RS232_RECEIVE_1.DATA;

IF RS232_RECEIVE_1.RECEIVE_BUFFER_COUNT = INT#4 AND RS232_INIT_1.VALID AND NOT xRequest THEN
    xRequest := TRUE;
END_IF;

IF RS232_RECEIVE_1.DONE THEN
    BIT0 := arrReceiveData[2].X0;
    BIT1 := arrReceiveData[2].X1;
    BIT2 := arrReceiveData[2].X2;
    BIT3 := arrReceiveData[2].X3;
    BIT4 := arrReceiveData[2].X4;
    BIT5 := arrReceiveData[2].X5;
    BIT6 := arrReceiveData[2].X6;
    BIT7 := arrReceiveData[2].X7;
    BIT8 := arrReceiveData[3].X0;
    BIT9 := arrReceiveData[3].X1;
    BIT10 := arrReceiveData[3].X2;
    BIT11 := arrReceiveData[3].X3;
    BIT12 := arrReceiveData[3].X4;
    BIT13 := arrReceiveData[3].X5;
    BIT14 := arrReceiveData[3].X6;
    BIT15 := arrReceiveData[3].X7;
    xRequest := FALSE;
END_IF;
```

Was noch fehlt ist natürlich das Error-Handling und die Parameter.
Aber ich wollte nur erstmal den Ablauf bekommen.


----------



## Portisch (10 Januar 2012)

Das habe ich schon so ähnlich gehabt.

Das RS232_INIT habe ich nicht hier untergebracht, da es nur einmal verwendet werden kann im Projekt. Es gibt ja auch noch ein RS232_SEND...
Ich habe (oben nicht drinnen) beim RS232_INIT das über eine globale VAR gelöst.


```
RS232_RECEIVE_1.RECEIVE_BUFFER_COUNT = INT#4
```
Das gab bei mir einige Hänger, denn wenn nun 5 Bytes im Buffer sind wird dieser nie ausgelesen.
Wenn man also >=INT#4 macht gehen aber Bytes verloren falls 5 oder mehr im Buffer sind.

Am liebsten möchte ich Byte für Byte aus dem Buffer lesen.
Das erste Byte sagt mir die Anzahl von Bytes die reinkommen. Danach lese ich nachfolgenden Bytes ein.
So würde ich es in Delphi/C machen.

Wenn man nähmlich z.B. mehrere Bytes an die SPS schickt, ist nicht gesagt das schon alle Bytes im Buffer sind bis ich den Buffer auslese.
Und wenn nun mehrere Bytes schnell hintereinander reinkommen kann es sein wenn ich auf z.B. >=INT#512 warte und dann beim nächten Durchlauf erst auslese das schon z.B. 600 Bytes im Buffer sind. Die über 512 gehen dann verloren.


----------



## Mobi (10 Januar 2012)

Wenn du weißt wie du es in C# machen willst, dann schreib es doch einfach um. Wie stellst du dir denn den Code in C+ vor, vielleicht kann ich das dann so in ST abwandeln.

Wenn du weißt wieviele Bytes kommen, warum wartest du dann nicht einfach bist du alle hast? Sind es immer unterschiedliche Anzahlen von Bytes?


----------



## Portisch (17 Januar 2012)

Eigentlich wollte ich gestern schon Antworten. Scheint aber verloren geganngen zu sein, egal.

Es ist nicht sicher, dass immer die gleiche Anzahl von Bytes reinkommen.
Ich arbeite gerade daran:
http://www.mikrocontroller.net/topic/244302#2503381

Es wird sozusagen per RS232 z.B. 1-Wire Temperatursensoren und aber auch andere Daten zur Verfügung gestellt.
z.B. ein zusätzlicher µC der im Wohnzimmer sitzt und IR-Signale empfangen kann. Somit kann man dann per Fernbedienung Lichter schalten.

Derzeit kommen z.B. die Daten der RS232 so an die SPS:

```
Beispiel der RS232 mit 2 Temp Sensoren:
000 085 011 048 040 033 057 187 003 000 000 009 236 000 000 085 011 048
040 168 056 187 003 000 000 184 236 000

000 sync byte 1
085 sync byte 2
011 data len
048 command byte
040 id
033 id
057 id
187 id
003 id
000 id
000 id
009 id
236 temp int16
000 temp int16 = 23,6°C

000 sync byte 1
085 sync byte 2
011 data len
048 command byte
040 id
168 id
056 id
187 id
003 id
000 id
000 id
184 id
236 temp int16
000 temp int16 = 23,6°C
```

Und oder auch die IR Commands:

```
Wenn nun dieser Code empfangen wird bekommt man das IRMP Command byte:
0x00 0x55 0x02 0x10 0x11

0x00 sync byte 1
0x55 sync byte 2
0x02 data len
0x10 command
0x11 IRMP Command
```

Von der SPS -> µC über die RS232 mache ich das dann z.B. so wie in der AVR_Bus.c.

Was ich eben leider nicht in der SPS abbilden kann. Im µC wird immer Byte für Byte eingelesen. Die ersten 2 Bytes sind SyncBytes und dann das data len Byte um zu wissen wieviele Bytes noch folgen.
Wenn die SPS nun jetzt bei jedem Durchlauf ein Byte einließt und dann im Buffer hat kann ich es mir vorstellen da es ähnlich dem C-Code abläuft.
Wenn nun aber z.B. schon 3 Bytes im Buffer sind und dann beim nächsten Durchlauf die restlichen wird's kompliziert.

Auch wenn ich auf Buffer_Count >= INT#3 warte sind beim nächsten Durchlauf ja schon wieder neue Bytes (vielleicht auch noch nicht alle Bytes der Nachricht) im Buffer.


----------



## Mobi (17 Januar 2012)

Cool. Das wollte ich auch schon machen. Nur hatte ich für den AVR keine Zeit, das ganze zu entwickeln. Ich wollte es machen wegen der IR-Fernbedienung. Das einfachste wäre gewesen, das ganze in PC Worx umzusetzen. Aber nun kann ich ja deins nehmen. 

Also wenn du die Syncbytes bekommst, solltest du erstmal alle Bytes zwischenspeichern. Und dann wenn du alle hast, das ganze auswerten. Ich müsste mir den AVR-Kram mal aufbauen, dann kann ich dir zeigen was ich meine.


----------



## Portisch (17 Januar 2012)

Mobi schrieb:


> Also wenn du die Syncbytes bekommst, solltest du erstmal alle Bytes zwischenspeichern. Und dann wenn du alle hast, das ganze auswerten.



Ok, aber hier scheidet sich mein Verständniss.
Ich sage ich warte bis der Buffer mindestens 2 Bytes enthält. Dann überprüfe ich die 2 Bytes auf das Sync.
Was aber wenn nun 3 Bytes im Buffer sind und das Sync erst beim zweiten anfängt usw.

Wenn bei jedem durchlauf immer 1 Byte gelesen wird sollte es ohne Probleme funktionieren.

EDIT:
Ich glaub jetzt hat im Kopf geklickelt:


> Speicher in den die Daten des Empfangsspeicher der seriellen Schnittstelle
> kopiert werden. Es werden nur so viele Zeichen eingelesen, wie der an DATA
> angelegte Speicher groß ist.



Wenn man also an DATA ein Array mit der Länge 1 anlegt sollte immer nur ein Byte aus dem Buffer gelesen werden. Was aber mit den übrigen geschieht kann ich noch nicht sagen. Ob diese dann sozusagen aufrücken oder verlorengehen (was ich nicht glaube) könnte es funktionieren.


----------



## Mobi (17 Januar 2012)

Mach doch das Array so groß wie das größte Datenpaket groß sein kann. Dann wartest du bis BUFFER_NOT_EMPTY True ist, weil dann weißt du ja das was im Empfangsspeicher steht. Dann füllt er den Speicher und du wartest dann bis sich an RECEIVE_BUFFER_COUNT nix mehr ändert, also 500 ms zum Beispiel. Und dann kannste den Speicher auslesen mit Request.


----------



## Portisch (17 Januar 2012)

Habe es jetzt einmal auf die schnelle versucht:

```
IF NOT RS232_INIT_DONE THEN
 uart_status := INT#0;
 data_len := BYTE#0;
 RETURN;
END_IF;

RS232_RECEIVE_1(REQUEST:=RS232_RECEIVE_1.BUFFER_NOT_EMPTY,DATA:=Receive_Buffer);
Receive_Buffer:=RS232_RECEIVE_1.DATA;

IF RS232_RECEIVE_1.ERROR THEN
 uart_status := INT#0;
 data_len := BYTE#0;
 RETURN;
END_IF;

IF RS232_RECEIVE_1.DONE THEN
 CASE uart_status OF
 0: IF (Receive_Buffer[0] = BYTE#0) THEN
    uart_status := uart_status + INT#1;
  END_IF;
 1: IF( Receive_Buffer[0] = BYTE#16#55) THEN
   uart_status := uart_status + INT#1;
  END_IF;
 2: IF data_len = BYTE#0 THEN
   data_len := Receive_Buffer[0];
  ELSE
   (* read all data bytes *)
   uart_status := INT#0;   
  END_IF;
 END_CASE;
END_IF;
```
Ich schicke nun per RS232 0x00, 0x55, 0x02 an die SPS.
Leider wird aber nur das erste Byte empfangen. Die restlichen scheinen verloren zu gehen.

Receive_Buffer : Array[0..0] of BYTE;


----------



## Mobi (17 Januar 2012)

Receive_Buffer : Array*[0..0]* of BYTE; ?????????????????


----------



## Mobi (17 Januar 2012)

Ich hab nochwas gefunden. Vielleicht hilft es dir ja.


----------



## Portisch (18 Januar 2012)

Ja, ich weiß das Array [0..0] ist eigentlich ein (in Delphi zumindest) Dynamisches Array.
Aber es gibt halt kein Array mit der Länge 1 da es dann ja einfach ein Byte ist.
Das kann man aber als Data beim Receive nicht anhängen.

Aber ich habe das mit dem Tipp mit der Zeit umgesetzt:

```
IF NOT RS232_INIT_DONE THEN
 init := FALSE;
 ReadBuffer := FALSE;
 RETURN;
END_IF;

(* make sure the first cycle works correctly *)
IF NOT init THEN
 init := TRUE;
 last_count := INT#0;
 data_len := BYTE#0;
 ms := UDINT#0;
END_IF;

RS232_RECEIVE(REQUEST:=ReadBuffer ,DATA:=Receive_Buffer);
Receive_Buffer:=RS232_RECEIVE.DATA;

IF RS232_RECEIVE.DONE THEN 
 ReadBuffer := FALSE;
 data_len := INT_TO_BYTE(last_count);
 last_count := INT#0;
 ms := UDINT#0; 
END_IF;

IF last_count <> RS232_RECEIVE.RECEIVE_BUFFER_COUNT THEN
 (* buffer count changed, reset timeout *)
 last_count := RS232_RECEIVE.RECEIVE_BUFFER_COUNT;
 ms := UDINT#0;
ELSE
 IF last_count <> INT#0 THEN
  (* read system time *)
  T_PLC_MS();
  tx:= T_PLC_MS.T_PLC_MS;
  (* add the current milliseconds *)
  ms := (tx - last) + ms;
  IF ms >= UDINT#50 THEN
   ReadBuffer := TRUE;  
  END_IF;
  last := tx;
 END_IF; 
END_IF;
```



> RS232_RECEIVE RS232_RECEIVE VAR    0 0 0 0 0 0
> tx UDINT VAR    0 0 0 0 0 0
> last UDINT VAR    0 0 0 0 0 0
> init BOOL VAR    0 0 0 0 0 0
> ...



Ist noch nicht ganz sauber - geht aber!!
Wenn sich der Buffer Count 50ms nicht mehr ändert werden die Daten aus dem Buffer geholt.

Danach kann der Receive_Buffer abgearbeitet werden...


----------



## Portisch (18 Januar 2012)

Da ich somit gerade an der Auswertung der RS232 Daten arbeite ist mir was aufgefallen:

Es scheint das PC WorX kein ADR und SIZEOF kennt.
Das scheinen aber Standard Operatoren von CodeSys zu sein:
http://www.3s-software.com/index.shtml?de_infosys&infosys=de_adroperator_gloss

Wie bekomme ich die nun für PC WorX?


----------



## Portisch (18 Januar 2012)

Mit den Temperaturauswertungen über RS232 bin ich dann schon fast fertig!
Anbei die 3 Funktionsblöcke.
Diese Empfangen die Bytes und Werten das CMD_TSENSOR_TEMP aus.

Die OneWire Devices haben diese Struct:

```
TYPE
T_TEMPSENSOR : STRUCT
 Name : STRING;
 Temperature : REAL;
 Family : BYTE;
 Serial : STRING;
END_STRUCT;
END_TYPE

TYPE
T_TEMPSENSOR_ARRAY : ARRAY[0..1] OF T_TEMPSENSOR;
T_ONEWIRE : STRUCT
 OneWireDevice : T_TEMPSENSOR_ARRAY;
 DeviceCount : INT;
END_STRUCT;  
END_TYPE
```

Mann muss halt hier die Länge des Arrays anpassen damit es mit den Sensoren zusammen passt.

In der OneWireDevices.ST kann man dann die Devices mit einer String-Serial einen Namen zuweisen.
z.B. Raum 1, Raum 2,....





Für diese Daten wurde dies an die SPS geschickt:
00 55 0B 30 28 21 39 BB 03 00 00 09 D5 00 00 55 0B 30 28 A8 38 BB 03 00 00 B8 05 FF
(HEX Format - eh klar)
-25,1°C wegen Kältespray 

Aber der Code muss noch aufgeräumt werden!




Anzeige mit WebVisit im IE9 geht dann auch recht leicht 
Anhang anzeigen Exports_FB.zip


----------



## Mobi (18 Januar 2012)

Hallo,

also dynamische Arrays gibts schonmal nicht in PC WorX. Und Codesys hat mit PC WorX auch nichts zutun.
Bei PC WorX gibts auch keine Pointer und somit auch kein ADR und SIZEOF. Aber du weißt ja sowieso wie groß das Array ist, da es ja statisch ist. 


Könntest du mal dein Projekt schicken, würde mir das gerne mal im Zusammenhang ansehen.

Ich muss mir dann nur noch die Hardware zusammenstricken. Dann kann ich es auch mal testen.


Hast du schon probiert mit den IR-Signalen?


Aber sonst super, dass es läuft.


----------



## Portisch (19 Januar 2012)

Mobi schrieb:


> Hallo,
> 
> also dynamische Arrays gibts schonmal nicht in PC WorX. Und Codesys hat mit PC WorX auch nichts zutun.
> Bei PC WorX gibts auch keine Pointer und somit auch kein ADR und SIZEOF. Aber du weißt ja sowieso wie groß das Array ist, da es ja statisch ist.
> ...



Das mit den Pointern habe ich dann schon feststellen müssen. Auch das bei PC WorX in Funktionen keine Arrays übergeben werden können. Dazu muss man einen Funktionsblock nehmen.

Das mit dem IR funktioniert schon, ist nur noch nicht umgesetzt. Per IR können Commands 0x00-0xFF eingestellt werden.
Auch kann man im AVR zwei verschiedenen IR-Befehlen (z.B. unterschiedliche Fernbedienungen) das gleiche Command zuweisen usw...

Das µC Projekt ist noch nicht abgeschlossen da mir die Verbindung zwischen den µC noch nicht so gefällt. Wird wahrscheinlich CAN statt "OneWire".
Auch das Programmieren der IR Codes für die Slaves, was derzeit über die RS232 gemacht wird, werde ich wahrscheinlich auf USB umstellen.
Am PC ist die Programmierung der Slaves dann um einiges einfacher und komfortabler.


----------



## Portisch (19 Januar 2012)

Nun hier einmal das RS232 Projekt.

Also einfach mit 9600, 8E1 mit der SPS verbinden und dann z.B. diese Commandos probieren:

*2 Temperatur Sensoren:
*Hex:
00 55 0B 30 28 21 39 BB 03 00 00 09 E5 00 00 55 0B 30 28 A8 38 BB 03 00 00 B8 EE 00
DEC:
000 085 011 048 040 033 057 187 003 000 000 009 229 000 000 085 011 048 040 168 056 187 003 000 000 184 229 000

*IR Command:
*Hex:
00 55 02 10 11
DEC:
000 085 002 016 017

(ich nehme HTerm von hier http://www.der-hammer.info/terminal/)

Wenn ein neuer unbekannter Sensor angeschlossen wird, der nicht definiert ist wird er per Serial-String definiert.
Das "IR_Changed" gibt einen Puls aus wenn ein neues IR_Command empfangen wurde.
Danach kann man das IR_Command mit 265 Möglichkeiten verknüpfen.

Testweise kann ich schon schön mit der Fernbedienung ein Toggle hin und her schalten 





Anhang anzeigen RS232_Test.zip


----------



## Mobi (2 Februar 2012)

Hallo Portisch,

bin nun endlich mal dazu gekommen dein PC Worx Projekt mir anzuschauen. Sieht schonmal klasse aus.
Was mich nur irrtiert, ein PND_IO-Array. Im Busaufbau ist eine ILC 150 ETH. Was machst du denn mit Profinet?

Und kannst du mal den Schaltplan geben von der Haupt-µC-Platine? Möchte mir das gerne mal aufbauen, aber wie gesagt nur
mit dem IR Part.
Wenn du so nett wärst.

Und ich würde an deiner Stelle selbst erstellte Datentypen nicht mit in die sys_flag_types packen sondern in ein eigenes Datentypenblatt.
Vorallem wenn man aus dem Projekt eine Lib macht.

Wieso gibst du den Ausgang IR_Changed auf einen Flankenbaustein?

```
R_TRIG(CLK := IR_Changed);
IR_Changed := R_TRIG.Q;
```


----------



## Portisch (3 Februar 2012)

Anbei einmal ein Schaltplan wie ich mir schon einmal etwas aufgebaut habe.
Meine Platine passt dann in eine Unterputzdose.

Es ist mit einem Atmega168 (SMD)
+ IR Steckplatz (TSOP4838 ) 
+ OneWire Steckplatz
+ RS232
+ USB (V-USB)
+ CAN (MCP2515)

Nach einigen Tests hat sich gezeigt, dass doch CAN das bessere ist um die µC miteinander zu verbinden.
Die Firmware für den µC ist stark im Aufbau und dauert sicher noch ein wenig.

Derzeit habe ich erfolgreich getestet:
OneWire Temperatursensoren, OneWire Humitidy (DS2438 + HIH-4021), IR Empfang (IRMP)



> Und ich würde an deiner Stelle selbst erstellte Datentypen nicht mit in die sys_flag_types packen sondern in ein eigenes Datentypenblatt.
> Vorallem wenn man aus dem Projekt eine Lib macht.


Wusste ich nicht - danke für den Tipp!



> Wieso gibst du den Ausgang IR_Changed auf einen Flankenbaustein?


Da ich es nicht besser weis...
Es sollte eigentlich reichen eine VAR_OUT auf True zu setzen und beim nächsten Durchlauf wieder auf False, oder?

Ich habe auch einmal das "aktuell" Projekt von PC WorX angehängt.
Bei der Auswertung von Temperatur und IR Signalen hat scih schon einiges getan.

Aber ist alles noch Alpha Stadium!
Nix is fix!

Derzeit bin ich noch daran den µC Protokolltechnisch aufzubauen. Es soll dann auch über den "Master" im Schaltschrank über USB ein Firmwareupdate der Slaves (über CAN) möglich sein usw...
Steckt noch eine Menge Arbeit drinnen!

AVR & CAN: http://www.kreatives-chaos.com/artikel/universelle-can-bibliothek
Die haben auch einen CAN-Bootloader.

Anhang anzeigen RS232_Test.zip

Anhang anzeigen AVR_CAN.pdf


----------



## Mobi (13 Februar 2012)

So hab jetzt die Woche frei und wollte mich mal mit dem IR-Teil beschäftigen. Hab nen Atmega16 und einen TSOP1736, damit müsste es ja auch funktionieren. Ich werds dann mal aufbauen mit meinem "schönen" Pollin-Board. Kannst du mal den Code für den Mega posten?


----------



## Portisch (13 Februar 2012)

Den ganzen µC Code nicht - das ist eine zu große Baustelle!

Aber es ist ganz einfach:
http://www.mikrocontroller.net/articles/IRMP

Man besorgt sich das IRMP Paket.
Da ist auch ein Beispiel drinnen, wie man Signale empfängt und auswertet.
Diese Daten dann einfach bei der RS232 rausschieben.
Achtung: 9600, 8*E*1 nicht wie üblich 8*N*1

Hier ein paar Schnipsel:
Überprüfung ob neuer IR-Code empfangen wurde und gleichzeitig eine Entprellung der Fernbedienung:

```
IRMP_DATA irmp_data;
uint8_t RepeatCounter = 0;
uint8_t MinRepeats = 5;
void check_for_new_irmp_data(void)
{
 if (irmp_get_data (&irmp_data))  
 {
  if (!(irmp_data.flags & IRMP_FLAG_REPETITION))    // first check if the IR command is repeated
  {
   RepeatCounter = 0;          // new command received, reset RepeatCounter
  }                 
  else
  { 
   RepeatCounter++;          // still the same command, inc RepeatCounter
  }  
   
  if ((RepeatCounter == 0) | ( RepeatCounter >= MinRepeats)) // only send interrupt if first command, or Repeatcounter >= MinRepeats
  {
   if (RepeatCounter >= MinRepeats) 
    RepeatCounter = MinRepeats;       // fix overflow for long key push
   // Send the new message
   uart_put(IRMP_DECODED, CAN_NODE_NR, (uint8_t *)&irmp_data, sizeof(IRMP_DATA));
  }   
 }
}
```



Und noch die Uart_Put:

```
void uart_put(uint8_t command, uint8_t NodeID, uint8_t data[], uint8_t len)
{
 uint8_t i;
 uart_putc(BUS_SYNC_BYTE_1);
 uart_putc(BUS_SYNC_BYTE_2);
 uart_putc((uint8_t)(sizeof(command) + sizeof(NodeID) + len)); 
 uart_putc(NodeID);
 uart_putc(command);
 
 for (i=0; i < len; i++)
  uart_putc(data[i]);
}
```

Die UART Lib habe ich von http://jump.to/fleury
Die einzige Änderung ist auf 8E1:

```
/* Set frame format: asynchronous, 8data, even parity, 1stop bit */
#ifdef URSEL0
    UCSR0C = (1<<URSEL0)|(3<<UCSZ00)|(1<<UPM01);
#else
    UCSR0C = (3<<UCSZ00) | (1<<UPM01);
#endif
```
(musst du dir aus den code der uart.c raussuchen)

Derzeit werde ich gerade mit dem CAN Bus wahnsinnig.
Es ist einfach blöde wenn man pro CAN-Message nur 8 Bytes hat und man will z.B. 12 verschicken...


----------



## Portisch (13 März 2012)

Ich bin nun schon ein schönes Stück weiter!

Aber die Lösung mit Konstanten gefällt mir so nicht so ganz:

```
(* define temperature sensors here *)
Devices.DeviceCount := 5;

Devices.OneWireDevice[0].Name := 'Raum 1';
Devices.OneWireDevice[0].Serial := '000003BB3921';
Devices.OneWireDevice[0].Index := 0;
Devices.OneWireDevice[0].HumitidySensor := False;
Devices.OneWireDevice[0].IntensitySensor := False;
Devices.OneWireDevice[0].PressureSensor := False;

Devices.OneWireDevice[1].Name := 'Raum 2';
Devices.OneWireDevice[1].Serial := '000003BB38A8';
Devices.OneWireDevice[1].Index := 1;
Devices.OneWireDevice[1].HumitidySensor := False;
Devices.OneWireDevice[1].IntensitySensor := False;
Devices.OneWireDevice[1].PressureSensor := False;

Devices.OneWireDevice[2].Name := 'Raum 3';
Devices.OneWireDevice[2].Serial := '0000015510F6';
Devices.OneWireDevice[2].Index := 2;
Devices.OneWireDevice[2].HumitidySensor := True;
Devices.OneWireDevice[2].IntensitySensor := False;
Devices.OneWireDevice[2].PressureSensor := False;

Devices.OneWireDevice[3].Name := 'Raum 4';
Devices.OneWireDevice[3].Serial := '0000015510EC';
Devices.OneWireDevice[3].Index := 3;
Devices.OneWireDevice[3].HumitidySensor := False;
Devices.OneWireDevice[3].IntensitySensor := True;
Devices.OneWireDevice[3].PressureSensor := False;

Devices.OneWireDevice[4].Name := 'Raum 5';
Devices.OneWireDevice[4].Serial := '0000015606EE';
Devices.OneWireDevice[4].Index := 4;
Devices.OneWireDevice[4].HumitidySensor := False;
Devices.OneWireDevice[4].IntensitySensor := False;
Devices.OneWireDevice[4].PressureSensor := True;
```

Ich habe jetzt einmal in einem Programm hier meine OneWire Sensoren augelistet.
Wird ein Sensor mit der passenden Serial gefunden wird der Name angezeigt.
Auch definiere ich hier ob es sich um einen Humitidy, Pressure oder Itensity Sensor handelt.

Wenn ich aber nun einen Sensor austuasche muss ich das SPS Projekt anpassen.
Kann man das irgendwie schön lösen, dass sich die SPS die Daten von wo anders holt.
Dann nur einen Trigger an die SPS -> lade Daten neu oder so.

Danke!


----------



## Mobi (13 März 2012)

Du könntest über eine selbstgeschriebene Client Anwendung Sensoren hinzufügen über TCP/IP. Wenn die DeviceCount begrenzt ist kannst du ja dann die Sensoren einfach einfügen in das remanente Array. Wenn nicht würde ich die in einer CSV schreiben auf dem FTP.

Kann den der Sensor alle drei Typen gleichzeitig haben?

```
Devices.OneWireDevice[2].HumitidySensor := True; Devices.OneWireDevice[2].IntensitySensor := False; Devices.OneWireDevice[2].PressureSensor := False;
```
Wenn nicht reicht doch ein Eintrag wo du den Typ angibst.


----------



## Portisch (13 März 2012)

Ging ja schnell!



> Du könntest über eine selbstgeschriebene Client Anwendung Sensoren hinzufügen über TCP/IP. Wenn die DeviceCount begrenzt ist kannst du ja dann die Sensoren einfach einfügen in das remanente Array. Wenn nicht würde ich die in einer CSV schreiben auf dem FTP.


Das mit TCP gefällt mir nicht so, da die SPS wirklich ohne externes Zutun starten soll. Ansonsten müsste ich ja die Daten bei einem Neustart/Stromausfall die Daten wieder runterschieben. Da gefällt mir die Lösung mit einer CSV auf dem FTP schon besser.

Gibt es zu CSV ein fertigen FB? Oder muss man sich das String zerlegen selber zusammenbauen?



> Wenn nicht reicht doch ein Eintrag wo du den Typ angibst.


Wenn zu 100% sicher ist, dass die Startwerte auf FALSE sind? Oder muss ich das auch selber in einer FOR-Schleife erledigen?


----------



## Mobi (13 März 2012)

Über TCP sollen doch nur die Daten reinkommen. Speichern kannst du die in einem Array, so wie jetzt, oder in der CSV. Die Daten würde ich als XML Schema übertragen.

Könnte sein, dass es schon einen fertigen FB gibt, spätestens wenn ich ihn schreibe. 

Warum sollen die Startwerte denn auf False stehen? Ich würde das eher so machen. Ein Hex-Wert gibt den Typ an.


Humitidy = 0x01
Intensity = 0x02
Pressure = 0x04

Wenn du mal einen Sensor hast der z.B. Humitidy und Pressure kann lautet der Wert 0x05.


----------



## Portisch (13 März 2012)

Das mit dem Sensor Typ über Bits zu definieren gefällt mir! Danke!

Ich habe einmal die Oscat Lib überflogen - da scheint was für XML drinnen zu sein.
XML gefällt mir persönlich auch besser als CSV.

Hört sich so gut an:
Daten per TCP an die SPS, die legt es in einem XML auf dem FTP ab.
Oder XML direkt auf den FTP und dann die SPS die Datei neu einlesen lassen. Wär weniger Arbeit.
Mal sehen wie sich das am einfachsten Umsetzen lässt.


----------



## Mobi (13 März 2012)

Ich meinte eigentlich die Daten im XML-Scheme zu übertragen und dann in die CSV zu schreiben. Aber wenn du es auch als XML speichern willst...ok.


----------



## Mobi (13 März 2012)

Hab mal schnell nen Client geschrieben um es dir mal zu zeigen was ich meine. Als IP kannste ja die von deinem Router nehmen und Port 80, nur damit die Felder alle freigeschaltet sind. Das Anhaken des Sensortyps funktioniert schonmal nur der Button "Device hinzufügen" ist noch ohne Funktion.


----------



## Portisch (22 März 2012)

Werde ich mir noch ansehen! Danke!

Ich habe jetzt aber dazu noch eine Frage wegen dem RS232_RECEIVE.

ich habe das in dem Default_Task drinnen und es wird somit bei jedem Zyklus ausgeführt.
Dadurch steigt aber die CPU auf ~30%. Ohne dem Receive habe ich ~3%.
Es ist egal ob REQUEST auf TRUE oder FALSE steht.

Ist das egal oder sollte man hier etwas verbessern?


----------



## Mobi (1 Februar 2013)

Hallo Hugo,

ich hab aktuell ein Projekt, da möchte ich auch gerne 1-Wire verwenden. Aber ich möchte das gerne direkt an die RS232 vom ILC hängen. Natürlich mit einem Interface dazwischen z.B. DS2480B oder so. Wie läuft inzwischen deine Sache so?


----------



## Portisch (4 Februar 2013)

Eigentlich zu 100% Stabil 

Anbei mal mein RS232 Part und mein Command Handling von der SPS.
Die AVR senden ca. alle 3min neue Sensor Daten an die SPS um den Bus nicht zu überfordern.

Das AVR Projekt könnte ich auch mal zur Verfügung stellen. Ist halt ein Projekt auf meine Anforderungen abgestimmt.
Da ist dann ja auch noch ein CAN mit drinnen, um mehrere AVRs miteinander zu verbinden.


----------

