# Unity Pro als Master und Modbus Slave



## eYe (28 April 2008)

Mit folgendem Quelltext versuche ich Daten aus einem Slave über Modbus auszulesen.

*IF NOT %MW4100.0 THEN 
READ_VAR(ADDR('0.3.1.1');'%MW',0,40,%MW4100:4,%MW4200:40);
%MW4102:=5; %MW4103:=80;
END_IF;*

Ich kann in der Kommunikationsübersicht des verwendeten Slave sehen, dass die Kommunikation soweit steht. Es wir eine Zykluszeit von ~130ms angegeben und er erkennt auch das ich ab Register 0 die Anzahl von 40 Wörtern lesen möchte. Laut Hersteller sollte damit alles in Ordnung sein.
Ich habe nun aber das Problem, das ich leider keinen Wert übertragen bekomme. In den Wörter 4200 und folgend steht überall 0 drinne.
(Unter anderem sollte dort zum Beispiel die Uhrzeit des Slave drinne stehen...)

In Unity Pro wird mir keine Fehlermeldung angegeben, die PCMCIA Karte in Kanal 1 blinkt vor sich hin und es erscheint auch keine Fehlermeldung in den Berichtswörtern. In den Wörter für den Betriebsbericht und den Kommunikationsbericht steht dauerhaft null drinne.
Nur die Austauschnummer wird fortlaufend hochgezählt und das Aktivitätsbit steht dauerhaft auf eins. 
Ich habe anhand einer Flankenerkennung einen Zähler gebaut welcher allerdings nur bei Neustart der Anlage einmal auf 1 zählt und diesen Wert hält, sprich das Aktivitätsbit wird anscheinend nicht getoggled...

Ich würde mich sehr freuen wenn mir jemand ein paar Lösungsvorschläge unterbreiten könnte 

Greetz, eYe


----------



## MarkusP (29 April 2008)

Hallo,

ich habe zwar PL7-Programme immer nur nach Unity importiert, der Aufruf sollte jedoch ähnlich sein. In PL7 würde dein Aufruf bei mir in etwa so aussehen:


```
IF NOT %MW4000:x0 THEN     (*Aktivitätsbit aus*)
  (*%MW4000:=0;  (*Austauschnummer initialisieren*)
  (*%MW4001:=0;  (*Operations-/Kommunikationsrückmeldung initialisieren*)
  %MW4002:=5;     (*Timeout*)
  %MW4003:=80;   (*Länge 40 Wörter = 80 Byte*)
  READ_VAR(ADR#0.3.1.1,'%MW',0,40,%MW4200:40,%MW4100:4);
END_IF;
```
Der einzige Unterschied zu deinem Programm scheint in der Reihenfolge zu bestehen. Prinzipiell müssen die Kommunikationsparameter immer vor dem Aufruf zugewiesen werden, die Austauschnummer sowie die Operations-/Kommunikationsrückmeldung darf nur bei Warmstart und Kaltstart initialisiert werden. (daher oben in (**) gesetzt, in den richtigen Programmteil kopieren) Weiters müssen die Parameter des Kommunikationskanals sinnvoll zu %MW4002 sein. Übrigens könnte ich über Kommunikation im Zusammenhang mit TSX-Mikro, TSX-Premium, TWIDO etc. ein wenn nicht mehrere Bücher schreiben 

Prinzipiell sollte es so schon klappen.

Schönen Abend.


----------



## eYe (29 April 2008)

Danke für die Antwort 

Habe den Fehler heute gefunden, wenn ich hier nicht nur mit einem Nickname angemeldet wäre würde ich mich gkatt schämen es zu sagen ^^
Es lag an den Einstellungen der Parität...lol

Naja hab das Ganze nun heute komplett zum laufen bekommen, mußte zwar noch etwas kämpfen da es 3 Slaves sind und ich immer nur eine Funktion zur Zeit aufrufen dard, weil sich sonst der Bus aufhängt.
Aber nun gehts ganz gut, dank Taktmerker 

