# Auslesen eines Temperatursensors über MODBUS an WAGO SPS



## SPS_A (12 Juni 2013)

Hallo liebe SPSler,

ich bin seit einigen Tagen Neueinsteiger in das Thema SPS, CoDeSys etc. Einige Sachen konnte ich schon hier im Forum nachlesen, und das hat mir den ersten Einstieg auch ermöglicht. Jetzt geht es für mich darum, einen Temperatursensor an eine SPS anzuschließen und über das MODBUS Protokoll auszulesen. So ganz ans Ziel komme ich leider noch nicht. Vielleicht einmal die wichtigsten Sachen zum System und der Konfiguration:
WAGO 750-881 Controller
750-653 MODBUS Klemme (die frei konfigurierbare)
Über die MODBUS Bibliothek sind die beiden notwendigen Bausteine (Extended Slave und Master) konfiguriert. Die Baudrate, Parität etc. habe ich dabei nach der Modbusmap des Herstellers für beide Bausteine eingestellt:

http://www.datanab.com/zc/docs/datanab/other/MBus_AO_RTH_CO2_LCD__ModbusMap.pdf
Den Modbus Dienst (function code) definiere ich ja über die Extended Query Abfrage.

Einloggen kann ich mich nach Einstellen der IP etc., das klappt soweit. Da ich in diesem Thema aber wie gesagt komplett neu bin, fehlt mir das Verständnis für das weitere Vorgehen, also dem konkreten Abfragen des Sensors. Kann mir da vielleicht jemand einen Hinweis geben? Ich bin mir auch nicht ganz im Klaren darüber, wie die register in der Codesys eingegeben werden. Und wie muss ich in den Variablen der Klemme etwas konfigurieren? Dort sind ja unter der Steuerungskonfiguration 10 Eingangs- sowie 9 Ausgangsvariablen (Transmisison acknowledgment, etc.) angegeben, aber welche wie konfiguriert werden müssen ist mir leider nicht klar.

Ich würde mich freuen, wenn jemand Ideen hat, wie das ganze ans laufen gebracht werden kann. Ich geb natürlich auch gerne weitere notwendige Angaben zu der aktuellen Konfiguration durch.

Vielen Dank schon mal, viele Grüße


----------



## Snert (13 Juni 2013)

Hallo SPS_A,

zum Einstieg vielleicht mal einige grundlegende Infos:

- Die 750-653 gestattet den Aufbau einer RS485-Strecke. Die beiden Leiter sind jeweils an der 653-Klemme und dem Sensor anzuschliessen. Je nach Datenrate, Leistungslänge und Störfaktoren über die Leitungsabschlüsse/Terminierung nachdenken. 
- Der Sensor benötigt zwingend eine eindeutige UnitID für die Modbuskommunikation (RS485 ist ein Bus mit evtl. mehreren Teilnehmren).
- Als Software benötigst du nur die Master-Variante. Der Sensor ist Slave und läßt sich vom Master befragen.
- Das Modbus-Protokoll ist ein Frage-Antwort-Spiel, soll heißen: Der Master schickt ein Telegramm und der Slave antwortet. Das Master-Telegramm kann eine Datenabfrage sein oder ein Schreibbefehl für Parameter bzw. Daten allgemein. 
- Der Sensor kennt zwei Datenbereiche: LIVE VALUES und CONFIG VALUES. Die CONFIG VALUES sind nur einmalig einzulesen bzw. zu setzen. Im laufenden Betrieb interessieren nur noch die LIVE VALUES. 
- Zum Lesen und Schreiben kennt das Modbus-Protokoll unterschiedliche Funktionen (Funktionscodes). Zusätzlich wird unterschieden, auf welchen Registerbereich (Eingangsdaten, Merker, Ausgangsdaten) das Lesen und Schreiben angewendet werden soll. Die LIVE VALUES sind Eingangsdaten und sollen gelesen werden. Daher ist hier der Funktionscode 4 (=Read Input Register) zu verwenden. Die CONFIG VALUES liegen im Merkerbereich (Holding Register). Sie sind mit der Funktion 3 (Read Holding Register) zu lesen. Geschrieben werden die CONFIG VALUES entsprechend mit der Funktion 6 (Write Single Register). Da hier nur die Funktion 6 genannt ist, lassen sich vermutlich nicht mehrere Werte in einem Telegramm beschreiben, sondern jeder Parameter nur einzeln.
- Je nach verwendetem Masterbaustein für Modbus werden die Adressbereiche auf dem 881 als Pointer übergeben. D.h. du definierst dir ein Variable des entsprechenden Parametertyps und übergibst diese dem Masterbaustein mit der ADR()-Funktion. Das gilt sowohl in Lese- als auch Schreibrichtung. 
- Das Handbuch zum 653 sagt: Datenübertragung und Handshake parametrierbar mit WAGO-I/O-CHECK 2. Alternativ könnte das Teil des Masterbausteins sein.

Das erst mal zum Einstieg. 

Auf eine muntere Konversation...

Snert


----------



## SPS_A (13 Juni 2013)

Hallo Snert,

vielen Dank für deine ausführliche Antwort. Zu deinen beschriebenen Punkten:

- Die Strecke habe ich so aufgebaut. Beim 2-Leiter-Anschluss werden dann die Anschlusspunkte 1-2 und 5-6 überbrückt, und das Datenkabel geht dann von 1 und 5 an den Sensor. Die Strecke ist derzeit zum Testen nur ~0,5m lang.
- Im vorliegenden Fall habe ich ja erst einmal einen Teilnehmer. Daher habe ich dem Slave einfach mal die Adresse "0" zugewiesen in der CoDeSys. Oder muss da an dem Slave ansich noch etwas eingestellt werden?
- Ok, ich dachte das einerseits der Master konfiguriert wird und dann der Slave dementsprechend, damit baudrate etc. übereinstimmen. Es reicht also, in der CoDeSys den Master-Baustein zu konfigurieren?
- So hatte ich mir das Protokoll auch ungefähr vorgestellt. Ich will ja im ersten Schritt, dass der Master eine Abfrage (zB aktuelle Temperatur) absendet, und die angeschlossenen Slaves antworten.
- Über die Config Values wird also der Slave eingestellt? Also konkrekt bei dem Sensor die Farbübergänge zum Beispiel? Mein Einsatz war, über den Live Value mit entsprechender Funktion (4 - Read Input Register) überhaupt erst einmal einen Wert zu bekommen.
- Das mit den Eingangs-/Ausgangsdaten bzw. dem Merker ist mir noch nicht ganz klar. Das es verschiedene Funktionscodes gibt, kann ich so nachvollziehen. Über die "Modbus Extended Query Abfrage" aus dem baustein habe ich als FunctionCode die 4 für Register lesen eingetragen
- Den Punkt müste ich mir noch mal anschauen, da sind bei mir zugegebenermaßen noch viele Fragezeichen. 
- Genau, die FlowControl etc. kann sowohl im Masterbaustein als auch im I/O-Check eingetragen werden. Da habe ich für die 2-Leiter-Übertragung half-duplex (bekommt den Wert 4) eingetragen.

Das waren auf jeden Fall sehr interessante Infos. So ganz habe ich aber noch nicht die zündende Idee, wie ich praktisch voran kommen könnte. Ich glaub der Knackpunkt ist derzeit der Teil mit dem Eintragen der Register. Ich versteh denke ich nicht wirklich den Zusammenhang bzw. die Einstellung zwischen der Klemme und dem Slave, bzw. welche Einstellungen dort noch wo zu treffen sind. Kannst du mir da vielleicht einen Gedankenanstoß geben?

Vielen Dank nochmals, viele Grüße


----------



## Snert (13 Juni 2013)

Der Slave (Sensor) muss eine eigene UnitID eingestellt haben, entweder hardwaremäßig über DIP-Schalter oder Drehschalter oder softwaremäßig (vermutlich weniger der Fall). Diese UnitID ist dem Masterbaustein als Ziel-ID anzugeben, damit der Master die Telegramme auch richtig addressieren kann. Einfach so mal die "0" annehmen ist nicht zielführend! 

Konfiguriert wird nur der Master in der CoDeSys. Der Slave liegt ja "konfiguriert" als Hardware vor. Sichergestellt sein muss nur, dass beide die gleichen Parameter verwenden. Um es einfach zu halten, würde ich die Defaulteinstellungen des Sensors (UnitID=1, Baudrate=4800bps, Parity=keine, Stoppbits=2) verwenden. Wenn die Werte im Masterbaustein einstellbar sind, brauchst du womöglich kein i/O-Check verwenden. 

Die LIVE-Werte kannst du dann mit Funktionscode 4 einlesen. Der Datentyp ist "Float inverse", d.h. deine Variable ist grundsätzlich vom Typ REAL. Es kann sein, dass du die beiden Worte, aus denen der REAL-Wert besteht, umdrehen musst. Zu erkennen daran, dass die gelesen Werte keinen Sinn ergeben ;-)

Du verwendest CoDeSys 2.3 (wegen des 881). Welche Bibliothek verwendest du, die deinen Masterbaustein enthält?


----------



## SPS_A (13 Juni 2013)

Hallo Snert,

vielen Dank wieder für deine schnelle und ausführliche Antwort.

Genau, verwendet wird CoDeSys 2.3, die Version 3 läuft ja soweit ich weiss nicht mit dem Controller. Für diese Zwecke ist die 2.3 aber hoff ich ganz gut und ausreichend. Verwendet wird die Bibliothek "Modb_l05" mit dem Masterbaustein "MODBUS_EXTENDED_MASTER", der ja laut Anleitung empfohlen wird. Die 0 anzunehmen war natürlich nicht so clever, da hab ich nicht richtig nachgedacht. Defaultmässig muss es ja die 1 sein (laut dem Datenblatt), genauso lassen sich dann ja die Bbaudrate (19200bps), keine Parität und 2 Stop Bit einstellen, wie auch von dir geschildert. Die erste Klemme müsste ja laut Anleitung bei "bCOM_PORT" eine 2 bekommen, ich hab auch nur eine Klemme in der Konfiguration. Aufgrund der 2-Leiter-Übertragung dann für Halfduplex die 4. Ich habe mal einen Screenshot von den Eingaben gemacht, die ich bisher reingepackt hab. 
	

		
			
		

		
	




Wie genau würde ich denn dann nun den Wert auslesen? Ich definiere eine Variable, zB. "Temperatur" des Typen REAL. Aber was genau hat es dann mit dem "Float Inverse" auf sich? Und wie stelle ich die Verbindung von FunctionCode zu dem was der Sensor liefert her? In dem Programm wird der functionCode abgefragt, und soll dann über RESP.functionCode ausgegeben werden. Aber da ist mir noch nicht ganz klar wie da die Verknüpfung ist, diese Abfrage hatte ich in einem beispiel gesehen.

Vielleicht nochmal zum Verständnis was ich bisher dank deiner Hilfe hab: Der Master (Die SPS bzw. die Klemme) ist so konfiguriert, dass die Parameter mit den Default-Werten des Slave (Tempsensor) übereinstimmen. Prinzipiell kann nun also eine Abfrage zB. des Temperaturwerts gestartet werden. Dazu muss ja die Abfrage in irgendeiner Form definiert sein, dass der Slave damit was anfangen kann. Der knackpunkt in meinem Verständnis scheint somit grade zu sein, woher der Slave die Abfrage bekommt und wo das Register definiert wird, damit zwischen den unterschiedlichen Messwerten (CO2, Feuchte, Temp.) "unterschieden" werden kann. 

Ich kann mir gut vorstellen, dass diese Probleme für jemanden mit Erfahrungen in dem Bereich unverständlich sind. Für mich als absolutem Neuling auf dem gesamten Gebiet (MODBUS, serielle Schnittstellen allgemein, SPS) ist dies aber nicht immer so einfach zu durchdringen. Deswegen würd ich mich natürlich freuen wenn du mir noch weitere entscheidende Hinweise geben kannst. 

