Modbus > Register bei E3DC S10 Energy Storage - Readings scrambled?

Begonnen von juanmax, 26 Juli 2017, 21:10:36

Vorheriges Thema - Nächstes Thema

juanmax

Hallo Community, erstmal entschuldige ich mich für mein Deutsch, aber ich vermute ihr wollt lieber in gebrochenes Deutsch als auf Englisch.

Hier mein Problem, ich hoffe ein Modbus Guru kann mich hier helfen.

Für ein Freund (bei mir läuft ein Fronius Umrichter über FHEM - Modbus perfekt) versuche ich seit ein Paar Woche die Kommunikation mit sein "Hauskraftwerk" über FHEM Modbus herzustellen. Ich habe die FroniusModbus modul angepasst und die kommunikation mit dem E3DC (so heißt die Kombo von Umrichter, Akku und alles...) funktioniert halbswegs. Manche readings sind OK, manche bleiben Konstant und noch dazu falsch. Ich habe in FHEM Forum nichts über die E3DC gefunden, und google hat mir auf loxone forum fündig gemacht. Da haben mir schon bestätigt dass die Register die ich nehme, eigentlich die richtige sind. In Loxone funtkioniert es wie erwartet (allerdings mit irgendwelche Umrechnungen)

Meine Vermutung ist das der Fehler in die Formatierung liegt, aber das kann eigentlich nicht erklären warum man bei manche (fast alle) Readings konstante Werte bekommt.

Kann eine von euch kurz drüber schauen? Vielen Dank!

Mein Fhem Readings
(OK/KO) / Register / Description / Value:

KO 40082 BattSOC   1536
KO 40070 Batt_Power_Watt constant   369107203
OK 40020 Device_model   S10 E AIO
OK 40052 Inverter SN   S10-4616430007XX
KO 40072 HomeLoad_Watt constant   5907440
KO 40001 MagicByte   21358
OK 40004 Manufacturer   E3/DC GmbH
KO 40068 PV_Power_Watt constant   256
OK 40036 SW_Version   OPTIONS10_2017_02

Beispiel Code für SOC in der 09_E3DC_Modbus_S10.pm (kann/soll ich die Datei anhängen?)