Nochmal danke für die Hilfe


----------



## MarkusP (29 April 2008)

Ja ja, die tapferen SCHNEIDERlein und die Kommunikation...

Vergiss aber bitte nicht die Initialisierung bei Warm- und Kaltstart, sonst kann sich das schon mal aufhängen. Bei mehreren Slaves musst du zudem aufpassen, von welchem Slave die letzten Antwort war. (aber nur falls du idente Speicherbereiche für dein READ_VAR verwendest, und mal ein Slave nicht am Bus sein sollte) 

Schönen Abend


----------



## eYe (3 Mai 2008)

Hallo MarkusP,

ich hoffe du hattest ein schönes Wochenende und kannst mir nun gutgelaunt bei folgendem Problem behilflich sein 

Ich habe mit der Modbus Kommunikation nur noch ein Problem, welches aber leider sehr schwerwiegend ist. Und zwar läuft alles einwandfrei bis es sporadisch zum Zusammenbruch der Kommunikation kommt. Die Zeit nach Neustart der Anlage ist dabei unterschiedlich und liegt meist bei einigen Stunden. Bis zum Zusammenbruch werden alle Daten richtig gelesen und geschrieben, danach tut such gar nichts mehr und im Kommunikationsbericht erscheinen bei allen sechs Funktionen abwechselnd die Fehlercodes #01 und #07.

Ich hoffe das du mir anhand des Codes bei der Lösung des Problems behilflich sein kannst.

Danke im vorraus, eYe


```
(*Zeittakt %S5=100ms*)
 FBI_12 (CLK := %S5,
         RISE => RE,
         EDGE => FE,
         FALL => RFE);
 
 IF RE THEN INC(%MW4900) ;
 END_IF;
 
 IF (%MW4900>11) THEN %MW4900:=0;
 END_IF;
 
 (*Symap 1 lesen*)
 IF NOT %MW4110.0 AND %MW4900=0 THEN (*Aktivitätsbit und Zyklusbit*)
 %MW4112:=5; %MW4113:=80; (*500ms Timeout und 80 Byte Datenlänge*)
 READ_VAR(ADDR('0.3.1.1'), '%MW', 0, 40, %MW4110:4, %MW4200:40); (*Addrese, Typ, Anfang, Anzahl, Parameter, Speicherbereich*)
 END_IF;
 
 (*Symap 1 schreiben*)
 IF NOT %MW4114.0 AND %MW4900=2 THEN (*Aktivitätsbit und Zyklusbit*)
 %MW4116:=5; %MW4117:=2; (*500ms Timeout und 80 Byte Datenlänge*)
 WRITE_VAR(ADDR('0.3.1.1'), '%MW', 6, 1, %MW4240:1, %MW4114:4); (*Addrese, Typ, Anfang, Anzahl, Speicherbereich, Parameter*)
 END_IF;
 
 
 (*Symap 2 lesen*)
 IF NOT %MW4120.0 AND %MW4900=4 THEN
 %MW4122:=5; %MW4123:=80;
 READ_VAR(ADDR('0.3.1.2'), '%MW', 0, 40, %MW4120:4, %MW4300:40);
 END_IF;
 
 (*Symap 2 schreiben*)
 IF NOT %MW4124.0 AND %MW4900=6 THEN
 %MW4126:=5; %MW4127:=2;
 WRITE_VAR(ADDR('0.3.1.2'), '%MW', 6, 1, %MW4241:1, %MW4124:4);
 END_IF;
 
 
 (*Symap 3 lesen*)
 IF NOT %MW4130.0 AND %MW4900=8 THEN
 %MW4132:=5; %MW4133:=80;
 READ_VAR(ADDR('0.3.1.3'), '%MW', 0, 40, %MW4130:4, %MW4400:40);
 END_IF;
 
 (*Symap 3 schreiben*)
 IF NOT %MW4134.0 AND %MW4900=10 THEN
 %MW4136:=5; %MW4137:=2;
 WRITE_VAR(ADDR('0.3.1.3'), '%MW', 6, 1, %MW4242:1, %MW4134:4);
 END_IF;
```