Vielen Dank aber nochmals für die bisherigen Antworten, Grüße


----------



## Snert (13 Juni 2013)

Ok, CoDeSys 2.3 mit MODBUS_EXTENDED_MASTER aus aktueller Modb_I05.lib - bin im Bilde.

Dein Screenshot ist schon fast korrekt:

- Statt 1920 als Baudrate ist als Defaultwert laut Datenblatt 480 zu nehmen.
- Die cbsCOM_BYTESIZE mit 8 und cfCOM_FLOW_CONTROL mit 4 können richtig sein. Wenn es nicht funktioniert, muss man hier vielleicht noch einmal etwas anderes probieren.
- Beim Timeout würde dem Baustein (gerade im Anfang) eine Chance geben und die Zeit auf den Defaultwert von min. t#500ms setzen.
- Damit der Baustein anläuft solltest du "Start" mit TRUE vorbelegen in der Definition, also: Start:BOOL:=TRUE;
- Ganz entscheidend für die Funktionalität des Ganzen ist die richtige Abarbeitungsreihenfolge der Anweisungen. Du solltest die Anweisungen untereinander setzen, beginnend mit der Extended-Query, dann den Master-Baustein und schließlich die Response. Danach rechter Mausklick -> Ausführungsreihenfolge -> nach Datenfluss wählen.

Nach dem Herunterladen auf die Steuerung und dem Starten (F5) sollte in der Variable "Wert" (Elemente 0 und 1 des Arrays) etwas zu lesen sein. Wenn ein Fehler aufgetreten ist, dann steht in "Fehler", "ERROR" und/oder "ERROR2" etwas Interessantes.

Zum Wiederholen der Modbus-Abfrage kannst du die Variable "Start" auf TRUE forcen (Doppelklick + F7) und das Forcen sofort wieder aufheben (SHIFT-F7).

Den Temperaturwert erhälst du, wenn du die beiden Worte "Wert[0]" und "Wert[1]" zu einem REAL zusammenfügst. Dazu später mehr, wenn alles andere läuft ;-)

Zum Verständnis:

Mit den Werten am Eingang des Masterbausteins wird die Strecke "RS485" beschrieben. Die "ExtQuery" beschreibt mit SlaveAddress, FunctionCode, StartAddress und Quantity das Anfragetelegramm. Das Antworttelegramm wird entgegengenommen und als Einzelbestandteile in der "Response" abgelegt. Die abgefragten Werte stehen in "Response.Data". Physikalisch sorgt die 750-653 dafür, dass die Infos auf den beiden Drähten zwischen Sensor und Steuerung hin- und herwandern. Die anderen abfragbaren Werte bekommt man, wenn man die Read_StartAddress variiert zwischen 0 (CO2), 2 (Temperatur) und 4 (Feuchtigkeit).


----------



## SPS_A (13 Juni 2013)

Hallo Snert,

vielen Dank erstmal wieder für deine schnelle und ausführliche Antwort.

- Muss man sich bei der baudrate nicht auf den Slave beziehen? Die Klemme kann ja sozusagen "alles". laut der Modbus Map ist Default doch 4, was bei dem Device 19200 entspricht, oder?
- Alles klar, könnte man dann ja, je nach Ergebnis anpassen.
- Alles klar, hab den Wert angepasst.
- ist vorbelegt 
- Alles klar, auch das ist erledigt.
- das herunterladen und Starten hat geklappt. Fehler und ERROR 2 geben eine 0 aus. ERROR gibt MB_NO_ERROR aus, also sollten die Sachen soweit fehlerfrei sein. RESP.Data mit dem ausgang Wert ist grau hinterlegt, und oben in den lokalen Variablen ist das gesamte Array mit "0"en ausgefüllt.
Das wäre ja super, wenn die Lösung zum tatsächlichen Ausgeben eines Wertes nicht mehr so fern wäre. 

Vielen Dank auch für die Verständniserweiterung. So in etwa habe ich das auch gedacht, ohne das mir die Abfragen "ExtQuery" und "Response" so ganz klar waren. Was ich nicht wusste ist, dass die 0/2/4 dann sozusagen die Register darstellen.

Was mir noch einfällt: Die beiden Datenleitungseingänge von dem Sensor sind einfach mit "A" und "B" benannt. ich konnte noch nicht wirklich rausfinden, in welcher Reihenfolge die beiden an die Klemme müssen. Da fällt mir eigentlich nur das Ausprobieren ein, was ja nur eben das Kabel umklemmen ist. Der Hersteller hielt anscheinend nichts davon das konform zu der Klemme mit TxD zu beschriften. Oder gibt es da eine Konvention?

Vielen Dank nochmals, ich freue mich sehr das du mir da so geholfen hast und hoffentlich noch bei den nächsten Schritten unterstützt. 

Viele Grüße


----------



## Snert (13 Juni 2013)

Die Baudrate ist natürlich 1920. Sorry, wer lesen kann, ist klar im Vorteil...

Die Leitungen zu tauschen kann man ja mal probieren. Wenn es dann geht, ist ok.

Dass ohne Fehler nur Nullen erscheinen könnte bedeuten, dass das Telegramm nicht gesendet wurde oder die Werte tatsächlich Null sind. Arbeitet der Sensor denn? Einfach mal mehrfach die Startfunktion forcen und schauen was passiert.

Bei Neuigkeiten helfe ich gern weiter.

Gruß,
Snert


----------



## SPS_A (13 Juni 2013)

Hallo Snert,