"h40082" => { # uint16; SOC
name => "BattSOC",         # internal name of this register in the hardware doc
reading => "BattSOC",         # name of the reading for this value
len => 1, # number of Registers this value spans
unpack => "s<", # defines the translation between data in the module and in the communication frame
polldelay => "x2",         # only poll this Value if last read is older than 2*Iteration, otherwiese



Danke in voraus!

Juanma

juanmax


##############################################
##############################################
# $Id: 98_E3DC_Modbus_S10.pm
#
# fhem Modul für E3DC S10 Energy Storage:
# verwendet Modbus.pm als Basismodul für die eigentliche Implementation des Protokolls.
#
# Fhem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# Fhem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with fhem.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
# Changelog:
# 2017-06-24 initial draft - debugging


package main;

use strict;
use warnings;
use Time::HiRes qw( time );

sub E3DC_Modbus_S10_Initialize($);
sub MinMaxChk($$$;$$); # prüft, ob ein Wert außerhalb Min/Max ist

# deviceInfo defines properties of the device.
# some values can be overwritten in parseInfo, some defaults can even be overwritten by the user with attributes if a corresponding attribute is added to AttrList in _Initialize.
#
my %E3DC_deviceInfo = (
"timing" => {
timeout => 2, # 2 seconds timeout when waiting for a response
commDelay => 0.7, # 0.7 seconds minimal delay between two communications e.g. a read a the next write,
# can be overwritten with attribute commDelay if added to AttrList in _Initialize below
sendDelay => 0.7, # 0.7 seconds minimal delay between two sends, can be overwritten with the attribute
# sendDelay if added to AttrList in _Initialize function below
},
"h" => { # details for "holding registers" if the device offers them
read => 3, # use function code 3 to read registers
write => 6, # use function code 6 to write registers
defPoll => 1, # All defined Input Registers should be polled by default unless specified otherwise in parseInfo or by attributes
defShowGet => 1, # default für showget Key in parseInfo
},
);

# %parseInfo:
# r/c/i+adress => objHashRef (h = holding register, c = coil, i = input register, d = discrete input)
# the address is a decimal number without leading 0
#
# Explanation of the parseInfo hash sub-keys:
# name internal name of the value in the modbus documentation of the physical device
# reading name of the reading to be used in Fhem
# set can be set to 1 to allow writing this value with a Fhem set-command
# setmin min value for input validation in a set command
# setmax max value for input validation in a set command
# hint string for fhemweb to create a selection or slider
# expr perl expression to convert a string after it has bee read
# map a map string to convert an value from the device to a more readable output string
# or to convert a user input to the machine representation
# e.g. "0:mittig, 1:oberhalb, 2:unterhalb"
# setexpr per expression to convert an input string to the machine format before writing
# this is typically the reverse of the above expr
# format a format string for sprintf to format a value read
# len number of Registers this value spans
# poll defines if this value is included in the read that the module does every defined interval
# this can be changed by a user with an attribute
# unpack defines the translation between data in the module and in the communication frame
# see the documentation of the perl pack function for details.
# example: "n" for an unsigned 16 bit value or "f>" for a float that is stored in two registers, "s" int16
# "l" int32, "L" uint32
# http://perldoc.perl.org/functions/pack.html#unpack-TEMPLATE%2cEXPR
# showget can be set to 1 to allow a Fhem get command to read this value from the device
# polldelay if a value should not be read in each iteration after interval has passed,
# this value can be set to a multiple of interval

#E3DC S10 Register info from Official E3DC S10 Doc. https://www.google.de/url?sa=t&rct=j&q=&esrc=s&source=web&cd=2&cad=rja&uact=8&ved=0ahUKEwjigrqEodfUAhVEVhQKHUaXDh8QFggsMAE&url=http%3A%2F%2Fwww.loxwiki.eu%2Fdownload%2Fattachments%2F9339566%2FModuBus-Dokumentation_2016-08-05.pdf%3Fversion%3D1%26modificationDate%3D1471507657000%26api%3Dv2&usg=AFQjCNFq3mH3ZWkpJI9VCyvK2W_NDRH2eA

my %E3DC_parseInfo = (

"h40001" => { # Uint16; Manufacturer
name => "Mg", # internal name of this register in the hardware doc
reading => "MagicByte", # name of the reading for this value
len => 1, # number of Registers this value spans
unpack => "s<", # defines the translation between data in the module and in the communication frame
poll => "once", # only poll once after define (or after a set)
},

"h40004" => { # String32; Manufacturer
name => "Mn", # internal name of this register in the hardware doc
reading => "Manufacturer", # name of the reading for this value
len => 16, # number of Registers this value spans
unpack => "A*", # defines the translation between data in the module and in the communication frame
poll => "once", # only poll once after define (or after a set)
},

"h40020" => { # String32; Device model
name => "Md", # internal name of this register in the hardware doc
reading => "Device_model", # name of the reading for this value
len => 16, # number of Registers this value spans
unpack => "A*", # defines the translation between data in the module and in the communication frame
poll => "once", # only poll once after define (or after a set)
},

"h40036" => { # String16; SW version of datamanager
name => "SN", # internal name of this register in the hardware doc
reading => "SerialNumber",                              # name of the reading for this value
len => 16, # number of Registers this value spans
unpack => "a*", # defines the translation between data in the module and in the communication frame
poll => "once", # only poll once after define (or after a set)
},

"h40052" => { # String32; Serialnumber of the inverter
name => "Firm", # internal name of this register in the hardware doc
reading => "Firmware_Rel", # name of the reading for this value
len => 16, # number of Registers this value spans
unpack => "A*", # defines the translation between data in the module and in the communication frame
poll => "once", # only poll once after define (or after a set)
},

"h40068" => { # int32; Photovoltaik Power
name => "PV", # internal name of this register in the hardware doc
reading => "PV_Power_Watt", # name of the reading for this value
len => 2, # number of Registers this value spans
unpack => "l", # defines the translation between data in the module and in the communication frame
polldelay => "x2", # only poll this Value if last read is older than 2*Iteration, otherwiese

},
"h40070" => { # int32; Batterie Leistung in Watt
name => "Batt", # internal name of this register in the hardware doc
reading => "Batt_Power_Watt", # name of the reading for this value
len => 2, # number of Registers this value spans
unpack => "l", # defines the translation between data in the module and in the communication frame
polldelay => "x2", # only poll this Value if last read is older than 2*Iteration, otherwiese

},
"h40072" => { # int32; Haus Verbrauch in Watt
name => "Home", # internal name of this register in the hardware doc
reading => "HomeLoad_Watt", # name of the reading for this value
len => 2, # number of Registers this value spans
unpack => "l", # defines the translation between data in the module and in the communication frame
polldelay => "x2", # only poll this Value if last read is older than 2*Iteration, otherwiese

},
"h40082" => { # uint16; SOC
name => "BattSOC", # internal name of this register in the hardware doc
reading => "BattSOC", # name of the reading for this value
len => 1, # number of Registers this value spans
unpack => "s<", # defines the translation between data in the module and in the communication frame
polldelay => "x2", # only poll this Value if last read is older than 2*Iteration, otherwiese

}

# Ende parseInfo
);

#####################################
sub E3DC_Modbus_S10_Initialize($) {
    my ($modHash) = @_;

require "$attr{global}{modpath}/FHEM/98_Modbus.pm";

$modHash->{parseInfo}  = \%E3DC_parseInfo; # defines registers, inputs, coils etc. for this Modbus Defive

$modHash->{deviceInfo} = \%E3DC_deviceInfo; # defines properties of the device like
# defaults and supported function codes

ModbusLD_Initialize($modHash); # Generic function of the Modbus module does the rest

$modHash->{AttrList} = $modHash->{AttrList} . " " . # Standard Attributes like IODEv etc
$modHash->{ObjAttrList} . " " . # Attributes to add or overwrite parseInfo definitions
$modHash->{DevAttrList} . " " . # Attributes to add or overwrite devInfo definitions
"poll-.* " . # overwrite poll with poll-ReadingName
"polldelay-.* "; # overwrite polldelay with polldelay-ReadingName
}

sub MinMaxChk($$$;$$) { # prüft, ob ein Wert außerhalb Min/Max ist
my $Zahl = $_[0]; # Übergabe des zu prüfenden Wertes
my $Min = $_[1]; # Übergabe Minimum
my $Max = $_[2]; # Übergabe Maximum
;
my $name = $_[3]; # optional: Übergabe Name vom Device
my $Reading = $_[4]; # optional: Übergabe Name Reading (für Auslesen letzten Wert)

if (defined $Reading) { # wenn Name Reading  übergeben wurde
if ($Zahl < $Min) { # Zahl ist kleiner als Minimum
$Zahl = ReadingsNum($name,$Reading,$Min); # Zahl auf letzten Wert setzen
}
if ($Zahl > $Max) { # Zahl ist größer als Maximum
$Zahl = ReadingsNum($name,$Reading,$Max); # Zahl auf letzten Wert setzen
}
} else { # wenn Name Reading  nicht übergeben wurde
if ($Zahl < $Min) { # Zahl ist kleiner als Minimum
$Zahl = $Min; # Zahl auf Minimum setzen
}
if ($Zahl > $Max) { # Zahl ist größer als Maximum
$Zahl = $Max; # Zahl auf Maximum setzen
}
}
return $Zahl;
}
1;


JimKnopf

#3
Hi Juanma!
Ich komme mit Deinem Deutsch zuper zurecht  ;).