----------



## MarkusP (5 Mai 2008)

Guten Morgen!

Warum soll es dir anders gehen, als mir....

Also, wenn ich Deinen Code so ansehe, fällt mir folgendes auf:

Du inkrementierst deinen Stationszähler im Takt von 100ms, das Timeout ist jedoch höher. (500 ms)
Falls im Kommunikationsbericht tatsächlich nur 16#0001 bzw. 16#0007 steht, bedeutet das folgendes:

16#01 Austausch-Stop bei Timeout
16#07 Problem bei Senden zum Empfänger

Demnach antworten die Slaves nicht mehr. (warum auch immer)
Wie ist der Timeout in der Karte eingestellt. (muss kürzer sein, als 500 ms)
Kannst Du mir die Kartenkonfig hereinstellen? (Wiederholungen etc.)

Weiters muss der Aufruf einer Kommunikation immer auf Flanke sein!!!!
Du zählst zwar die Stationen über Flanke weiter, jedoch nicht den Kommunikationsaufruf, d.h. es könnten u.U. mehrere Aufrufe gleicheitig erfolgen. Ändere deine Abfrage, bei Bedarf helfe ich dir gerne weiter.

Wie gesagt, ich habe auch schon sehr viel Lehrgeld über Jahre hinweg bezahlt.

LG


----------



## eYe (5 Mai 2008)

Hallo MarkusP,

erstmal danke für deine Antwort. Es ist immer schön wenn man das Gefühl hat nicht ganz alleine zu sein 

Die Kartenkonfiguration habe ich in den Anhang gepackt, die Baugruppe ist eine SCY 21601 und davon nutze ich Kanal 1 mit einer SCP114/1114. Allerdings habe ich den Konfig nihcts gefunden wo ich den Timeout einstellen kann, sondern nur in den Paramater der Schreib/Lese Funktionen.
Was schlägst du als einen sinnvollen Wert für den Timeout vor? 

Meine Kommunikation unterliegt zwar keinen Echtzeitbedingungen, aber ich finde das eine Zykluszeit von 1200ms schon recht hoch ist. Meinst du die 100ms pro Slave/Pause sind zu wenig?

Deine Vermutung das eventuell mehrere Aufrufe pro Takt stattfinden habe ich auch schon vermutet, nur leider hatte ich noch nicht die zündende Idee wie ich sichergehen kann das auch wirklich nur ein Lese/Schreibvorgang stattfindet.
Sollte ich eventuell auf den Taktmerker verzichten und das ganze besser in einer Schleife programmieren, also wenn Lesen 1 fertig, dann starte Schreiben 1 etc...?
Würde mich interessieren wie du so etwas gelöst hast 

Vielen Dank, eYe


----------



## MarkusP (5 Mai 2008)

Also deine Kartentimeout beträgt 3 Sekunden! 3 Wiederholungen mit je bis zu 10x100 ms Wartezeit. Die Wartezeit der Hardware sollte lt. meinen Informationen niederer sein, als die in der Software (500 ms) angegebene.


> ...Zykluszeit von 1200ms


also z.b. liest momentan jeder deiner READ_VAR für die Dauer von 100 ms so schnell und oft wie möglich die Daten. Entweder Du machst für jeden Aufruf eine eigene flankenausgelöste Aktion, oder du zählst einfach mit jedem Wechsel des Aktivitätsbit von 0-->1 deinen Stationszähler und löst dann die Aktion auf. Aber Achtung: Du solltest Abfangen, dass deine Weiterschaltbedingung sich nicht aufhängen kann --> Zeitüberwachung.

Wenn du willst, kann ich dir einen beispielhaften PL7-Code (ist ja ziemlich das selbe) unverbindlich zukommen lassen.