also, arbeiten tut der Sensor würd ich sagen einwandfrei. Das Display zeigt Werte für Feuchte/Temperatur/CO2 an, die auf jeden Fall realistisch sind. Auch das verfärben des Displays bei Überschreiten funktioniert. ich habe die kabel gerade eben getauscht und ein paar Durchläufe "geforct", und jetzt hat er tatsächlich einen Wert im ersten Feld des Array "Ausgespuckt". Für Wert [0] gibt er 17584 aus, bzw. beim neuen Versuch dann 17466 (StartAdress ist 0, also Anzeige von Co2, das muss dann noch umgerechnet werden, oder?). wie lange darf man diesen "Force" denn laufen lassen, oder ist das nicht "schlimmes"? Aber ich freu mich gerade schon, dass wenigstens ein Wert übertragen wird. Auch für die anderen register gibt er Werte um 17000 aus.

Ich freu mich schon auf die nächsten Schritte, vielen lieben Dank bis hierhin


----------



## Snert (13 Juni 2013)

Erste Erfolge! Sehr schön.

Die Read_Quantity im Request muss noch auf 2 gesetzt werden, da der REAL-Wert aus zwei Worten besteht. Dann sollten Wert[0] und Wert[1] ungleich Null sein.

Das Forcen ist nur eine Krücke. Eigentlich braucht man eine Mimik, die Start wieder auf TRUE setzt, wenn der Masterbaustein Start auf FALSE setzt. Bei serieller Kommunikation am besten mit einer kleinen Verzögerung. Dann läuft die Kommunikation kontinuierlich.

Du könntest mal versuchen, eine Variable pTemperatur vom Typ POINTER_TO_REAL anzulegen. Dieser Variable weist du dann den Wert "ADR(Wert)". Im oberen Teil der Variablen sollte dann zur Laufzeit ein REAL-Wert sichtbar sein. Bin mir allerdings im Augenblick nicht ganz sicher, ob das so geht. Muss ich noch mal drüber nachdenken.


----------



## SPS_A (13 Juni 2013)

Ok, vorhin hatte ich das testweise auch einfach mal mit der 2 im Request eingegeben. Da kamen dann auch 2 Werte in den Anfangsbereich des Array. Ich probier das morgen früh dann direkt weiter. Weiterhin will ich dann probieren die Systematik einzubauen, dass der Zyklus wiederholt wird. 

Könntest du mir das mit dem POINTER_TO_REAL noch genauer beschreiben? Ich hab grade keine CoDeSys Software vor mir, aber wird die Variable genauso über F2 definiert und ist dann dort irgendwo hinterlegt? Oder reicht es einfach zu schreiben "pTemperatur: POINTER TO REAL;" in den lokalen Variablen? Und wie würde ich dann den Wert "ADR(Wert)" zuweisen?

Vielen Dank nochmals für deine Hilfe, viele Grüße


----------



## Snert (14 Juni 2013)

Hintergrundinfo:

Alle Datentypen sind im tiefsten Kern nichts anderes als eine Aneinandereihung von Bytes. Bei INT sind es 2, bei REAL sind es 4 etc. Der Datentyp gibt lediglich an, wie die Ansammlung von Bytes zu interpretieren ist. Die in CoDeSys zur Verfügung stehenden Funktionen erlauben meist nur eine Konvertierung auf der "bereits interpretierten" Ebene. So kann man aus einem REAL mit Wert 21.4 einen INT mit Wert 21 machen. Was nicht so ohne weiteres geht, ist die Neuinterpretation der Bytes, d.h. man kann nicht aus 2 Worten (4 Bytes) einen REAL (ebenfalls 4 Bytes) erschaffen. Schön wäre ja eine Funktion, die ich mit 2 Worten füttere und hinten kommt ein REAL heraus. So etwas muss man sich selbst schreiben, nachdem man verstanden hat, wie das geht.

Auf die schnelle gibt es aber folgende Lösung:

Du nagelst dein Wert-Array an eine feste Adresse im Speicher, z.B.:


```
Wert AT %MD0: ARRAY[0..124] OF WORD;
```

Dann nagelst du auch deine Temperatur-Variable an die gleiche Adresse im Speicher, z.B.:


```
Temperatur AT %MD0: REAL;
```

Damit stehen die Daten aus der RESPONSE einerseits als WORD-Array zur Verfügung. Andererseits werden die ersten beiden Worte dieses Array aber auch über die Variable Temperatur als REAL interpretiert. Das wäre eine vorläufige, halbwegs elegante Typkonvertierung.


Eine zweite Lösung:

Du definierst dir im lokalen Variablenteil (oberes Fenster) eine Variable vom Typ "Zeiger auf REAL" und eine REAL-Variable Temperatur:


```
pTemperatur: POINTER TO REAL;
Temperatur: REAL;
```

Im Programmteil schreibst du zwei Zuweisungen:
1. Adresse von Wert[0] (Baustein "Eingang") auf den Zeiger pTemperatur (Baustein "Ausgang")
2. Inhalt von pTemperatur (Baustein "Eingang") auf Temperatur (Baustein "Ausgang")

[ ADR(Wert[0]) ]-----[ pTemperatur ]
[ pTemperatur^ ]-----[ Temperatur ]

Wichtig ist der Inhaltsoperator "^" hinter pTemperatur. Er gibt an, auf welchen Wert der Zeiger pTemperatur zeigt. Er ist das Gegenstück zu "ADR()".

Beide Varianten funktionieren nur dann richtig, wenn die Reihenfolge der beiden Worte, die den REAL-Wert beschreiben, korrekt ist. Ist das nicht der Fall, muss die oben bereits erwähnte eigene Funkton her. Dazu mehr, wenn es nötig wird.


----------



## SPS_A (16 Juni 2013)

Hallo Snert,

vielen Dank wieder für deine schnelle Antwort und auch den "Exkurs" zu den Datentypen. Ich hatte die letzten 3 Tage leider kaum Zeit um mich mit der Materie zu beschäftigen, aber ab morgen kanns hoffentlich wieder weitergehen. Ich müsste mir dann bei Gelegenheit sicherlich mal die einzelnen Datentypen anschauen, aber ich denke das ist im Moment noch nicht ganz so dringend.