Bei mir läuft auch ein S10 und ich nutze FHEM um die Register zu lesen.
Um die Batterieleistung zu lesen definiere ich das MODBUS_Register und setzte ein UserReading:
Leistung { if (Value("BatterieLeistung")<32768 ) {Value("BatterieLeistung")} else {Value("BatterieLeistung")-65536};; }

Genau so mache ich es mit allen anderen Registern.
Für Notstrom lautet das UserReading:
STATE {
if (ReadingsVal($name,"state",0)  == 0) {return "nicht_unterstüzt"}
elsif (ReadingsVal($name,"state",0)  == 1) {return "Notstrom_aktiv"}
elsif (ReadingsVal($name,"state",0)  == 2) {return "Notstrom_inaktiv"}
elsif (ReadingsVal($name,"state",0)  == 3) {return "Notstrom_nv"}
elsif (ReadingsVal($name,"state",0)  == 4) {return "Notstrom_keine_Grundstellung"}
else {return "unbekannt"}
}

Vielleicht konnte ich Dir helfen.
Gruß, Burkhard
PS: ich habe von Pearl null Ahnung
FHEM,LaCrosse,PCA301,Revolt,MAX!,HM,FS20, MQTT2, ebusd 3.4.v3.4-96-g96d5623, ebus Adapter 3.0 mit 20201219-offset , Wolf  CGB (-K)-20, Wolf ISM7, Wolf Solar SM, Speicher/WR E3DC S10, eGolf, Keba P30, Phoenix Contact EV, OpenWB

