Neues Modul für Geräte mit Modbus Schnittstelle über RS232 bzw. RS485

Begonnen von StefanStrobel, 12 Juli 2014, 14:50:22

Vorheriges Thema - Nächstes Thema

Roger

Hallo Stefan,
zu 2: Ja, wurde schon oft diskutiert. Aber was machen wir? Für jedes Register in parseInfo eine Eigenschaft Unit angeben können und im Reading mit Leerzeichen getrennt dranhängen? ReadingsNum zum Auslesen verwenden?

zu 3: Mein Zähler liefert Strom, Spannung, Verbrauch in W, Verbrauch in VA, Phasenverschiebung und noch mehr für jede der 3 Phasen und noch verschiedenen Durchschnitts- und Summenwerte. Der Zähler kann laut Doku 40 Werte (80 Register) mit einer Abfrage liefern - also Unterschied zu Deinem Modbus Gerät. Wenn Du in die Richtung "Auslesen mehrere Werte in einen Read" geht, sollte hier die Anzahl konfigurierbar sein.
Nun zu meinem Ansinnen der unterschiedlichen Zeiten: Verbräuche sind z.B. wichtiger als Phasenverschiebung oder Durchschnitts- und Summenwerte. Die Verbräuche will ich genauer sehen -> also öfter auslesen. Meine Idee war hier: Angabe, ob Werte in jeden Durchlauf, jeden zweiten, oder nur jeden x-ten Durchlauf ausgelesen wird. Dann könnte ich den Zyklus auf 15s stellen. Derzeit dauert ein Durchlauf zu lange, als das ich alle (fast 100) Register bei 60s Zyklus auslesen kann. (Bin allerdings noch mit 9600Baud unterwegs.)

zu 4: Die Attribute:  "timeout ",  "minSendDelay ", und "minCommDelay " kennt auch mein Modul, da ich einfach Dein ModbusSET als Vorlage genommen habe  :).
Folgendes Szenario: Device-Modul wurde mit zyklischer Abfrage gestartet. Ich kann keinen einzelnen get-Befehl ohne Fehlermeldung timeout absetzte, wenn der die Werte gerade zyklisch abgefragt werden.

zu 5: Device-Modul wurde ohne zyklischer Abfrage gestartet. Fehlermeldung timeout, wenn ich 3 get-Befehle über Telnet absetzte (minSendDelay & minCommDelay = Default). Werte zu den Abständen habe ich keine in der Doku gefunden. Der Zähler arbeitet aber halbduplex.