LG


----------



## eYe (5 Mai 2008)

Ich werde die Einstellungen in der Karte mal so abändern das ich nur eine Wiederholung mit 100ms habe, mal schauen ob es was bringt.

Über ein Beispiel deinerseits wäre ich sehr dankbar, Quellcode der schon erprobt ist kann nie Schaden 

Ansonsten bin ich gerade dabei deinen Vorschlag aufzugreifen und versuche anhand der fallenden Flanke des Aktivitätsbit die Funktion zu beenden, dann 100ms zu warten und danach die nächste Funktion zu starten...

Darf ich dir hiermit schonmal ein Kasten "Cyberbier" übergeben, als kleines Danke schön für deine Mühe ^^

Danke, eYe


schick am besten an eYe@rokket.de


----------



## MarkusP (5 Mai 2008)

> bin ich gerade dabei deinen Vorschlag aufzugreifen und versuche anhand der fallenden Flanke des Aktivitätsbit die Funktion zu beenden, dann 100ms zu warten und danach die nächste Funktion zu starten...


 
Hier wie erwähnt unbedingt aufpassen, dass sich der Code unter keinen Umständen "aufhängen" kann, also nicht mehr weitergeschaltet wird.

Hier ein Beispiel in PL7 (allerdings mit anderen Requests):


