ModBus TCP Interface

Einrichtung, Anschluss und Programmierung der HomeMatic CCU

Moderator: Co-Administratoren

Benutzeravatar
Black
Beiträge: 5470
Registriert: 12.09.2015, 22:31
System: Alternative CCU (auf Basis OCCU)
Wohnort: Wegberg
Hat sich bedankt: 419 Mal
Danksagung erhalten: 1070 Mal
Kontaktdaten:

Re: ModBus TCP Interface

Beitrag von Black » 11.07.2022, 11:24

32Bit Float entspricht ja dem Datentyp Single bei aktuellen Hochsprachen

Entweder sich für sie Umsetzung kleines Programm in einem CrossCompiler schreiben
oder
gucken ob der integrierte TCL die convertierung mit dem Datentyp beherrscht
oder
gemäß https://en.wikipedia.org/wiki/Single-pr ... int_format sich selber die Umrechnung coden

Black
Wenn das Fernsehprogramm immer mehr durch nervende Werbung unterbrochen wird und der Radiomoderator nur noch Müll erzählt, ist es besser, die Zeit für sinnvolle Dinge zu nutzen -
mal aufs Klo zu gehen, ein Bier zu holen oder einfach mal den roten AUS-Knopf zu drücken. Klick - und weg

Script Time Scheduler V1.3
AstroSteuerung über Zeitmodul flexibel mit Offset / spätestens, frühestens
SDV 5.03.01 Das umfassende Entwicklungs und Diagnosetool für Homematik
Selektive Backups - Nützliche Dinge, die die WebUI nicht kann

Intel NUC6 Celeron 16GB mit 512GB SSD unter Proxxmox mit insgesamt 5 VM: 2 x bloatwarebefreiter Raspberrymatik, 2 x IOBroker als Middleware und einer MariaDB zur Archivierung. Verbrauch: 6W

technical contribution against annoying advertising

mario12345
Beiträge: 93
Registriert: 17.04.2013, 16:40

Re: ModBus TCP Interface

Beitrag von mario12345 » 11.07.2022, 13:35

Oh das ist für mich zu kompliziert - dem kann ich nicht mehr folgen :roll:

Ich habe einfach die Sachen von Indi55 (danke dafür nochmal) in die Verzeichnisse kopiert und dann im Script die Adressen angepasst die ich brauche.
Habe mir die TCL zwar mal angeschaut aber leider nur Bahnhof gesehen.
Kann man mit 32Bit denn keine größere Zahl darstellen oder was passiert ab 32000, weil bis dahin wird die Zahl ja richtig ausgelesen.

richard.mayer
Beiträge: 21
Registriert: 12.05.2016, 11:47

Re: ModBus TCP Interface

Beitrag von richard.mayer » 11.07.2022, 21:22

Also ich werde aus dieser Library /usr/local/addons/modbus/modbus.tcl, die vom modbus_interface.tcl verwendet wird auch nicht schlau.

Das hier scheint ja irgendwie der Programmteil "read holding registers" zu sein:

Code: Alles auswählen