NMozer

Hallo Jim Knopf,

hast du mir einen Tipp wo ich an Infos zur Umsetzung der Modbus TCP Verbindung mit dem S10 komme?
Ich habe bisher mit dem FHEM noch keine solche Datenverbindungen aufgebaut.
Welches ist eine gute Lecktüre um in der Thematik einen durchblick zu bekommen?


Danke mal, habe mich wieder etwas weiter bewegt in der Welt von Perl und FHEM.
Konnte das Modul integrieren.
Angezeigt bekomme ich nun die gleichen falschen Werte wie zuvor bei den Einzelabrufen mit dem Modbus Modul attrModbus.
Wie genau Formatierst du denn die Werte deines E3DCs?

Gruß Nico

blofield

Hallo juanmax,


ich versuche das auch gerade zu lösen, bin aber noch nicht 100%ig erfolgreich.
Das Register 40072 kann ich aber OK auslesen, wenn ich bei unpack "N" verwende.
Die Register 40068, 40070 und 40074 bekomme ich auch nicht zum laufen :-/

@JimKnopf
Wie sehen denn Deine attr obj-h400* in Deinem device aus, bei Dir scheint es ja korrekt zu laufen, wenn ich Dich richtig verstanden habe.

Gruß
blofield

juanmax

Zitat von: blofield am 30 August 2017, 19:02:44
Hallo juanmax,


ich versuche das auch gerade zu lösen, bin aber noch nicht 100%ig erfolgreich.
Das Register 40072 kann ich aber OK auslesen, wenn ich bei unpack "N" verwende.

Gruß
blofield

Hi blofield, schön das jemand andere auch dieselbe Probleme hat. Ich bin momentan in Urlaub und kann ich es nicht ausprobiert, ich tue  (unpack N)aber wenn ich wieder da bin. Danke!

Gruß aus dem Bodensee

blofield

Hallo Juanmax,

ich habe es jetzt raus ;)
Ich habe es mit ModbusAttr umgesetzt und meinen S10 so definiert:


define S10 ModbusAttr 1 20 192.168.1.1:502 TCP
attr S10 userattr event-min-interval event-on-change-reading obj-h40066-len obj-h40066-poll obj-h40066-reading obj-h40066-unpack obj-h40068-len obj-h40068-poll obj-h40068-reading obj-h40068-unpack obj-h40070-len obj-h40070-poll obj-h40070-reading obj-h40070-unpack obj-h40072-len obj-h40072-max obj-h40072-min obj-h40072-poll obj-h40072-reading obj-h40072-unpack obj-h40074-len obj-h40074-poll obj-h40074-reading obj-h40074-unpack obj-h40080-len obj-h40080-poll obj-h40080-reading obj-h40080-unpack obj-h40081-len obj-h40081-poll obj-h40081-reading obj-h40081-unpack obj-h40082-len obj-h40082-poll obj-h40082-reading obj-h40082-unpack obj-h40083-len obj-h40083-poll obj-h40083-reading obj-h40083-unpack userReadings
attr S10 event-min-interval .*:3600
attr S10 event-on-change-reading .*
attr S10 obj-h40066-len 2
attr S10 obj-h40066-poll 1
attr S10 obj-h40066-reading sunwatt
attr S10 obj-h40066-unpack N
attr S10 obj-h40068-len 2
attr S10 obj-h40068-poll 1
attr S10 obj-h40068-reading battwatt0
attr S10 obj-h40068-unpack N
attr S10 obj-h40070-len 2
attr S10 obj-h40070-poll 1
attr S10 obj-h40070-reading homewatt0
attr S10 obj-h40070-unpack N
attr S10 obj-h40072-len 2
attr S10 obj-h40072-max 65537
attr S10 obj-h40072-min 0
attr S10 obj-h40072-poll 1
attr S10 obj-h40072-reading gridwatt0
attr S10 obj-h40072-unpack N
attr S10 obj-h40082-len 1
attr S10 obj-h40082-poll 1
attr S10 obj-h40082-reading battsoc
attr S10 obj-h40082-unpack n
attr S10 room ServerRaum
attr S10 userReadings gridwatt { if (ReadingsVal("S10", "gridwatt0", "") <= 32768 ) {(ReadingsVal("S10", "gridwatt0", ""))} else {(ReadingsVal("S10", "gridwatt0", "")) - 65536 };; },\
battwatt { if (ReadingsVal("S10", "battwatt0", "") <= 32768 ) {(ReadingsVal("S10", "battwatt0", ""))} else {(ReadingsVal("S10", "battwatt0", "")) - 65536 };; },\
homewatt { if (ReadingsVal("S10", "homewatt0", "") <= 4294901759 ) {(ReadingsVal("S10", "homewatt0", ""))} else {(ReadingsVal("S10", "homewatt0", "")) - 4294967295 + 65536 };; }


Das interessante ist, dass alle Readings im Register jeweils -2 zur E3DC Dokumentation sind. Willst Du also das Register h40070 auslesen, dann musst Du h40068 angeben! Unpacking für Int32 ist dabei "N".
Beim Battsoc mit Uint16 ist das Register h40082 auszulesen und folgerichtig unpacking "n" zu verwenden.
Die userReadings verwende ich dann gem. dem Hinweis von Jim Knopf für Werte, die positive und negative Werte liefern können (Netzbezug-/einspeisung, Batterie(ent)ladung).

So stimmt jetzt bei mir alles mit dem Display am S10 überein.
Im Webportal werden die Werte noch irgendwie geglättet, daher erscheinen dort manchmal andere Werte, insbesondere bei schnellem Wechsel zw. Netzbezug-/einspeisung.

Damit solltest Du Dein Modul schnell einsetzen können. ;)

blofield

Mirko_2013

Hallo blofield,

Kannst Du mir sagen welche Einstellungen Du in der S10 noch vorgenommen hast?

Ich habe versucht analog deiner Variante die Verbindung zur S10 aufzubauen.
Allerdings habe ich ständige Verbindungsabrüche:
Im Eventlog habe ich folgende Meldung:
2017-09-04 22:12:30 ModbusAttr S10 DISCONNECTED
2017-09-04 22:12:30 ModbusAttr S10 CONNECTED
2017-09-04 22:12:30 ModbusAttr S10 DISCONNECTED
2017-09-04 22:12:30 ModbusAttr S10 CONNECTED

Im Logfile:
2017.09.04 22:15:27 3: x.x.x.x:502 reappeared (S10)
2017.09.04 22:15:27 3: x.x.x.x:502 disconnected, waiting to reappear (S10)
2017.09.04 22:15:27 4: HttpUtils url=http://x.x.x.x:502/
2017.09.04 22:15:27 3: x.x.x.x:502 reappeared (S10)
2017.09.04 22:15:27 3: x.x.x.x:502 disconnected, waiting to reappear (S10)

Danke
Mirko
HP Microserver Gen8; fhem-5.8; CUL868 - V1.66; CUL868 - V1.61; CUL433 - V1.61; CUNX - V2.67; eBus Koppler USB

blofield

Hallo Mirko,

Am S10 muss Modbus zunächst aktiviert werden. Über das Hauptmenü kannst Du in das Menü "Funktionen" ( ich glaube so heißt es, bin leider gerade nicht zu Hause ) und dort gibt es ein Menü "Modbus". Da habe ich alle Buttons angeschaltet ( sind dann grün hinterlegt ) und die Modbus ID festgelegt ( Default: 1 ), das ist im define die 1 direkt nach ModbusAttr.
Wenn man will kann man da auch den Port ändern.