zu 6: konfigurierbare Defaults finde ich prinzipiell gut, insbesondere für format. Allerdings ist len eher eine Eigenschaft für %fCodeMap und nicht für die einzelnen Register. Bei meinem Stromzähler ist len generell 2. Wer aber ein Modbus-Gerät mit verschiedenen Längen der in %fCodeMap definierten Befehlsarten hat, der tippt sich den Wolf  :(

7. Würde Dein Basis-Modul auch mit der Verarbeitung von zwei unterschiedlichen Modbus-Geräten an einem Bus zurechtkommen? Die %fCodeMap Definition heißt ja im Device-Modul immer gleich.

Vielen Dank. Gute Nacht - bis bald
Roger
Zotac, BBB, RPIs mit 10*FHEM
2*HM-LAN, 2*JeeLink, 2*RS485, SignalESP
HomeMatic, PCA301 Komponenten, ModBus: Stromzähler, Fronius WR, Shelly

golem

Hallo Stefan,

ich versuche mich gerade darin ein Modul für einen Energiemonitor UMG103 von Janitza machen. Soweit funktioniert das schon mal. Es ist schon einige der Verfügbaren und wichtigen Variablen drin, aber längst noch nicht alle. Zum testen reicht es aber.

Die Probleme mit dem Timeout bei einzelnen GET oder SET habe ich auch.

Das Modul habe ich angehängt.

Gruß Denis
Pi - Max-Lan - 8x max Ht -3x Max WT - Max Fk -modbus umg103- 2x Arduino mit Firmata Ethernet- ws300 - 433Mhz Sender Empfänger - 7x 1wire ds1820

StefanStrobel

Hallo Roger und Denis,

Ich habe die Ursache für die Timeouts gefunden. Leider komme ich aber frühestens am Wochenende dazu das Problem zu fixen.

Gruß
    Stefan

Roger

Hallo Denis,
willkommen im Modbus-Club  ;D. Mann mit Janitza hast Du ja ein feines Gerät.
Ich habe mir mal die Bezeichnungen in Deinem Device-Modul angeschaut und meines so ähnlich gemacht. Habe auch noch nicht alle Register eingetragen.
Die Zeile 49 my %SET10parseInfo = ( habe ich an den Gerätenamen angepasst (ist auch nochmal in Zeile761 relevant).
Ich habe gesehen, dass Du die Einheiten in format => '%.1f Wh', angegeben hast. Kommen die auch in den Readings an? Habe ich probiert, bei mir nicht  :(.
Anbei mal mein Device-Modul.

@Stefan: keine Eile, Grundfunktionen laufen ja  :)

Roger
Zotac, BBB, RPIs mit 10*FHEM
2*HM-LAN, 2*JeeLink, 2*RS485, SignalESP
HomeMatic, PCA301 Komponenten, ModBus: Stromzähler, Fronius WR, Shelly

golem

Hallo Roger,
format => '%.1f Wh',
funktioniert, siehe Anhang. Die Bezeichnungen habe ich aus der Modbus-Adressenliste des Gerätes übernommen. Ich werde es wohl nicht alle Adressen mit einbauen, das wird zu viel.

gruß Denis
Pi - Max-Lan - 8x max Ht -3x Max WT - Max Fk -modbus umg103- 2x Arduino mit Firmata Ethernet- ws300 - 433Mhz Sender Empfänger - 7x 1wire ds1820

StefanStrobel

#20
Hallo Roger und Denis,

anbei ein neuer Zwischenstand. Das Problem mit den Timeouts sollte behoben sein, wenngleich ein Timeout prinzipiell natürlich trotzdem mal auftreten kann.
Ich habe einiges an den Strukturen geändert. Statt der fCodeMap gibt es jetzt ein devicveInfo Hash, in dem die function codes und diverse default-Werte eingestellt werden können.
Ich denke wenn Ihr das ModbusSET Modul anseht, sollte klar sein, wie es gemeint ist.
Defaults für timeouts und delays könnt Ihr darüber jetzt auch festlegen.
Auch Defaults für Längen, Unpack und Format. Das kann man dann in der parseInfo weglassen, wenn der Default genommen werden soll.

Da verschiedene read function codes mehrere Register auf einmal lesen können, könnt Ihr im deviceInfo Hash angeben, wie viele maximal bei getUpdate auf einmal gelesen werden können
(combine). GetUpdate versucht dann die read function codes zu optimieren / kombinieren.

Gruss
   Stefan

Edit 13.2.15: removed attached old versions

Roger

Hallo Stefan,
vielen Dank. Bin am testen. Bisher keine timeouts.  :D
Das mit dem devicveInfo finde ich Klasse, insbesondere die Defaults für parseInfo.



zu 2: (Einheiten): Nutze format dafür ('%.1f V' für Volt). Der ist dann aber fast bei jedem Register anders, so dass der Default hier nichts nützt  :(
Willst Du noch etwas eigenes nur für Einheiten einbauen?
@Denis: Einheiten werden nun auch bei mir in den Attributen angezeigt. Ursache bei Nichtanzeige war event-on-change-reading
#attr HA_SDM630M_1 event-on-change-reading .*:.2   # Einheiten verschwinden im Reading
attr HA_SDM630M_1 event-on-change-reading .*      # Einheiten werden angezeigt




zu 3: (unterschiedliche Zykluszeiten): Halte ich für sinnvoll, da ich fast 100 Register auslese, einige aber nicht so häufig brauche. Der Zähler von Denis hat auch Tonnen von Registern
Willst Du noch was in Richtung unterschiedliche Abfragezyklen tun?



zu 4&5: (timeouts): timeouts sind bisher nicht mehr aufgetreten, auch ein get , wenn gerade eine Abfragezyklus läuft, funktioniert Ohne Probleme. Ich habe combine => 10 eingestellt. erledigt!



zu 6: (Len, Defaults): mit devicveInfo erledigt  :D



zu 7: (mehrere Devices): Denis hat die Namen so wie in 98_ModbusSET gelassen, ich habe die geändert, da ich Deinen Namen SET auf meinen anpassen wollte:
sub
ModbusSDM630M_Initialize($)
{ my ($modHash) = @_;
require "$attr{global}{modpath}/FHEM/98_Modbus.pm";
$modHash->{DefFn}     = "ModbusSDM630M_Define";
$modHash->{UndefFn}   = "ModbusSDM630M_Undef";
$modHash->{SetFn}     = "ModbusLD_Set"; # provided by physical module
$modHash->{GetFn}     = "ModbusLD_Get"; # provided by physical module
$modHash->{parseInfo} = \%SDM630MparseInfo;
$modHash->{deviceInfo} = \%deviceInfo; # map that defines properties of the device like

Dann muss ich mich auch als Client im Basismodul eintragen.
Wie ist das gedacht? Wie soll die Abfrage unterschiedlicher Modbus-Geräte über einen Bus funktionieren?

Wie gesagt es funktioniert alles zufriedenstellend. Fragen oben sich Sahnehäubchen. :D
Werde mich demnächst mal dem Schreiben zum Gerät widmen.
Roger
Zotac, BBB, RPIs mit 10*FHEM
2*HM-LAN, 2*JeeLink, 2*RS485, SignalESP
HomeMatic, PCA301 Komponenten, ModBus: Stromzähler, Fronius WR, Shelly

StefanStrobel

Hallo Roger,

Das mit den Einheiten im Reading halte ich nicht für eine gute Idee. Bei der Weiterverarbeitung, beim Plotten etc. wird das ungeschickt. Es gibt im Wiki auch eine Entwicklungsrichtlinie, in der explizit steht
Zitat
Readings enthalten grundsätzlich genau einen Wert und diesen ohne Einheit.

Für die Abfragezyklen wäre die einfachste Lösung in der ParseInfo je Reading ein minInterval anzugeben.
Die getUpdate Funktion, die mit einem Basisintervall aufgerufen wird, würde dann nur die Werte abfragen, bei denen minInterval abgelaufen ist. Die Intervalle wären dann effektiv immer Vielfache des Basisintervalls.

Zur Clientliste: Ich könnte in der nächsten Version die Clientliste abschaffen und das mit der IODevice-Zuordnung anders lösen. Ich schau mir das bei nächster Gelegenheit noch mal an.

Wegen mehrerer Geräte am selben Bus: das sollte einfach funktionieren ;-)
Jedes Gerät hat eine eigene Id und das Basismodul speichert die Zuordnung.

Gruß
    Stefan

golem

Hallo Stefan,

kurze Rückinfo. Ich habe die neue Version getestet.

Habe im deviceInfo   combine=>50 eingestellt - funktioniert.
write=>6 und 16 habe ich geteste, bei beiden bekomme ich immer ein Timeout.

gruß Denis

Pi - Max-Lan - 8x max Ht -3x Max WT - Max Fk -modbus umg103- 2x Arduino mit Firmata Ethernet- ws300 - 433Mhz Sender Empfänger - 7x 1wire ds1820

StefanStrobel

Hallo Denis,

Function Code 6 funktioniert bei meiner Wärmepumpe ohne Probleme. Setz doch mal verbose bei Deinem Modul und dem Basismodul auf 5 und schick den relevanten Teil aus dem Log. Vielleicht sieht man da ja was.
Eigentlich sollte ein Gerät zumindest eine Fehlermeldung zurücksenden ...

Gruß
     Stefan

Roger

Hi Stefan,
habe mich mal dem Schreiben gewidmet. Bekomme: got exception code 313434 / 01  :(
Versuche mit Modbus protocol function code 16 zu schreiben (allerdings steht an anderer Stelle der Doku was von 15  >:()

Hier das Log:

2015.02.03 20:14:56 5: HA_SDM630M_1: Set found option Demand_Period__minutes in setHash created from parseInfo data
2015.02.03 20:14:56 5: HA_Modbus_1: Add frame to queue: 0110000200000008e8, force send
2015.02.03 20:14:56 5: HA_SDM630M_1: _Send: creating new queue
2015.02.03 20:14:56 5: HA_Modbus_1: handle send queue, force
2015.02.03 20:14:56 4: HA_Modbus_1: handle queue sends frame: 0110000200000008e8 (fcode 16 to 1)
2015.02.03 20:14:56 5: SW: 0110000200000008e8
2015.02.03 20:14:56 5: HA_SDM630M_1: ReadAnswer called and remaining timeout is 1.99733996391296 requested reading is Demand_Period__minutes
2015.02.03 20:14:56 5: SetSilent ReadAnswer got: 01
2015.02.03 20:14:56 5: SetSilent ReadAnswer got: 0190
2015.02.03 20:14:56 5: SetSilent ReadAnswer got: 019001
2015.02.03 20:14:56 5: SetSilent ReadAnswer got: 0190018d
2015.02.03 20:14:56 5: SetSilent ReadAnswer got: 0190018dc0
2015.02.03 20:14:56 5: HA_Modbus_1: ParseFrames got: 0190018dc0
2015.02.03 20:14:56 4: HA_Modbus_1: ParseFrames: fcode 144 from 1, data 01 calc crc = 49293, read = 49293 expect 16 from 1 for module HA_SDM630M_1
2015.02.03 20:14:56 5: HA_Modbus_1: read got error code 313434 / 01
2015.02.03 20:14:56 5: HA_SDM630M_1: ReadAnswer done, err = got exception code 313434 / 01

Verstehe die Anwort 313434 nicht, eher müsste es 9001 (siehe oben 2015.02.03 20:14:56 5: HA_Modbus_1: ParseFrames got: 0190018dc0) heisen. Dieses würde dann: "The function code is not supported by the product" bedeuten. Werde es später mal mit fcode=15 testen.


zu 2: (Einheiten):
ZitatDas mit den Einheiten im Reading halte ich nicht für eine gute Idee. Bei der Weiterverarbeitung, beim Plotten etc. wird das ungeschickt. Es gibt im Wiki auch eine Entwicklungsrichtlinie, in der explizit steht
OK, habe ja auch Probleme mit
attr HA_SDM630M_1 event-on-change-reading .*[b]:.2[/b] gehabt. Aber wozu wurde dann ReadingsNum eingeführt? Und Plos sind kein Problem. Und nun?


zu 3: (unterschiedliche Zykluszeiten): noch offen


zu 7: (mehrere Devices):
ZitatWegen mehrerer Geräte am selben Bus: das sollte einfach funktionieren ;-)
Jedes Gerät hat eine eigene Id und das Basismodul speichert die Zuordnung.
Ja technisch funktioniert das prima. Ich weiss nur nicht, was Dein Basismodul sagt, wenn sich mehrere Device-Module (klar mit unterschiedlichen IDs) bei ihm anmelden. Klappt die Zuordnung der unterschiedlichen %deviceInfo, %parseInfo, Namen der SUBs usw.? Ich hänge noch mal mein aktuelles Device Module an. Vielleicht definiert Du mal zusätzlich zu Deinem Gerät -> meinen Zähler und schaust Dir die Logs an und ...


8: Fehlermeldungen beim Schreiben (siehe oben)



Hoffentlich nerven Dich meine Aufzählungen nicht so sehr  :-[. Ich will nur den Überblick behalten  :D.
Roger
Zotac, BBB, RPIs mit 10*FHEM
2*HM-LAN, 2*JeeLink, 2*RS485, SignalESP
HomeMatic, PCA301 Komponenten, ModBus: Stromzähler, Fronius WR, Shelly

Roger

Hallo Stefan,
habe mit dem Schreiben weitergemacht. fcode=16 ist richtig (nicht 15), aber das von 98_Modbus.pm generierte Schreib-Kommando scheint falsch zu sein.
Ein Schreiben auf Register 2 mit
set HA_SDM630M_1 Demand_Period__minutes 60
erzeugt folgendes Log:
2015.02.04 19:59:34 5: HA_SDM630M_1: Set found option Demand_Period__minutes in setHash created from parseInfo data
2015.02.04 19:59:34 5: HA_Modbus_1: Add frame to queue: 01100002000061c9, force send
2015.02.04 19:59:34 5: HA_SDM630M_1: _Send: creating new queue
2015.02.04 19:59:34 5: HA_Modbus_1: handle send queue, force
2015.02.04 19:59:34 4: HA_Modbus_1: handle queue sends frame: 01100002000061c9 (fcode 16 to 1)
2015.02.04 19:59:34 5: SW: 01100002000061c9
2015.02.04 19:59:34 5: HA_SDM630M_1: ReadAnswer called and remaining timeout is 1.99713921546936 requested reading is Demand_Period__minutes
2015.02.04 19:59:36 3: HA_SDM630M_1: Timeout2 in ReadAnswer for Demand_Period__minutes
2015.02.04 19:59:36 1: Perfmon: possible freeze starting at 19:59:35, delay is 1.031
2015.02.04 19:59:36 4: HA_Modbus_1: timeout waiting for 16 from 1, Request was 01100002000061c9, last Buffer: 01030442700000ef90

Wenn ich das richtig sehe, so ist das erzeugte Kommando unvollständig/falsch:

Field Name  Hex-erwartet   Hex-98_Modbus.pm
Slave Address        01      01
Function             10      10
Start Address High   00      00
Start Address Low    02      02
Number of Reg. High  00      00
Number of Reg. Low   02      00
Byte Count           04
Data, Hi-Reg,Hi-Byte 42
Data, Hi-Reg,LowByte 70
Data, LowReg,Hi-Byte 00
Data, LowReg,LowByte 00
Error Check Low      67      61
Error Check High     D5      C9

Vielleicht liege ich in der Analyse richtig und es hilft Dir  :D
Roger
Zotac, BBB, RPIs mit 10*FHEM
2*HM-LAN, 2*JeeLink, 2*RS485, SignalESP
HomeMatic, PCA301 Komponenten, ModBus: Stromzähler, Fronius WR, Shelly

StefanStrobel

Hallo Roger,

Deine Analyse ist richtig, da ist noch ein kleiner Bug beim Senden. Wenn in der ParseInfo keine Länge angegeben ist, wird beim Write 0 verwendet statt dem Default.
Leider kann ich das erst am Wochenende korrigieren.
Du kannst aber zum Testen einfach mal len in der ParseInfo angeben.

Gruß
    Stefan

golem

Hi,

selber Fehler bei mir:

2015.02.06 10:56:37 5: ModbusRT485: handle send queue
2015.02.06 10:56:38 4: ModbusRT485: handle queue sends frame: 010313880008c0a2 (fcode 3 to 1)
2015.02.06 10:56:38 5: SW: 010313880008c0a2
2015.02.06 10:56:38 5: ModbusRT485: raw read: 0103104680780446cc95f04712eceb47983eca389a
2015.02.06 10:56:38 5: ModbusRT485: ParseFrames got: 0103104680780446cc95f04712eceb47983eca389a
2015.02.06 10:56:38 4: ModbusRT485: ParseFrames: fcode 3 from 1, data 104680780446cc95f04712eceb47983eca calc crc = 39480, read = 39480 expect 3 from 1 for module umg103
2015.02.06 10:56:38 5: ModbusRT485: _ParseRegs has returned, now returning from ParseFrames
2015.02.06 10:56:38 5: ModbusRT485: handle send queue
2015.02.06 10:56:38 4: ModbusRT485: handle queue sends frame: 010303e8002cc467 (fcode 3 to 1)
2015.02.06 10:56:38 5: SW: 010303e8002cc467
2015.02.06 10:56:38 5: ModbusRT485: raw read: 010358436715b843663ea043668fd143c7b2e243c77b6843c802883e0455993fe9b1e83f0f240a3fc5947e411f06e543d1c8bc42cd78d344050f94c0e2d6d0c1bdc0b0c21a9856c28ae9c441eee8ee43d22f024300eacb4410c97bc60d
2015.02.06 10:56:38 5: ModbusRT485: ParseFrames got: 010358436715b843663ea043668fd143c7b2e243c77b6843c802883e0455993fe9b1e83f0f240a3fc5947e411f06e543d1c8bc42cd78d344050f94c0e2d6d0c1bdc0b0c21a9856c28ae9c441eee8ee43d22f024300eacb4410c97bc60d
2015.02.06 10:56:38 4: ModbusRT485: ParseFrames: fcode 3 from 1, data 58436715b843663ea043668fd143c7b2e243c77b6843c802883e0455993fe9b1e83f0f240a3fc5947e411f06e543d1c8bc42cd78d344050f94c0e2d6d0c1bdc0b0c21a9856c28ae9c441eee8ee43d22f024300eacb4410c97b calc crc = 3526, read = 3526 expect 3 from 1 for module umg103
2015.02.06 10:56:38 5: ModbusRT485: _ParseRegs has returned, now returning from ParseFrames
2015.02.06 10:56:38 5: ModbusRT485: handle send queue
2015.02.06 10:56:42 5: ModbusRT485: handle send queue, force
2015.02.06 10:56:42 4: ModbusRT485: handle queue sends frame: 0106000901ded8 (fcode 6 to 1)
2015.02.06 10:56:42 5: SW: 0106000901ded8
2015.02.06 10:56:44 3: umg103: Timeout2 in ReadAnswer for Delete_Work
2015.02.06 10:56:45 4: ModbusRT485: timeout waiting for 6 from 1, Request was 0106000901ded8, last Buffer: 010358436715b843663ea043668fd143c7b2e243c77b6843c802883e0455993fe9b1e83f0f240a3fc5947e411f06e543d1c8bc42cd78d344050f94c0e2d6d0c1bdc0b0c21a9856c28ae9c441eee8ee43d22f024300eacb4410c97bc60d
2015.02.06 10:56:51 5: ModbusRT485: handle send queue
2015.02.06 10:56:51 4: ModbusRT485: handle queue sends frame: 010304ca0002e505 (fcode 3 to 1)
2015.02.06 10:56:51 5: SW: 010304ca0002e505
2015.02.06 10:56:51 5: ModbusRT485: handle send queue
2015.02.06 10:56:51 5: ModbusRT485: send busy, delay writing from queue
2015.02.06 10:56:51 5: ModbusRT485: handle send queue
2015.02.06 10:56:51 5: ModbusRT485: send busy, delay writing from queue
2015.02.06 10:56:51 5: ModbusRT485: raw read: 0103044248029dae94


Delete_Work ist in der ParseInfo folgend definiert

"h9"=>  { unpack => "c", # 9 Lösche_Arbeit 0 1 CHAR 0
name => "del_wh",
showget => 1,
expr => '$val',
                                        setexpr => '$val',
len => 1,
reading => "Delete_Work",
                                        set => 1,
                                        setmin => 0,
setmax => 1},

Gruß Denis
Pi - Max-Lan - 8x max Ht -3x Max WT - Max Fk -modbus umg103- 2x Arduino mit Firmata Ethernet- ws300 - 433Mhz Sender Empfänger - 7x 1wire ds1820

StefanStrobel

#29
Hallo Roger,

anbei eine neue Version.
Die Default-Länge sollte jetzt verwendet werden. Bei mir funktioniert sowohl fCode 6 als auch 16. Die Ausgabe der Fehlermeldung habe ich auch korrigiert und eine Klartextmeldung hinzugefügt.

Gruss
   Stefan

Edit 13.2.15: removed attached old versions