```
!
IF Init THEN 
 Ad_slave_1_1:6:=ADR#1.1.1; (* Slave @1, MODBUS Knoten 1, Softstarter P030201  *)
 Ad_slave_2_1:6:=ADR#1.1.2; (* Slave @2, MODBUS Knoten 2, Softstarter P030301  *)
 Ad_slave_3_1:6:=ADR#1.1.3; (* Slave @3, MODBUS Knoten 3, Softstarter P030401  *)
 Ad_slave_4_1:6:=ADR#1.1.4; (* Slave @4, MODBUS Knoten 4, Softstarter P030801  *)
 Ad_slave_5_1:6:=ADR#1.1.5; (* Slave @5, MODBUS Knoten 5, Softstarter P030901  *)
 Ad_slave_6_1:6:=ADR#1.1.6; (* Slave @6, MODBUS Knoten 6, VACON Umrichter P031001  *) 
 Ad_slave_7_1:6:=ADR#1.1.7; (* Slave @7, MODBUS Knoten 7, VACON Umrichter P031101  *)
 Ad_slave_8_1:6:=ADR#1.1.8; (* Slave @8, MODBUS Knoten 8, JANITZA Leistungsmessung *)
 Ad_slave_9_1:6:=ADR#1.1.9; (* Slave @9, MODBUS Knoten 9, JANITZA Leistungsmessung *)  
END_IF;
(* Derzeit 9 Stationen *)
IF Mb1_station>9 THEN Mb1_station:=1;END_IF;
!
(* Die Antwort der Slaves befindet sich im Empfangsbuffer ab Wort 3 *)
(* Wort 1 und 2 enthält die Rückmeldung des MODBUS-Reqests    *)
Mb1_request_finished:=NOT Mb1_austauschnummer_read:X0 OR Init;
Mb1_data_valid:=(Empfangsdaten_1=0);  (* Wort 1, Aktion erfolgreich = 0 *)
IF RE Mb1_request_finished THEN
 IF Mb1_data_valid THEN
  IF Mb1_station=1 AND P030201_fehlerroutine THEN
   ROR1_ARB(%MB1740:30);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:15); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P030201_eta6:12:=Empfangsdaten_3:12;
  ELSIF Mb1_station=2 AND P030301_fehlerroutine THEN
   ROR1_ARB(%MB1740:30);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:15); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P030301_eta6:12:=Empfangsdaten_3:12;
  ELSIF Mb1_station=3 AND P030401_fehlerroutine THEN
   ROR1_ARB(%MB1740:30);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:15); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P030401_eta6:12:=Empfangsdaten_3:12;
  ELSIF Mb1_station=4 AND P030801_fehlerroutine THEN
   ROR1_ARB(%MB1740:30);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:15); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P030801_eta6:12:=Empfangsdaten_3:12;
  ELSIF Mb1_station=5 AND P030901_fehlerroutine THEN
   ROR1_ARB(%MB1740:30);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:15); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P030901_eta6:12:=Empfangsdaten_3:12;
  ELSIF Mb1_station=6 AND P031001_betrieb THEN
   ROR1_ARB(%MB1740:46);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:23); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P031001_n1:20:=Empfangsdaten_3:20;
  ELSIF Mb1_station=7 AND P031101_betrieb THEN
   ROR1_ARB(%MB1740:46);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:23); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   P031101_n1:20:=Empfangsdaten_3:20; 
  ELSIF Mb1_station=8 THEN 
   ROR1_ARB(%MB1740:46);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:23); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   Umg96s_1_data_1:20:=Empfangsdaten_3:20;
  ELSIF Mb1_station=9 AND Ersetzen THEN 
   ROR1_ARB(%MB1740:46);  (* Empfangstabelle um 1 Byte verschoben, ROR1...   *)
   SWAP(Empfangsdaten_1:23); (* alle HI- und LO-Bytes umdrehen, MODBUS    *)
   Umg96s_2_data_1:20:=Empfangsdaten_3:20; 
  END_IF;   
 END_IF;
 INC Mb1_station;
END_IF;
 
(* Daten MODBUS, Knoten 1, Softstarter P030201 lesen *)
(* Angezeigte Parameter, W4061-W4072 *)
IF NOT Mb1_austauschnummer_read:X0 AND(Mb1_station=1)THEN
 IF P030201_fehlerroutine THEN  (* Softstarter eingeschaltet *)
(*  Mb1_austauschnummer_read:=0; (* Austauschnummer initialisieren      *)
(*  Mb1_rueckmeldung_read:=0; (* Operations-/Kommunikationsrückmeldung    *)
  Mb1_timeout_read:=50;  (* Timeout 50 x 0.1 sec        *)
  Mb1_laenge_read:=10;  (* Länge Requesttabelle 5 Wörter (10 Byte)    *)
  Read_request_1:=16#0004;  (* READ INPUT REGISTER, MODBUS FUNKTION 4    *)
  Read_request_2:=16#0296;  (* ENTSPRICHT DER IDENTIFIKATION DER MODBUS-FUNKTION  *)
  Read_request_3:=16#0000;  (* 0 RESERVIERT        *)
  Read_request_4:=16#DD0F;  (* W4061, ADRESSE DES ERSTEN ZU LESENDEN WORTES, HI- LO-BYTE INVERTIERT  *)
  Read_request_5:=16#0C00;  (* 12 ZU LESENDE WÖRTER, HI- LO-BYTE INVERTIERT       *)
  (* Achtung: Empfangstabelle muss um 3 Wörter größer sein, wegen Rückmeldung des Request !!    *)
  SEND_REQ(Ad_slave_1_1:6,16#009F,Read_request_1:5,Empfangsdaten_1:15,Mb1_austauschnummer_read:4);  
 ELSE
  INC Mb1_station;
 END_IF;
END_IF;
 
(* Daten MODBUS, Knoten 2, Softstarter P030301 lesen *)
(* Angezeigte Parameter, W4061-W4072 *)
IF NOT Mb1_austauschnummer_read:X0 AND(Mb1_station=2)THEN
 IF P030301_fehlerroutine THEN  (* Softstarter eingeschaltet *)
(*  Mb1_austauschnummer_read:=0; (* Austauschnummer initialisieren      *)
(*  Mb1_rueckmeldung_read:=0; (* Operations-/Kommunikationsrückmeldung    *)
  Mb1_timeout_read:=50;  (* Timeout 50 x 0.1 sec        *)
  Mb1_laenge_read:=10;  (* Länge Requesttabelle 5 Wörter (10 Byte)    *)
  Read_request_1:=16#0004;  (* READ INPUT REGISTER, MODBUS FUNKTION 4    *)
  Read_request_2:=16#0296;  (* ENTSPRICHT DER IDENTIFIKATION DER MODBUS-FUNKTION  *)
  Read_request_3:=16#0000;  (* 0 RESERVIERT        *)
  Read_request_4:=16#DD0F;  (* W4061, ADRESSE DES ERSTEN ZU LESENDEN WORTES, HI- LO-BYTE INVERTIERT  *)
  Read_request_5:=16#0C00;  (* 12 ZU LESENDE WÖRTER, HI- LO-BYTE INVERTIERT       *)
  (* Achtung: Empfangstabelle muss um 3 Wörter größer sein, wegen Rückmeldung des Request !!    *)
  SEND_REQ(Ad_slave_2_1:6,16#009F,Read_request_1:5,Empfangsdaten_1:15,Mb1_austauschnummer_read:4);  
 ELSE
  INC Mb1_station;
 END_IF;
END_IF;
 
usw. usw.
 
Vergiss nicht die Initialisierung der Komm.parameter bei Warm- und Kaltstart.
```
 