Wenn Du das hast sollte es sofort gehen, denn eine Authentifizierung braucht es nicht.

Viel Erfolg
blofield

Gesendet von meinem ONEPLUS A5000 mit Tapatalk


Mirko_2013

Hallo blofield,

Ja das habe ich bereits aktiviert, auch versucht  mit den 2 verschiedenen Modbus Protokollen die Verbindung aufzubauen. Leider kein Erfolg.
Auch in den Logfile finde ich keine weiteren Meldungen.

Gruß Mirko
HP Microserver Gen8; fhem-5.8; CUL868 - V1.66; CUL868 - V1.61; CUL433 - V1.61; CUNX - V2.67; eBus Koppler USB

Mirko_2013

Hallo zusammen,

Ich habe jetzt soweit rausgefunden, das der ständige Verbindungsaufbau damit zusammen hängt, das die S10 und der Shem Server in verschiedenen Netzen steht.
Könnt Ihr mir hier einen Tip geben, ohne das ich alles die 2 Geräte in ein Netz stellen muss.

Danke
Mirko
HP Microserver Gen8; fhem-5.8; CUL868 - V1.66; CUL868 - V1.61; CUL433 - V1.61; CUNX - V2.67; eBus Koppler USB

blofield

Zitat von: Mirko_2013 am 05 September 2017, 22:25:39
Hallo zusammen,

Ich habe jetzt soweit rausgefunden, das der ständige Verbindungsaufbau damit zusammen hängt, das die S10 und der Shem Server in verschiedenen Netzen steht.
Könnt Ihr mir hier einen Tip geben, ohne das ich alles die 2 Geräte in ein Netz stellen muss.

Danke
Mirko
Hallo Mirko,

Das sollte aber kein Problem sein, sonst würde das Internet ja auch nicht funktionieren ;)
Da scheint bei Dir das Routing nicht einwandfrei zu klappen oder Du hast bei einer Firewall vergessen Regeln zu definieren. Es ist also offenbar ein Netzwerk Problem.
Ohne zu wissen, wir das bei Dir aufgebaut ist, kann man da pauschal aber keine Hilfestellung leisten. Mach doch mal einen anderen Thread auf und beschreibe dein Netz.

blofield

Gesendet von meinem ONEPLUS A5000 mit Tapatalk


Mirko_2013

Hallo zusammen,

Problem ist gelöst, der fhem server und die e3dc Anlage muss im selben Netz stehen.
Ist scheinbar ein sicherheitsfeature das sie nur auf Anfragen am Modus nur aus dem gleichen Netz antworten.

Gruß
Mirko
HP Microserver Gen8; fhem-5.8; CUL868 - V1.66; CUL868 - V1.61; CUL433 - V1.61; CUNX - V2.67; eBus Koppler USB

Baeda

Hallo Blofield,

ich bin zufällig auf den Beitrag gestossen, da ich bei den Readings auch keine (oder falsche) Werte bekam - allerdings habe ich den Wert in der E3DC Doku genommen, bzw. diesen um -1 angepasst - somit war Dein Tipp, den Wert der Doku um "-2" zu korregieren die Lösung. Vielen Dank dafür!

Ich kann nun schöne Graphen generieren, jedoch fällt mir dabei auf, dass ich hin und wieder beim "Hausverbrauch" (homewatt) negative Werte bekomme - was ja nicht sein kann.
Wie kommst Du auf die Zahl 4294900000? (es ist ja nicht ganz 2 hoch 32...)


...
homewatt { if (ReadingsVal("S10", "homewatt0", "") < 4294900000 ) {(ReadingsVal("S10", "homewatt0", ""))} else {((ReadingsVal("S10", "battwatt", "")) * -1 ) + (ReadingsVal("S10", "sunwatt", "")) + (ReadingsVal("S10", "gridwatt", ""))};; }


Es kommt selten vor, dass ich negative Werte erhalte und ich könnte mir vorstellen, dass hier ein Peak im Stromverlauf stattfindet, der zu einem kurzfristigen Zahlenüberlauf führt.

Vielen Dank vorab,
VG,
Baeda