Ich werde dann zuerst deine beiden beschriebenen Möglichkeiten testen, an den Wert bzw. die Werte zu kommen. Dazu habe ich überlegt, eine Abfrage einzubauen, die zB. alle 5 Sekunden einen Wert abfragt. Also nach 5 Sekunden CO2, dann wieder nach 5 Sekunden die Temperatur und dann nach weiteren 5 Sekunden die rel. Feuchte. Dazu würde ich analog dem Vorgehen bisher 2 weitere Abfrageblöcke reinsetzen mit dem zugehörigen Registerwert. Dies würde ich probieren über den Timer zu realiseren. Dein beschriebenes Vorgehen würde ich dann analog für die 3 Werte durchführen, dazu müsste ich mir dann wahrscheinlich die Speicheradressierung anschauen, oder? Also welche Speicherblöcke es noch außer "%MD0" gibt. Die Deklaration des Array und der Wert-Variablen würde dann auch oben in dem Bereich für die lokalen Varibalen erfolgen, analog zu der zweiten Methode?
Wenn beide Methoden nicht klappen sollten (Also weiter keine "richtigen" Messwerte rauskommen sollten), stände ich sicherlich ziemlich auf dem Schlauch wie man das zustande bringt. Da würd ich mich natürlich über ein wenig Hilfe sehr freuen. Aber vielen Dank natürlich nochmals für deine bisherige Hilfe und Lösungsvorschläge, ich mach mich morgen früh direkt dran. Ich würde parallel dazu auch gerne probieren, einen zweiten Sensor vom gleichen Typ ins System zu bringen. Da hab ich überlegt, dass man diesen ja erstmal adressieren müsste, da die Default-ID "1" ja schon von dem ersten Slave belegt ist. Mein geplantes Vorgehen wäre dann wie folgt:
1) Der Sensor, der ID Nummer 2 bekommen soll wird an 24V/0V/RS485-Klemme angeschlossen
2) Die Funktion, die eine ID zuweist, wird irgendwie definiert. ("6 - Write Single Register")
3) Der Sensor bekommt dann die ID 2
4) Der Sensor mit der ID 1 wird wieder wie folgt angeschlossen: Klemme1/Klemme5<->A/B Sensor 1<->A/B Sensor 2<->(Abschlusswiderstand?), beide jeweils mit Spannungsversorgung

Ich melde mich sicherlich morgen mittag direkt wieder, entweder mit Verzweiflung oder dem nächsten kleinen Fortschritt! 

Viele Grüße


----------



## Snert (17 Juni 2013)

Bevor du dir die Mühe machst, alle Bausteine zu vervielfachen, um alle Werte aus allen Slaves zu bekommen, solltest du mal ausprobieren, ob durch Erhöhen der READ_QUANTITY von 2 auf 6 (READ_ADDRESS=0) alle drei Werte des Sensors fehlerfrei gelesen werden (Wert[0] - Wert[5]). Dann würde ich Variante 2 zum Umwandeln in REAL-Werte favorisieren.

Für den zweiten Sensor brauchst du dann nur die Slave-Address zyklisch wechseln. Da du nur eine serielle Klemme hast, brauchst du keinen weiteren Masterbaustein. Die würden sich nur gegenseitig behindern...

Das Vorgehen für den zweiten Sensor ist ok. 

Tip:
Vielleicht wäre ein Umstellen beider Slave-Adressen weg von der standardmäßigen 1 zu überlegen. Denn dann kannst du weitere Slaves einbinden, ohne einen der bestehenden abzuklemmen.


----------



## SPS_A (17 Juni 2013)

Hallo Snert,

vielen Dank für die Antwort. Ich habe gerade ein wenig nach deinem Ratschlag rumgebastelt. Die Anzahl der gelesenen Werte auf 6 erhöhen klappt, bei 7 oder noch mehr würde dann eine Fehlermeldung kommen. Ich habe einmal einen Screenshort von dem "laufenden" Modell erstellt, eingeloggt und gestartet (noch mit der force-Funktion, das mit dem Timer scheint noch nicht ganz zu klappen).




Der angezeigte CO2-Wert (1290) entspricht auch genau dem was auf dem Display angezeigt wird, bei Temperatur und rel. Feuchte scheint es noch nicht zu klappen. Aber morgen gehts dann erst einmal ans testen eines zweiten Slaves. Dass mit dem Master hatte ich auch so aufgefasst, also ein Master für (in meinem Fall werdens max. 20 Sensoren) "beliebig" viele Sensoren. Als Abschlusswiderstand würde ich einfach die beiden kabelenden (A und B) an einen Widerstand mit 220 Ohm klemmen, da ich gerade nichts anderes zur Verfügung habe. Oder ist das bei 2 Slaves mit nur kurzer Kabellänge so nicht in Ordnung?

Viele Grüße und danke nochmals


----------



## Snert (17 Juni 2013)

Für die Umwandlung der Worte in REAL sind natürlich die Adressen von Wert[0] (CO2), Wert[2] (Temp.) und Wert[4] (Hum.) zu nehmen, da REAL jweils 2 Worte benötigt. In Summe also 6 Worte, die du ja auch eingestellt hast als READ_QUANTITY.

Sind die Variablen Temperatur, CO2 und RH vom Typ REAL? Poste doch bitte mal die 6 Werte von Wert[0] - Wert[5]. Dann kann man erkennen, ob die Worte paarweise zu drehen sind, damit ein ordentlicher REAL herauskommt.

Ob und welchen Abschlusswiderstand man nehmen sollte, hängt von vielen Dingen ab. Hier im Forum gibt es entsprechend Beiträge dazu, z.B. http://www.sps-forum.de/feldbusse/44809-abschlusswiderstaende-fuer-modbus-rs485.html.

Wenn man einen Abschlusswiderstand verwendet, so sind in der Tat Leitung A und B über diesen Widerstand zu verbinden, und zwar hinter dem letzten Slave.


----------



## SPS_A (18 Juni 2013)

Hallo,

vielen Dank für den Hinweis. Klar, das mit den Adressen ist ja logisch. Hier sind die 6 Werte der 3 Messdaten im laufenden Betrieb:





Wie kann man denn aus den 6 Array-Werten erkennen, wie daraus 3 REAL-Werte "entstehen"? Also z.B. "17463" und "49152" zu dem Wert Tempertur werden? (Hier merk ich gerade, dass ich die ersten beiden Variablen verdreht hab, aber das dürfte für die Rechnung ja erstmal egal ein)
Die Variablen sind vom Typ REAL, hier noch mal ein Screenshot im ausgeloggten Zustand:




Danke auch für den Hinweis zum Abschlusswiderstand. Dort scheint es ja verschiedene Ansichten zu geben. Wenn ich soweit komme, mus ich mich da wohl noch ein wenig detaillierter informieren.

Viele Grüße

edit:

Ein kleiner Nachtrag: ich habe gerade einmal für die Werte der Adresse 1,3 und 5 eingegeben. Jetzt kommen auf einmal "richtige" Werte raus, also so wie sie auch auf dem Display angezeigt werden:




Interpretiert die CoDeSys dann sozusagen selbstständig den eingegebenen Wert und den vorherigen als Messwert und wanelt diesen in REAL um?


----------



## Snert (18 Juni 2013)

Dass das Ergebnis bei Verwendung der Adressen 1, 3 und 5 korrekt zu sein scheint, hängt mit der Gleichartigkeit der Real-Werte zusammen. Zu sehen daran, dass die Werte 0, 2 und 4 ungefähr gleich sind. Wenn man also das Haupt-WORD statt mit dem Nach-WORD mit dem Vor-WORD kombiniert, ist das zunächst nicht auffällig, aber dennoch falsch. Auffällig wird es, wenn die Zehnerpotenzen der Zahlen stark variieren, z.B. 0.1 / 2.4e-12 / 4.5e14.

Somit ist auch deutlich, dass die WORDs zu drehen sind, damit ein ordentlicher REAL herauskommt. Was du benötigst, ist folgende Funktion "DW_TO_R" (geschrieben in ST), die zwei WORD in einen REAL verwandelt. Dabei kann man die WORDs als Eingangsparameter beliebig setzen. In deinem Fall ist die Reihenfolge der Werte jeweils 1-0, 3-2 und 5-4. Das Ergebnis der Funktion ist schon der REAL-Wert und kann direkt den Variablen CO2, Temperatur und Feuchtigkeit zugewiesen werden.


```
FUNCTION DW_TO_R : REAL
VAR_INPUT
	w1: WORD; (* erstes  Wort, z.B. Wert[1] von aussen angelegt *)
	w2: WORD; (* zweites Wort, z.B. Wert[0] von aussen angelegt *)
END_VAR
VAR
	w: ARRAY[0..1] OF WORD;
	pR: POINTER TO REAL;
END_VAR


(* Worte in das 2er-Array schreiben *)
w[0] := w1;
w[1] := w2;

(* POINTER TO REAL auf die Adresse des 2er-Array setzen *)
pR := ADR(w);

(* Inhalt des Pointers übergeben an Funktion *)
DW_TO_R := pR^;
```

Die Funktion nutzt die Tatsache, dass ein POINTER TO REAL die beiden Worte, auf die er zeigt, als REAL deutet. Dass sie ursprunglich von einem 2-Wort-Array stammen, ist der Kunstgriff.


----------



## SPS_A (18 Juni 2013)

Hallo, vielen Dank wieder für deine Antwort. Jetzt bin ich leider ein wenig verwirrt, da ich doch zumindest nach meiner Einschätzung richtige Messwerte hatte, die auch synchron mit der Anzeige auf dem Display des Sensors sind. Ich hatte mir das so erklärt, dass durch die "force" Abfrage alle paar ms ein Wert abgefragt und geschrieben wird, also parallel zu der Anzeige im Display.

Jetzt habe ich deine beschriebene Abfrage einmal versucht zu integrieren. Dazu habe ich einen neuen Baustein (Typ Funktion, ST, REAL) eingefügt. Bei diesem habe ich oben dann die loklen Variablen definiert und unten den Code reinkopiert. Einloggen klappt auch ohne Probleme und in dem Hauptbaustein PLC_RPG die Werte wie vorher angezeigt. In dem Baustein DW_TO_R steht überall ein "?":




Dies wird dann ja daran liegen, dass die Variablen lokal sind und nicht in den Bblock übertragen werden. war es überhaupt richtig dafür eine neue Funktion anzulegen? Ich steht aber gerade zugegebenermaßen noch ein wenig auf dem Schlauch, warum und vor allem was genau in dem Programm gemacht wird. Also die beiden Werte "w1" und "w2" werden in das 2er-Array "w"geschrieben. Auf dieses Arry wird wieder der Pointer gesetzt und an die Funktion übergeben. Das kann ich soweit nachvollziehen. Aber es hapert noch wenig an dem Verständnis des "Zwecks" des ganzen, wahrscheinlich bin ich gerade nur froh endlich Messwerte zu sehen 

Hoffe du kannst meinen Horizont da ein wenig erweitern, viele Grüße bei dem sonnigen Wetter


----------



## Snert (18 Juni 2013)

Im Baustein muss alles "?" sein, weil es eine Funktion ist. Der Geltungsbereich einer Funktion reicht nicht, um nach Zyklusende noch Werte anzeigen zu können. Welche denn auch? Der Baustein ist im laufenden Programm nur 1x definiert, wird aber 3x aufgerufen! Das geht also nicht. Bei Funktionsblöcken sieht das anders aus. Die haben einen reservierten, eigenen Speicherbereich, so dass man zwischendurch auch mal reinschauen kann.

Zur Erklärung der Datenkonvertierung:

Der Modbus-Masterbaustein füllt dein RESP.Data und damit letzendlich dein Array "Wert". Der Speicher der WAGO ist grundsätzlich wortweise arrangiert, wobei jedes Wort aus dem High- und dem Low-Byte besteht. Die Reihenfolge, in der die Worte aus dem Sensor sortiert sind, entspricht dem Datentyp "FLOAT INVERS" (laut Datenblatt). Diesen gibt es auf der WAGO nicht. Dreht man allerdings die Worte paarweise um, so entsteht der Datentyp REAL. Den kennt WAGO sehr gut. Man könnte auch sagen, dass man die beiden Worte des jeweiligen Wertes "rückwärts" lesen muss, um sie korrekt zu deuten. "Rückwärts" geht aber nicht, daher müssen die Worte umgedreht werden in ihrer Reihenfolge. Aus Wert[0]/Wert[1] wird Wert[1]/Wert[0] usw. Wenn man dann einen REAL-Pointer auf den Beginn eines "gedrehten" WORD-Pärchens zeigen läßt, dann ist der Inhalt des Pointers ein REAL-Wert. Und nichts anderes macht die Funktion. Das folgende Bild versucht das etwas zu verdeutlichen.


----------



## SPS_A (19 Juni 2013)

Hallo, vielen Dank für die Erläuterung. 

Das mit dem Drehen der Werte kann ich mir dann halbwegs vorstellen.Ich fass den Datentyp "FLOAT INVERS" dann so auf, dass er sich aus dem High- und Low Byte zusammensetzt, und zwar in genau "entgegengesetzter" Richtung zu der Schreibweise in dem Array. 
Jetzt weiss ich aber immer noch nicht ganz genau was ich in dem Hauptprogramm anpassen muss. Dot wird ja das Array "WERT" beschrieben, aus dem die Messwerte dann herausbekommen werden. In der Funktion DW_TO_R wird ja ein neues Array "w" mit den beiden Werten "w1" und "w2" aufgespannt und über den Pointer wieder auf w gezeigt. Dieses hat aber so in meinen Augen noch keine Verbindung zu dem Hauptbaustein mit der Modbus-Kommunikation etc., die Schrift der Funktion ist in der Bausteinübersicht auch grau. Müsste ich jetzt nicht erst zusehen die einzelnen Werte aus dem Array "WERT" irgendwie in die Funktion rüber zu bekommen (z.B. als globale Variablen definieren!?), damit diese dann in der Funktion  DW_TO_R als w[1], w[2] etc. geladen werden können? Sonst macht die Funktion ja noch nichts mit den gelsenen Werten.


----------



## Snert (19 Juni 2013)

Ich hatte gedacht, mein Hinweis auf die Anwendung der Funktion wäre im früheren Post ausreichend gewesen:



> Was du benötigst, ist folgende Funktion "DW_TO_R" (geschrieben in ST), die zwei WORD in einen REAL verwandelt. Dabei kann man die WORDs als Eingangsparameter beliebig setzen. In deinem Fall ist die Reihenfolge der Werte jeweils 1-0, 3-2 und 5-4. Das Ergebnis der Funktion ist schon der REAL-Wert und kann direkt den Variablen CO2, Temperatur und Feuchtigkeit zugewiesen werden.



Was dir noch fehlt, ist der 3fache Aufruf der Funktion gemäß nachfolgendem Bild:


----------



## SPS_A (19 Juni 2013)

Alles klar, jetzt hats "klick" gemacht auch bei mir im Gehirn!  Vielen Dank für den letzten Hinweis. Das Senden von Anforderungen und Empfangen scheint ja somit zu klappen. Ich hab hier noch einen anderen CO2-Sensor der auch das MODBUS Protokoll unterstützt, jedoch anders adressiert wird, andere Baudrate etc. hat. Ich werd morgen probieren diesen dann ans Laufen zu kriegen und dann probieren 2 Sensoren vom gleichen Typen in "Reihe" zu schalten. Ich poste die nächsten Schritte natürlich gerne hier weiter.


----------



## Snert (19 Juni 2013)

Mit deinem frisch erworbenen Wissen sollte das Prinzip zumindest jetzt klar sein. Wie immer steckt der Teufel im Detail... bin mal gespannt...


----------



## SPS_A (20 Juni 2013)

Das Prinzip ist halbwegs klar, vielen Dank nochmals für die Erläuterungen und die geduld, aber wie erwartet gibt es auch bei dem zweiten Sensor Schwierigkeiten auf den "richtigen" Wert zu kommen. Mit dem Teufel im Detail liegst du auf jeden Fall richtig 

Also, es handelt sich wieder um einen CO2-Sensor, hier mal der Link zum Datenblatt:

http://www.syxthsense.com/product/product_pdf/SN1.76-HDH-M.pdf

Das relevante steht ja auf Seite 4:

Baudrate 9600, 8 Datenbits, keine Parität und 1 Stop Bit, Slave Adresse wieder 1. Diese Sachen habe ich dann in der Konfiguration des Masterbausteins eingegeben. Als Funktioncode wieder 4, als "Read_Quantity" hätte ich gedacht 13, um prinzipiell alle Register des Codes zu lesen. Dann eingeloggt, was auch klappt. Im Array werden dann folgende Werte angezeigt:




Datentyp ist  für die Messewrte CO2, Temperatur und RH ja "Signed 16", also der Bereich von -32768 bis 32767. Jetzt würde ich versuchen beispielsweise den Wert aus dem Register 30010 (CO2) auszulesen. Dazu habe ich Eingangsblöcke erstellt, diese mit "INT_TO_REAL" verknüpft und auf die Variable geschickt:




Diese zeigen also nichts anderes an, als den zugehörigen Wert aus dem Array. Die Werte sehen zwar schön aus, aber so wirklich können das ja noch nicht die Daten sein. Jetzt komme ich nicht drauf, wie die Werte aus dem Array konvertiert werden müssen. ich hätte gedacht, dass es in diesem Fall einfacher ist, da der Datentyp nicht Float Inverse ist, sondern ein einfacher Integer. Aber da habe ich wohl zu simpel gedacht


----------



## Snert (20 Juni 2013)

Ist doch alles bestens! Die Daten, die du liest, machen richtig Sinn, wenn man das Handbuch des Sensors daneben hält.

Im Detail:

Die Adressen wie "30001" sind in (alter) AEG/Schneider-Notation angegeben. Zumindest kenne ich sie von dort. Dort spricht man von sog. 4er-Adressen (Merker) und 3er-Adressen (Analogeingang). In der WAGO-Welt kann man diese Adressen so nicht verwenden. Die 3er-Adressen beginnen bei 30001 (nicht bei 30000!!!). Diese Adresse entspricht in der WAGO-Welt der Adresse "0" (bei passendem Funktionscode). Du musst also dein Auslesen bei "0" beginnen und dann 13 Wort lesen. Für dein Wert-Array bedeutet das, dass sich alle bisher gelesenen Werte um ein Wort verschieben. Damit liegen sie dann an der richtigen Stelle. Um dann z.B. den CO2-Wert zu lesen, brauchst du Adresse 30010, was bei WAGO "9" ist. Also steht der Wert in Wert[9].  Und so, wie ich das im Bild erkenne, steht dort auch ein plausibler Wert drin ("568").

Um jedoch alle Werte korrekt zu deuten, musst du dir mal den Wertebereich anschauen. Für CO2 ist das einfach. Der Wertebereich 0..2000 entspricht exakt dem physikalischen Bereich 0..2000 ppm. Hier ist also keine Umwandlung erforderlich. Anders sieht es bei der Temperatur aus. Als Wertebereich ist 0..500 angegeben. Physikalisch ist das aber 0..50,0 °C. Daher ist der gelesene Wert (Wert[10]) durch 10 zu teilen. In deinem Bild steht der Wert 255, also hast du es mit 25,5°C nett warm. Wenn man sich die Wertebereiche und die physikalischen Wertebereiche anschaut, ist entweder nichts mit dem Wert zum machen oder allenfalls durch 10 zu teilen.


----------



## SPS_A (21 Juni 2013)

Hallo, stimmt, ich hatte mich glaub ich von dem letzten Wert verwirren lassen, der dem Feuchtewert gleich ist. So passt das ja auf jeden Fall dann. Am Montag gehts dann weiter mit dem Einbinden von 2 Sensoren, mal gespannt wie gut das dann klappen wird. In meiner Überlegeung muss es eigentlich nur zu schaffen sein, einem Sensor eine andere ID zu vergeben.  Ein schönes Wochenende erst einmal und vielen Dank für die Tipps, Grüße


----------



## SPS_A (3 Juli 2013)

Hallo zusammen,

Entschuldigung erstmal für die lange Dauer bis zur nächsten Antwort, ich bin leider erst heute wieder dazu gekommen mich mit dem Thema zu beschäftigen. ich wollte mich daran machen, dem Sensor eine andere ID zu geben. Dazu habe ich folgenden im Screenshot ersichtlichen Aufbau aufgesetzt:



Ich rufe dann also den Sensor mit der ID 1 auf, Function Code 6 für Write Single Register, Schreibe Adresse 2. Als Antwort soll dann die "neue" Slave Adresse rausspringen und der Function Code. Das klappt so leider nicht. Der Error "MB_NO_ERROR" wechselt nach ein paar Sekunden ganz kurz auf "EXTENDED_SLAVE_ERROR" und dann wieder zurück auf "MB_NO_ERROR". So ganz weiß ich gerade nicht, was man dort alternativ testen könnte um das Register Adresse zu schreiben. Hat vielleicht jemand (Snert  ) eine rettende Idee?

Vielen Dank schon einmal für das Interesse, viele Grüße


----------



## SPS_A (4 Juli 2013)

Hi Leute, ich müsste dringend das Schreiben der Slave ID hinbekommen. Daher wollte ich eben nachfragen, ob ich mein Problem und meinen bisherigen Ansatz verständlich rüberbringen konnte?

Viele Grüße

edit: Ein kleiner Nachtrag, ich habe es gerade mit folgendem Code versucht:




Der Fehlercode der kommt ist ja "15", also ein interner Fehler. Vielleicht hilft dies schon mal weiter!?

Viele Grüße


----------



## Snert (5 Juli 2013)

Hallo SPS_A,

wenn du die "Modbus Address" des Sensors ändern willst, musst du die gewünschte neue Adresse ins Register 0 des Sensors schreiben. 

"SlaveAddress" bleibt zunächst "1", 
"FunctionCode" ist "6", 
"Read_StartAddres" und "Read_Quantity" sind irrelevant (weil Schreiben gewünscht), 
"Write_StartAddress" muss gem. Beschreibung "0" sein (nicht "2"!!! In "2" schreibst du "Parity/Stop-Bit".), 
"Write_Quantity" ist "1". 

Die neue "Modbus Address" ist in "REQ.Write_Data[0]" einzutragen. Wenn die neue Adresse also z.B. 13 sein soll, ist die Zuordnung "REQ.Write_Data[0]:=13;" anzuwenden. Oder besser: Wd[0]:=13, weil du ja "Wd" definiert hast und dieses Array an REQ.Write_Data übergeben wird.

Wenn du dieses Telegramm absetzt, bin ich mir nicht sicher, ob noch eine korrekte Antwort kommt, da ja die Modbus-Adresse dann geändert ist. Vielleicht wird aber auch die neue Adresse nach einem Neustart des Sensors aktiv. Das bleibt zu prüfen. Wenn das Telegramm erfolgreich war, funktioniert das Lesen von Daten nun auf der neuen Modbus-Adresse und nicht mehr auf der alten.


----------



## SPS_A (6 Juli 2013)

Hallo,

vielen Dank für deine Antwort. Ich hatte es gestern parallel zu deiner Antwort noch mal selber probiert und es hat tatsächlich geklappt. Der Sensor war dann nach dem Ausloggen nur noch über die "neue" Adresse erreichbar. Ich habe dann in dem Programm zur Abfrage der Messwerte die neue Adresse eingetragen, womit dann auch Werte kamen. Wenn im laufenden Betrieb dann ein Sensor mit einer anderen Adresse angedockt wurde, sind die Messwerte "eingefroren", sobald dann der Sensor mit der richtigen ID angeschlossen wurde hat es dann wieder funktioniert.


----------