proc ::modbus::cmd03_pack {sta addr len} {                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                 
# function : read holding registers                                                                                                                                                                                                                                                              
    # station : 1 byte                                                                                                                                                                                                                                                                           
    # function :  1 byte (always 0x03)                                                                                                                                                                                                                                                           
    # addr : 2 bytes                                                                                                                                                                                                                                                                             
    # read len : 2 bytes (how much registers)                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                 
# response                                                                                                                                                                                                                                                                                       
    #station : 1 byte                                                                                                                                                                                                                                                                            
    #function : 1 byte (always 0x03)                                                                                                                                                                                                                                                             
    #byte count : 1 byte                                                                                                                                                                                                                                                                         
    #data : N*2 bytes (reg1_low...reg1_hi...reg2_low...reg2_hi...)                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                 
    # 1+1+1+2*len                                                                                                                                                                                                                                                                                
    # sta=>1 fun=>1 dlen=>1                                                                                                                                                                                                                                                                      
        # return [list [binary format ccSS $sta 0x03 $addr $len] [expr 1+1+1+2*$len]]                                                                                                                                                                                                            
        return [list [binary format ccSS $sta 0x03 $addr $len] [expr 1+1+1+2*$len]]                                                                                                                                                                                                              
}                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                 
proc ::modbus::cmd03_unpack {reqCmd rspCmd} {                                                                                                                                                                                                                                                    
# response                                                                                                                                                                                                                                                                                       
    #station : 1 byte                                                                                                                                                                                                                                                                            
    #function : 1 byte (always 0x03)                                                                                                                                                                                                                                                             
    #byte count : 1 byte                                                                                                                                                                                                                                                                         
    #data : N*2 bytes (reg1_low...reg1_hi...reg2_low...reg2_hi...)                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                 
    if {[string range $reqCmd 0 1] != [string range $rspCmd 0 1]} {return [list]}                                                                                                                                                                                                                
    if {[binary scan [string range $reqCmd 4 5] S reqBytes] == 0} {return [list]}                                                                                                                                                                                                                
    set data [string range $rspCmd 3 end]                                                                                                                                                                                                                                                        
    set len [string length $data]                                                                                                                                                                                                                                                                
    set ret [list]                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                 
    for {set i 0} {$i < $len} {incr i} {                                                                                                                                                                                                                                                         
        binary scan [string range $data $i [incr i]] S byte                                                                                                                                                                                                                                      
        lappend ret $byte                                                                                                                                                                                                                                                                        
    }                                                                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                 
    return $ret                                                                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                                                                                 
}                                                                    
Ich hab aber keine Ahnung, wie man hier den zurückgegebenen Datentyp anpassen sollte.
Das müsste sich mal jemand anschauen, der sich auskennt mit tcl... :(

picnic
Beiträge: 2
Registriert: 11.08.2017, 09:06
System: CCU

Re: ModBus TCP Interface

Beitrag von picnic » 13.07.2022, 10:23

Hallo Zusammen !

sehr interessant das ganze - ich scheitere zur Zeit noch am EM24 :-(
Frage an Mario, kannst Du hier veröffentlichen wie Du den EM24 ausliest ?
Ich erhalte immer nur 0 zurück.

Möchte hier am Ende mit dem Überschuss den stufenlosen E-Heizstab (ATON) füttern.
(steuer ich über PWM an - das funktioniert schon ganz gut)

Gruß in die Runde, Thorsten

JensDev
Beiträge: 19
Registriert: 10.03.2020, 11:12
System: Alternative CCU (auf Basis OCCU)
Wohnort: Köln
Hat sich bedankt: 4 Mal
Danksagung erhalten: 1 Mal

Re: ModBus TCP Interface

Beitrag von JensDev » 13.07.2022, 19:54

richard.mayer hat geschrieben:
11.07.2022, 21:22
Das hier scheint ja irgendwie der Programmteil "read holding registers" zu sein:
Ich hab aber keine Ahnung, wie man hier den zurückgegebenen Datentyp anpassen sollte.
Das müsste sich mal jemand anschauen, der sich auskennt mit tcl... :(
Man kann auf die Schnelle eine weitere Funktion "13" einbauen, die dann bei Länge 2 die Umrechnung macht.

Einfach das hier in die modbus.tcl einfügen z.B. unterhalb der cmd03_unpack:

Code: Alles auswählen

proc ::modbus::cmd13_pack {sta addr len} {

# function : read holding registers
    # station : 1 byte
    # function :  1 byte (always 0x03)
    # addr : 2 bytes
    # read len : 2 bytes (how much registers)

# response
    #station : 1 byte
    #function : 1 byte (always 0x03)
    #byte count : 1 byte
    #data : N*2 bytes (reg1_low...reg1_hi...reg2_low...reg2_hi...)

    # 1+1+1+2*len
    # sta=>1 fun=>1 dlen=>1
        # return [list [binary format ccSS $sta 0x03 $addr $len] [expr 1+1+1+2*$len]]
        return [list [binary format ccSS $sta 0x03 $addr $len] [expr 1+1+1+2*$len]]
}

proc ::modbus::cmd13_unpack {reqCmd rspCmd} {
# response
    #station : 1 byte
    #function : 1 byte (always 0x03)
    #byte count : 1 byte
    #data : N*2 bytes (reg1_low...reg1_hi...reg2_low...reg2_hi...)

    if {[string range $reqCmd 0 1] != [string range $rspCmd 0 1]} {return [list]}
    if {[binary scan [string range $reqCmd 4 5] S reqBytes] == 0} {return [list]}
    set data [string range $rspCmd 3 end]
    set len [string length $data]
    set ret [list]

    for {set i 0} {$i < $len} {incr i} {
        binary scan [string range $data $i [incr i]] S byte
        lappend ret $byte
    }
    if { $len == 2 } {
       if { $ret < 0 && $ret != "" } {
           set ret [ expr 65536 + $ret ]
       }
    }
    return $ret

}
Dann nimmst du statt

Code: Alles auswählen

tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 03 30535 10 | /usr/bin/awk '{print $2}' 

Code: Alles auswählen

tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 13 30535 10 | /usr/bin/awk '{print $2}'
Sollte klappen - könnte es aber nur Debug-mäßig ausprobieren, habe gerade kein Register in unserem SolarEdge Wechselrichter mit einem passenden Wert...

Wenn du eine Doku zu den Registern hast und etwas Code-nahe Fleißarbeit nicht scheust, kann man das auch schöner machen und mit einer Abfrage mehr oder alle Werte auf einen Rutsch / Aufruf auslesen.

Viele Grüße,
Jens

richard.mayer
Beiträge: 21
Registriert: 12.05.2016, 11:47

Re: ModBus TCP Interface

Beitrag von richard.mayer » 14.07.2022, 11:06

Vielen Dank für die Rückmeldung,

ich habe die Anpassung mit der if-Schleife als cmd13 eingefügt und grundsätzlich scheint die Abfrage zu funktionieren.
Jetzt sitze ich gespannt vor der Status-Anzeige und warte darauf, dass die 32kWh gerissen werden... ;)

Zu dem zweiten Thema:
Mehrere Register in einem Rutsch auszulesen würde wahrscheinlich wirklich Sinn machen.
Ich verwende die Werte in Homematic als Systemvariablen für eine AIO Creator Neo Statusanzeige und für das Schalten einen Warmwasserheizstabs und eines Luftentfeuchters.
Das sind 5 Register, die ich momentan alle 10 Sekunden mit modbus_interface.tcl auslese.
Wider Erwarten funktioniert das sogar stabil und weder Raspberrymatic noch der SMA-WR haben ein Problem mit dem häufigen Auslesen.

Aber bzgl. Effizienz wäre es wahrscheinlich wirklich cooler, mehrer Werte auf einmal auszulesen...

Schöne Grüße und besten Dank
Ich werden berichten, ob die Darstellung ab 32kWh auch noch korrekt ist.

Benutzeravatar
funkleuchtturm
Beiträge: 2365
Registriert: 13.06.2011, 16:42
Hat sich bedankt: 23 Mal
Danksagung erhalten: 357 Mal
Kontaktdaten:

Re: ModBus TCP Interface

Beitrag von funkleuchtturm » 14.07.2022, 12:40

JensDev hat geschrieben:
13.07.2022, 19:54
Man kann auf die Schnelle eine weitere Funktion "13" einbauen, die dann bei Länge 2 die Umrechnung macht.
Ich hab bei meinem Umrichter (Kostal Piko IQ) 32bit float Zahlen, also Länge gleich 4 Bytes.
Werden mit dieser Änderung nun 16bit float oder 32bit float dekodiert ??

Die normale Abfrage von einfachen 2Byte-Integerzahlen funktioniert einwandfrei :D
die Dekodierung von 32bit-float mit der neuen Funktion '13' funktioniert leider nicht.
Viele Gruesse
Eugen
________________________________________________
SmartHome-Eintopf mit feinem Homeduino-Gemüse
... und für Feinschmecker gibt´s den WIFFI, den WEATHERMAN-2, den PULSECOUNTER und den AIRSNIFFER
mit vielen Kochrezepten für den ambitionierten Homematiker

richard.mayer
Beiträge: 21
Registriert: 12.05.2016, 11:47

Re: ModBus TCP Interface

Beitrag von richard.mayer » 14.07.2022, 14:36

Bei mir tut es mit der Änderung leider auch noch nicht:

Code: Alles auswählen

root@ccu:~# tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 13 30535 2
0 -29656root@ccu:~#

root@ccu:~# while true; do date; tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 13 30535 10 | /usr/bin/awk '{print $2}'; sleep 5;  done
Thu Jul 14 13:53:01 CEST 2022
32739
Thu Jul 14 13:53:06 CEST 2022
32749
Thu Jul 14 13:53:11 CEST 2022
32755
Thu Jul 14 13:53:16 CEST 2022
32765
Thu Jul 14 13:53:21 CEST 2022
-32764
Thu Jul 14 13:53:27 CEST 2022
-32754
Thu Jul 14 13:53:32 CEST 2022
-32748
Thu Jul 14 13:53:37 CEST 2022
-32738
Thu Jul 14 13:53:42 CEST 2022
-32731
Thu Jul 14 13:53:47 CEST 2022
-32721
Thu Jul 14 13:53:52 CEST 2022
-32715
Laut Doku ist die "Anzahl zusammenhängender SMA Register" = 2, "Datentyp SMA" = U32, "Datenformat SMA" = FIX0.

Benutzeravatar
funkleuchtturm
Beiträge: 2365
Registriert: 13.06.2011, 16:42
Hat sich bedankt: 23 Mal
Danksagung erhalten: 357 Mal
Kontaktdaten:

Re: ModBus TCP Interface

Beitrag von funkleuchtturm » 15.07.2022, 13:45

Um mit dem originalen TCL-Skript auch Gleitkommavariablen (32bit-float) von meinem Solar-Umrichter Kostal Piko IQ auszuwerten, habe ich das hier beschriebene HM-Skript erstellt. Dieses Skript rufe ich alle 60sec auf und hole damit vom Umrichter die gewünschten Messwerte ab.
Besonders interessant für die Hauautomation ist die aktuelle vom Umrichter abgegebene Leistung im 32bit-float-Format. Um herauszufinden, auf welcher Modbus-Adresse diese Information verfügbar ist, kann man mit diesem kostenlosen "Modbus Master Simulator" die verschiedene "Holding Register" in verschiedenen Zahlenformaten ansehen.
Bei mir liegt die abgegebene AC-Leistung auf Adresse 172, die device ID ist 71 und der Port ist 1502 (meist ist der Port 502). Mit diesen Informationen wird das nachfolgende HM-Skript angepasst:

Code: Alles auswählen

!modbus_tcp 32bit float Register auslesen mit TCL skript 
!https://homematic-forum.de/forum/viewtopic.php?f=26&t=55722&hilit=tcl+script#p553720
!Installation:
!Schritt 1: mit WinSCP im CCU-verzeichnis /usr/local/addons/ ein Unterverzeichnis modbus anlegen
!Schritt 2: in dieses neue Verzeichnis die tcl-Files modbus.tcl und modbus_interface.tcl reinkopieren
!Schritt 3: Das folgende HM-Skript mit den Registernummern und Daten des eigenen Umrichters anpassen 
!Schritt 4: Dieses HM-Skript auf der CCU mit Steuerprogramm z.B. alle 60sec aufrufen
!Schritt 5: Systemvariable "kostal_leistung" vom Typ Zahl auf der CCU anlegen 

string lGetOut;
string lGetErr;
 system.Exec("tclsh /usr/local/addons/modbus/modbus_interface.tcl 192.168.178.35 1502 71 03 172 2",&lGetOut,&lGetErr);
!system.Exec("tclsh /usr/local/addons/modbus/modbus_interface.tcl <    IP      > port ID 03 adres 2",&lGetOut,&lGetErr);

var hword = lGetOut;                        !WriteLine(hword);
integer space_pos = hword.Find(" ");        !WriteLine(space_pos);
var lword = 0 + hword.Substr(0,space_pos);
if(lword < 0) {lword = lword + 32768;}      !WriteLine(lword); 
hword = 0 + hword.Substr(space_pos);          
if(hword < 0) {hword = hword + 32768;}      !WriteLine(hword); 

integer hochzahl = hword / 128;             !WriteLine(hochzahl);
if( (hochzahl & 128) > 0) {hochzahl = 1 + (hochzahl & 127);} else {hochzahl = -128 + 1 + (hochzahl & 127);}

real mantisse = 1.0;
if( (hword & 64) > 0)     {mantisse = mantisse + 0.5; }
if( (hword & 32) > 0)     {mantisse = mantisse + 0.25; }
if( (hword & 16) > 0)     {mantisse = mantisse + 0.125; }
if( (hword & 8 ) > 0)     {mantisse = mantisse + 0.0625; }
if( (hword & 4 ) > 0)     {mantisse = mantisse + 0.03125; }
if( (hword & 2 ) > 0)     {mantisse = mantisse + 0.015625; }
if( (hword & 1 ) > 0)     {mantisse = mantisse + 0.0078125; }

if( (lword & 32768) > 0)  {mantisse = mantisse + 0.00390625; }
if( (lword & 16384) > 0)  {mantisse = mantisse + 0.001953125; }
if( (lword & 8182 ) > 0)  {mantisse = mantisse + 0.0009765625; }
if( (lword & 4096 ) > 0)  {mantisse = mantisse + 0.00048828125; }
if( (lword & 2048 ) > 0)  {mantisse = mantisse + 0.000244140625; }
if( (lword & 1024 ) > 0)  {mantisse = mantisse + 0.0001220703125; }
if( (lword & 512  ) > 0)  {mantisse = mantisse + 0.0000610351562; }
if( (lword & 256  ) > 0)  {mantisse = mantisse + 0.0000305175781; }

if( (lword & 128) > 0)    {mantisse = mantisse + 0.0000152587890; }
if( (lword & 64 ) > 0)    {mantisse = mantisse + 0.0000076293945; }
if( (lword & 32 ) > 0)    {mantisse = mantisse + 0.0000038146972; }
if( (lword & 16 ) > 0)    {mantisse = mantisse + 0.0000019073486; }
if( (lword & 8  ) > 0)    {mantisse = mantisse + 0.0000009536743; }
if( (lword & 4  ) > 0)    {mantisse = mantisse + 0.0000004768371; }
if( (lword & 2  ) > 0)    {mantisse = mantisse + 0.0000002384185; }
if( (lword & 1  ) > 0)    {mantisse = mantisse + 0.0000001192092; }

mantisse = mantisse * hochzahl.Exp2();
if( (hword & 32768) > 0) {mantisse = (-1) * mantisse;}   

dom.GetObject('kostal_leistung').State(0.001* mantisse);

WriteLine(mantisse);
Vorher muß man natürlich die TCL-Programme von Indi55 installieren und eine Systemvariable "kostal_leistung" im Format "Zahl" anlegen, auf welche die Leistung im Takt des Skriptaufrufes repliziert wird. Die Leistung wird in kW angezeigt.
Viele Gruesse
Eugen
________________________________________________
SmartHome-Eintopf mit feinem Homeduino-Gemüse
... und für Feinschmecker gibt´s den WIFFI, den WEATHERMAN-2, den PULSECOUNTER und den AIRSNIFFER
mit vielen Kochrezepten für den ambitionierten Homematiker

JensDev
Beiträge: 19
Registriert: 10.03.2020, 11:12
System: Alternative CCU (auf Basis OCCU)
Wohnort: Köln
Hat sich bedankt: 4 Mal
Danksagung erhalten: 1 Mal

Re: ModBus TCP Interface

Beitrag von JensDev » 16.07.2022, 14:21

richard.mayer hat geschrieben:
14.07.2022, 14:36
Bei mir tut es mit der Änderung leider auch noch nicht:

Code: Alles auswählen

root@ccu:~# while true; do date; tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 13 30535 10 | /usr/bin/awk '{print $2}'; 
Laut Doku ist die "Anzahl zusammenhängender SMA Register" = 2, "Datentyp SMA" = U32, "Datenformat SMA" = FIX0.
Du lässt 10 Register auf einmal abfragen - im Code habe ich die Abfrage fix auf die Länge für 1 Register gesetzt - wirklich nur ein "dirty hack"... Wenn SMA da 2 Register hat, müsste ich nochmal fies nachdenken, dass ich das auch noch reinkriege.

Versuch mal:

Code: Alles auswählen

root@ccu:~# while true; do date; tclsh /usr/local/addons/modbus/modbus_interface.tcl wr1.mayer 502 3 13 30535 1 | /usr/bin/awk '{print $2}'; 
P.S.: @funkleuchtturm: Das ist auch keine Lösung für Float-Werte - aber das hast du ja jetzt per HM-Script hinbekommen. Unser SolarEdge-WR schickt kein natives Float, sondern hat dann ein extra Scaling-Register - damit wird dann ein Faktor kodiert.

Für SolarEdge habe ich mir das hier zusammengebaut, danach hatte ich etwas mehr Blick für TCL - ist aber leider schon wieder verschüttet... 2020 hatte man mehr Zeit ;-). Das kann man im Prinzip auch für anderes erweitern. Float-Konvertierung gibt es wie gesagt nicht und man muss die Fleißarbeit investieren die Register zu verdrahten mit Text.

Viele Grüße
Jens

Antworten

Zurück zu „HomeMatic Zentrale (CCU / CCU2 / CCU3 / Charly)“