Hallo,
ich habe daheim eine Fußbodenheizung von Uponor.
Die Regelung im Heizkreisverteiler kommuniziert, so wie es aktuell
aussieht, mit den Thermostaten in den Räumen über Modbus RTU.
Meine Idee war, dass ich mit meinem Rapsi(OpenHAB) rein passiv am RS-485
Bus hänge und die aktuellen IST-Werte mitlese und dann in meine
Anzeigewerte auf OpenHAB übernehme.
Es gibt ja prinzipiell auch Projekte die sich dem Thema Modbus schon
angenommen haben (z.B. libmodbus). So wie ich das sehe spielen die aber
immer Master oder Slave. Ich möchte ja keines von beidem sein.
Das Protokoll ist nicht gerade anspruchsvoll, das ist nicht das Problem.
Die Frage ist für mich nur, wie bekomme ich im Linux den Anfang des
Frames raus? Auf einem uC würde ich einfach die Peripherie nutzen und
mir die 3,5 Zeichen Pause raussuchen (lassen) um zu erkennen dass ein
Frame abgeschlossen ist. Aber ich befürchte das ist in Linux nicht ganz
so einfach.
Wenn ich von einer festen Framelänge ausgehen könnte, würde ich einfach
bei jedem Byte rechnen ob die CRC stimmt und so den Frameanfang
herausfinden. Da die Framelänge bei Modbus RTU ja aber variable sein
kann wäre das etwas aufwändig.
Deshalb die Frage:
Gibt es in Linux eine einfache Möglichkeit auf serial eine idle Line zu
detektieren? Oder einen Zeitstempel zu den Zeichen zu bekommen?
Wenn es da nichts gibt, gehe ich halt den einfachen Weg und verwende
einen Controller der die Vorverarbeitung übernimmt.
Danke für eure Ideen
Grüße
Leis
Andreas B. schrieb:> Schau Dir mal jpnevulator an. Evtl. geht es damit.
Danke für den Tipp.
Du meinst mit -e, --timing-delta=MICROSECONDS ?
Für mich sieht das so aus als ob da auch nur gepollt wird ob nicht
innerhalb von einer gewissen Zeit ein Zeichen reinkam.
https://github.com/snarlistic/jpnevulator/blob/master/jpnevulator.c
Bei 19200 Baud sind das grob 2ms Timeout. Das heißt man müsste
mindestens mit 1ms pollen.
Gibt es da nichts schöneres?
Hallo Leis,
aus deiner Fragen kann ich leider nicht heraus lesen, ob die ein
"fertiges" Programm suchst, welches auf Linux läuft und welches dir eine
Sniffer Funktionalität bietet oder ob du auch bereit wärst ein Programm
zu schreiben / modifizieren, um diese Funktionalität zu bekommen.
Ich habe hier mal ein Projekt gefunden für einen Sniffer:
https://github.com/webstack/mbtools
Es basiert auch auf der libmodbus, ist also lauffähig unter Linux.
VG Leo
Leo schrieb:> aus deiner Fragen kann ich leider nicht heraus lesen, ob die ein> "fertiges" Programm suchst, welches auf Linux läuft und welches dir eine> Sniffer Funktionalität bietet oder ob du auch bereit wärst ein Programm> zu schreiben / modifizieren, um diese Funktionalität zu bekommen.
Braucht nicht fertig sein. Wenn es nur ein schubs in die richtige
Richtung ist kann ich mir das glaub ich auch selber zusammenschreiben.
Leo schrieb:> Ich habe hier mal ein Projekt gefunden für einen Sniffer:> https://github.com/webstack/mbtools> Es basiert auch auf der libmodbus, ist also lauffähig unter Linux.
Auf der Seite schreiben sie dazu:
1
•in client/master mode, it reads addresses defined by the user at regular interval and output results to a local Unix socket
2
•in server/slave mode, it waits for requests from other devices and output register writes to a local Unix socket.
Master passt nicht. Ich möchte ja nicht als Master fungieren und aktiv
von einem Slave einen Request machen.
Slave passt aber auch nicht. Ich möchte ja nicht antworten auf die
Requests des Masters. Das soll ja der eigentliche Slave machen.
Aber anscheinend wird es irgendwie gehen wenn mbtools sowohl als Master
als auch als Slave das hinbekommt.
Ich wühle mich mal durch den Code.
Danke.
Du könntest Sigrok mit einem Logic-Analyzer nutzen um den Bus zu
sniffen. Sigrok hat schon direkt einen Modbus-Decoder mit dabei:
https://sigrok.org/wiki/Protocol_decoder:Modbus
Nicht vom Screenshot verwirren lassen, man kann alle Protocoll-Decoder
auch von der Konsole aus nutzen.
Der Hard- und Software Workflow wäre dann:
RS485-Transceiver -> LA -> Sigrok -> Uart-Decoder -> Modbus-Decoder
Den Output des Modbus-Decoders könntest du dann noch mit einem kleinen
Script aufbereiten und die Ergebnisse dann an Openhab senden.
Das ganze klingt jetzt zwar relativ kompliziert und ein bisschen nach
"von hinten durch die Brust ins Auge" aber ich denke, dass du damit
relativ schnell zum Ziel kommst. Die Ausgaben für die Hardware halten
sich auch sehr in Grenzen, d.h. für 10€ bekommst du den LA und für 3,50€
den RS485-Transceiver von eBay (mit Versand aus Deutschland). Beim Ali
gäbe es sogar beides für die Hälfte, dafür aber mit entsprechend langer
Wartezeit.
Leis schrieb:> Gibt es in Linux eine einfache Möglichkeit auf serial eine idle Line zu> detektieren? Oder einen Zeitstempel zu den Zeichen zu bekommen?
Zeitstempel gibt es irgendwo im Zusammenhang mit dem ntpd und PPS. Aber
es geht auch einfach, um nicht zu sagen primitiv. Mit read(2) einzelne
Zeichen lesen und mit select(2) einen Time-Out von 2ms einstellen. Da
der Scheduler mit 250Hz oder 1000Hz läuft, kommen dabei worst case 4ms
raus. Aber eigentlich sollte select() davon unabhängig sein. Versuch
macht kluch!
Christopher J. schrieb:> Du könntest Sigrok mit einem Logic-Analyzer nutzen um den Bus zu> sniffen...
Ja das hatte ich mir auch schon überlegt. Ich hatte nur gehofft mir die
externe Komponente sparen zu können. Also nur ein RS485 Transceiver.
Bauform B. schrieb:> Mit read(2) einzelne Zeichen lesen und mit select(2) einen Time-Out von> 2ms einstellen.
OK. Das mit select muss ich mir nochmal genauer anschauen.
Bauform B. schrieb:> Da der Scheduler mit 250Hz oder 1000Hz läuft, kommen dabei worst case> 4ms raus. Aber eigentlich sollte select() davon unabhängig sein.
Blöde Frage, kann man das irgendwie beeinflussen?
Oder was meinst du mit unabhängig?
N. M. schrieb:> Bauform B. schrieb:>> Da der Scheduler mit 250Hz oder 1000Hz läuft, kommen dabei worst case>> 4ms raus. Aber eigentlich sollte select() davon unabhängig sein.>> Blöde Frage, kann man das irgendwie beeinflussen?> Oder was meinst du mit unabhängig?
250 oder 1000Hz könnte man wählen¹, damit dauert eine Zeitscheibe dann
1ms oder 4ms. Das ist das Zeitraster indem zwischen gleichzeitig
laufenden Programmen umgeschaltet wird -- solange es kein äußeres
Ereignis gibt. Ein neues Zeichen auf der seriellen Schnittstelle ist so
ein Ereignis, woraufhin select() sofort zurückkehrt.
Wenn kein Zeichen kommt gibt es einen Time-Out. Ich bin jetzt nur
"ziemlich" sicher, dass select() auf den Time-Out hin genauso sofort
zurückkehrt. Falls nicht, würde es bis zu 1 oder 4ms länger dauern. Was
aber praktisch kaum stören dürfte, weil der Modbus-Master wahrscheinlich
nicht genau 3.5 Zeichen Pause macht.
[1]
https://www.systutorials.com/241636/how-to-change-config_hz-parameter-for-linux-kernel/
Ich habe jetzt mal mit dem Oszi 19.2KBaud rausgemessen.
So wie es aussieht 8N1 ohne Parity.
Das würde sich auch decken mit dem Dokument was ich mal gefunden hatte
(siehe PDF).
Ich habe 5 Thermostate, jedes mit seiner eigenen Klemme.
So wie es aussieht ist das nur Fake.
Wenn ich ein Thermostat abziehe sehe ich, dass die vom Master
wahrscheinlich Round Robin an alle Klemmen durchgegangen werden. Dann
fehlt nämlich jede 5. Antwort.
Zwischen jedem Request liegen ziemlich genau 200ms (siehe
1_Paketabstand).
Nur mit dem interpretieren der Daten bin ich noch nicht so richtig
glücklich:
Der Request startet mit 0x88 E0 82 1E. Der Response ebenso.
Ein Read Request könnte es also schon mal nicht sein. Die unterscheiden
sich.
Ein Write Request wäre zwar prinzipiell möglich, aber der Funktion-Code
0xE0 scheint nicht spezifiziert. Wenn ich MSB und LSB rumdrehe, dann ist
es zwar 0x11 03 14 87 was dann Funktion-Code "Lesen" wäre, aber dann
passt die Framelänge nicht. Ein Request müsste dann 8 Byte und ein
Response 9 oder im Fehlerfall 5 Byte lang sein. Bei mir sind es aber 7
Byte und 18 Byte Response.
Übersehe ich was?
Oder ist es ein anderes Protokoll?
Danke
Mani
Irgendwie sind die Bilder widersprüchlich. Wenn am gelben Kanal ein
10:1-Tastkopf hängt, kommen glaubwürdige RS232-Spannungen raus -- nur
dass der Ruhepegel normalerweise negativ ist, also in der großen Pause
in der Mitte. Die Dekodierung sieht in beiden Fällen natürlich ganz
unterschiedlich aus. Aus 0x88 wird nicht unbedingt 0x77; wahrscheinlich
kann sich sogar die Anzahl der erkannten Zeichen ändern.
Die Reihenfolge der Bits im Byte kann natürlich auch umgedreht sein,
aber das wäre kein RS232 mehr und schon garkein Modbus RTU. Ist es so
allerdings auch nicht, ich kenne keinen genormten Request mit 7 Byte.
Im oben verlinkten Dokument sprechen sie von RS232.
Allerdings auch von getrennten Rx und TX Pins am Stecker.
Ist die Doku von einem anderen Gerät, da ich zu meinem nichts gefunden
habe.
Aber die erfinden ja auch nicht immer alles neu :-)
Die Klemmen am Heizkreisverteiler sind mit +, -, A, B beschriftet.
Zwischen + und - Messe ich statisch 5V.
Deshalb ging ich seither von RS485 aus und habe in den obigen Messungen
Differentiell zwischen A und B gemessen.
Du hast aber Recht mit den Pegeln. Irgendwas stimmt da nicht.
Ich messe morgen Mal beide gegen Masse und sehe es mir absolut an von
den Pegeln.
N. M. schrieb:> Die Klemmen am Heizkreisverteiler sind mit +, -, A, B beschriftet.> Deshalb ging ich seither von RS485 aus und habe in den obigen> Messungen Differentiell zwischen A und B gemessen.
"A B" ist ja auch klassische RS485-Schreibweise
> Ich messe morgen Mal beide gegen Masse und sehe es mir absolut an von> den Pegeln.
gegen Masse nur zwecks der Neugierde; differentiell, aber A und B
vertauscht, dürfte viel besser aussehen
Hab Deine Nachricht erst gesehen als ich bereits gemessen hatte.
Ich hab mal wie gesagt A&B gegen Masse gemessen.
Wie erwartet ist es Differentiell (+-5V) (siehe 4_...png)
Wird also schon RS485 sein.
Man sieht auch was von der Heizungssteuerung und was von den
Thermostaten kommt.
Ich messe ja an der Heizungssteuerung.
Das erste und das dritte Paket scheint von der Differenz großer.
Hab es auch mal über Math voneinander abgezogen nur das Bild vergessen.
Das wird also von der Steuerung kommen.
Ich hab mal nur A durch den Decoder gejagt. LSB first.
Math(A-B) gibt das gleiche nur mit Offset und potentiellen
Gleichtaktstörungen. Ist jetzt glaub jetzt erst mal nicht so wichtig die
zu unterdrücken.
Dann bekommt man 0x11 07 41 78 FF EB 23
Also wieder nur 7 Byte.
Wie dekodiert denn der Oszi eigentlich? Weiß der, dass Modbus RTU
normalerweise (gerade) Parity verwendet? Muss man die Baudrate von Hand
einstellen? Triggert der wirklich auf den Anfang des Startbits? Die
orangene Triggermarke scheint mir etwas nach hinten verschoben zu sein?
Entschuldigen Sie bitte die vielen Fragen ;)
Bauform B. schrieb:> Wie dekodiert denn der Oszi eigentlich? Weiß der, dass Modbus RTU> normalerweise (gerade) Parity verwendet?
Im obigen Dokument (wo sie allerdings RS232 verwenden) steht 8 Datenbit,
1 Stopbit, kein Parity. So habe ich das mal eingestellt da es mit Even
und Odd auch immer Zeichen gab die nicht gepasst haben. Außerdem hat mit
Parity auch nie das Stop-Bit gepasst. Deshalb verwende ich gerade mal
die Einstellungen wie im Dokument.
> Muss man die Baudrate von Hand einstellen?
Muss man nicht, kann man auch ausmessen lassen. Ich hatte die Bitzeit
aber händisch ausgemessen und auch auf 19.2kBit eingestellt.
> Triggert der wirklich auf den Anfang des Startbits?> Die orangene Triggermarke scheint mir etwas nach hinten verschoben zu sein?
Ich Zoome später nochmal genauer rein damit man nur den ersten Frame
> Entschuldigen Sie bitte die vielen Fragen ;)
Kein Problem. Gute Fragen sind immer gerne willkommen.
Vielen Dank für die neuen Anstöße.
Mani schrieb:> Ich Zoome später nochmal genauer rein damit man nur den ersten Frame
Ich bin am Wochenende doch nicht dazu gekommen.
Für mit sieht die Triggerung auf Frame Start eigentlich gut aus.
Die Triggerung sieht gut aus, dass die ersten Bytes wiederholt werden
ist Modbus-typisch, die CRC-Bytes passen -- "nur" der Inhalt ist
rätselhaft.
Was ich auch nicht verstehe sind die 3er-Gruppen im Bild 1_Paketabstand.
Die Pausen innerhalb einer Gruppe sind sehr kurz für einen Time-Out, die
große Pause könnte einer sein, oder auch nicht.
Wenn das erste, kleinste, Paket ein Request ist, müsste das dritte auch
einer sein. Weil, 2 Antworten sind sehr unwahrscheinlich und nur eine
Antwort kann es mit der langen Pause kaum sein. Aber warum ist der 2.
Request viel länger als der erste?
Wenn das dritte Paket an den gleichen Slave gerichtet ist: warum
antwortet er nicht? Ist das garkeine normale Kommunikation weil kein
Slave angeklemmt ist? In einer normalen Anlage sollten doch viele
(verschiedene) Antworten kommen?
Nachdem die Pegel geklärt sind und zumindest beim kleinsten Paket die
CRC-Bytes stimmen, würde ich jetzt auf Linux weitermachen. In einer
Aufzeichnung über Minuten sieht man evt. ein Muster. Wenn man einen
Temperaturverlauf erkennen kann, muss man die Einzelheiten des
Protokolls evt. garnicht verstehen.
Bauform B. schrieb:> Die Triggerung sieht gut aus, dass die ersten Bytes wiederholt werden> ist Modbus-typisch, die CRC-Bytes passen -- "nur" der Inhalt ist> rätselhaft
Ja die CRC stimmt, ganz falsch kann es also nicht sein. Zu Modbus RTU
will es aber nicht passen.
Ich habe mittlerweile auch noch diesen Thread gefunden, da glauben sie
auch an ein anderes Protokoll:
https://www.eevblog.com/forum/projects/figuring-out-an-rs485-protocol/
Sie haben dort ähnliche Daten (7Byte im ersten Paket, ähnlicher Aufbau,
...) Nur dass es bei mir immer 3 Frames pro Set sind, wohingegen sie von
2 Frames sprechen und dass es Set's gibt die einen Frame mehr haben.
Bauform B. schrieb:> Was ich auch nicht verstehe sind die 3er-Gruppen im Bild 1_Paketabstand.> Die Pausen innerhalb einer Gruppe sind sehr kurz für einen Time-Out, die> große Pause könnte einer sein, oder auch nicht.
Ich vermute wie gesagt dass es 3 Frames pro Teilnehmer (Set) sind. Als
ich mal einen Thermostat abgezogen hatte gab es eine Lücke in jedem 5.
Set. Der Slave konnte ja nicht mehr antworten.
Bauform B. schrieb:> Wenn das erste, kleinste, Paket ein Request ist, müsste das dritte auch> einer sein.
Ja davon gehe ich auch aus. Alleine schon wegen den Pegeln (siehe
4_Differentiell_A_B_with_Bounds.png). Der erste Frame hat differentiell
einen größeren Pegel als der zweite.
Deshalb glaub ich dass der erste und der dritte Frame vom Master kommt.
Der zweite vom Slave.
Bauform B. schrieb:> Wenn das dritte Paket an den gleichen Slave gerichtet ist: warum> antwortet er nicht?
Eine weitere Vermutung von mir war, dass es immer ein Polling/Write pro
Teilnehmer gibt (erster und zweiter Frame) und dann pro Set ein
Broadcast.
Das würde erklären warum keiner der Teilnehmer auf den 3. Frame
antwortet und warum der 3. Frame im Verhältnis sehr lang ist.
So hätte man die Ist/Soll-Werte pro Teilnehmer im Zyklus von 200ms *
Anzahl Teilnehmer (Round Robin).
Die Broadcasts würden aber alle Slaves zyklisch alle 200ms erreichen.
Das macht vielleicht insofern Sinn wenn man Events hat die alle Slaves
gleichzeitig erreichen soll!? Wobei ich mir das bei Heizungen nicht so
richtig vorstellen kann :-) Höchstens wegen der Erhöhung der
Verfügbarkeit wenn mal ein paar Frames ausfallen sollten.
Bauform B. schrieb:> Nachdem die Pegel geklärt sind und zumindest beim kleinsten Paket die> CRC-Bytes stimmen, würde ich jetzt auf Linux weitermachen. In einer> Aufzeichnung über Minuten sieht man evt. ein Muster. Wenn man einen> Temperaturverlauf erkennen kann, muss man die Einzelheiten des> Protokolls evt. garnicht verstehen.
Das war nachdem ich den Link oben gefunden habe auch mein Gedanke.
Ich versuche mal einen Teil richtig ausgeschnitten mitzuloggen und
notiere die Temperaturen und Luftfeuchtigkeit der einzelnen Thermostate.
Scheinbar wird da auch in Fahrenheit gerechnet.
Ob IEEE float oder Temperaturen * 100 oder ähnliches muss man dann mal
schauen.
Danke soweit für deine Hilfe.
Die Werte an den Thermostaten waren dabei folgende (nicht in der
richtigen Reihenfolge da noch unbekannt):
1
Thermostat_122,6°C56%
2
Thermostat_222,0°C48%
3
Thermostat_322,5°C47%
4
Thermostat_423,1°C50%
5
Thermostat_522,6°C51%
0x1107 scheint eine Konstante zu sein. Vielleicht eine Frameanfang
Kennung?
Danach kommt ein Wort (16 Bit) das bei jedem Frame im Set gleich ist.
Vielleicht eine Kennung für den einzelnen Teilnehmer?
Das letzte Wort ist wie bereits festgestellt die Modbus-CRC.
Im ersten Frame im Set bleibt dann nur noch ein Byte (0xFF).
Vielleicht ein Funktionscode?
Beim zweiten Frame scheint dieser Funktionscode dann aber auf einmal aus
16 Bit zu bestehen. Auf jedenfall ist dieses Wort bei jedem Teilnehmer
0x4002 im zweiten Frame. Danach scheinen die eigentlichen IST-Werte vom
Thermostat zu kommen. Zumindest ist es der Bereich mit der meisten
Varianz.
Im dritten Frame steckt in meinen Augen die wenigste Information.
Der Frame ist bei jedem Teilnehmer annähernd gleich. Bis auf ein Byte im
hinteren Drittel. Hätte jetzt auch sein können dass es sich um
Steuerwerte handelt. Was mich aber wundern würde...da es in obigem Link
so ziemlich die gleichen Daten sind:
Da ich es mit dem Raspberry nicht hinbekommen habe die Frames richtig zu
trennen, habe ich kurz einen ESP32 mit RS485 Receiver an die
Heizungssteuerung geklemmt. Er schneidet nun die Frames bei jedem
Timeout aus und schickt dann einen UDP Frame ins WLAN mit den RS485
Daten.
Anschließend habe ich mit Wireshark (siehe Anhang) gespielt und versucht
die Frames zu dissectieren. Zusammen mit der Anleitung im Anhang die ich
auf dem russischen Uponor Server gefunden habe, kann ich nun die
IST-Temperatur im Frame ausmachen und umrechnen. Es ist ein UINT16. Die
Formel ist laut Anleitung:
Die Luftfeuchtigkeit ist ein einzelnes Byte das direkt die relative
Luftfeuchtigkeit angibt.
Ich war mir am Anfang nicht sicher ob es noch andere Frame-Typen gibt,
deshalb habe ich mal einen Startup gemacht (siehe Log im Anhang).
Scheinbar gibt es da keine Unterscheidung. Der Master pollt am Anfang
vergeblich. Irgendwann sind die Slaves gebootet und antworten.
Ich vermute mittlerweile folgende Reihenfolge:
2 Byte: Vermutlich eine Gruppen ID
2 Byte: Vermutlich die Teilnehmer ID
1 Byte: Funktionscode
x Byte: Daten (wobei die bei Funktionscode 0xFF scheinbar nicht
vorhanden sind)
2 Byte: Modbus CRC
Ich suche mal noch etwas weiter. Irgendwo müssen auch noch die
SOLL-Temperaturen sein usw.