Ich hoffe das Prinzip hilft dir weiter.

LG


----------



## eYe (5 Mai 2008)

Oh man, das sieht alles andere als mal eben dahingeschrieben aus 

Also eigentlich bin ich ja ned so der Siemens Fan, aber ich muss ehrlich sagen das mir die Kommunikation über Profibus doch um einiges angenehmer ist 

Hab mein Programm nun soweit das die Berichtswörter initialisiert werden, die Funktion nur noch 1 mal beim entsprechenden Takt aufgerufen wird.
Mittlerweile läuft es schon besser, nun hängt sich nur ab und an mal ein Teilnehmer auf :/
Dafür habe ich nun nee Fehlerabfrage eingebaut, die im Notfall die Kommunikation resetet. Nur leider habe ich dann festgestellt das gar nichts mehr geht wenn ich einen Teilnehmer ausschalte, da die Kommunikation dann ständig resetet wird ^^
Naja, mein Chef hat die Geduld verloren und nun kommt Morgen einer aus dem Hause Schneider und wird mir das Ganze wohl mal zurechtproggen müssen. (Denke mal er packt seinen fertigen DFB aus und gut ist...)

Nur schade das diesbezüglich die Hilfe echt fürn Arsch ist, da steht ja nicht mal annähernd drinne was man alles beachten muss wenn man eine vernünftige Kommunikation aufbauen will...

Hatte eigentlich gehofft das selber hinzubekommen, denn nur die eigenen Sachen versteht man richtig und lassen sich im Notfall schnell umprogrammieren.

Aber nicht desto trotz noch einmal vielen Dank!


----------



## Bernarnold (16 Januar 2009)

*Kopplung + Premium*

Hi , 

Ich brauche Hilfe!!!

Ich Habe 5 premium (TSX572623M) die ich miteinander verkoppeln muss und damit sie daten austauschen können (Ethernet / TCP/IP). Entwicklung mit Unity pro. Mit quantum habe ich sowas ift gemacht (MBP_MSTR). Gibt es sowas wie diese MBP_MSTR unter unity für Premium steuerungen?


----------



## Bernarnold (16 Januar 2009)

Ich brauche Hilfe!!!

Ich Habe 5 premium (TSX572623M) die ich miteinander verkoppeln muss und damit sie daten austauschen können (Ethernet / TCP/IP). Entwicklung mit Unity pro. Mit quantum habe ich sowas ift gemacht (MBP_MSTR). Gibt es sowas wie diese MBP_MSTR unter unity für Premium steuerungen? 
http://www.spsforen.com/post_thanks...4706-9cccd1edbcedc9c5e227c62d9c919edc79d70fc7​


----------

