Hallo Stefan,
ich versuche mal wieder meine Pluggit Lüftungsanlage direkt mit FHEM über das Modbus Modul zu verbinden. Da die Daten der Pluggit AP300 nicht dem Modbus standard entsprechen und beispielweise 4 Temperaturen in 2 Registern gespeichert sind, würde ich gerne selber Readings über die "ModbusReadingsFN" Funktion erzeugen. Als Vorlage habe ich das ModbusElsnerWS von Klaus Scheuer verwendet, aktuell sieht das Modul so aus:
# $Id: 98_ModbusPluggitAP300.pm 24745 2022-10-02 16:40:26Z Andreas H $
# This modules handles the communication with a Pluggit AP190/300/400.
package main;
use strict;
use warnings;
use Time::HiRes qw( time );
use HttpUtils;
# 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.
#
sub ModbusPluggit_Eval($$$);
sub ModbusPluggit_Initialize($);
my %ModbusPluggit_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 holding registers.
write => 16, # use function code 6 to write holding registers (alternative could be 16)
defLen => 1, # default length (number of registers) per value (e.g. 2 for a float of 4 bytes that spans 2 registers)
# can be overwritten in parseInfo per reading by specifying the key "len"
combine => 10, # allow combined read of up to 10 adjacent registers during getUpdate
defUnpack => "f>", # default pack / unpack code to convert raw values, e.g. "n" for a 16 bit integer oder
# "f>" for a big endian float IEEE 754 floating-point numbers
# can be overwritten in parseInfo per reading by specifying the key "unpack"
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
# 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
my %ModbusPluggit_ParseInfo = (
'h1032' => {reading => 'Temperaturen',
name => 'Temperaturen',
expr => 'sprintf("%d %d %d %d",
$val[0],
$val[1],
$val[2],
$val[3])',
unpack => 'CCCC',
len => 2,
poll => 1
}
);
#####################################
sub ModbusPluggit_Initialize($)
{
my ($modHash) = @_;
require "$attr{global}{modpath}/FHEM/98_Modbus.pm";
$modHash->{parseInfo} = \%ModbusPluggit_ParseInfo; # defines registers, inputs, coils etc. for this Modbus Defive
$modHash->{deviceInfo} = \%ModbusPluggit_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->{ModbusReadingsFn} = "ModbusPluggit_Eval"; # to be called before a reading is set
$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 ModbusPluggit_Eval($$$) {
my ($modHash, $readingName, $readingVal) = @_;
my $name = $modHash->{NAME};
my $ctrl = 1;
my ($Frischluft, $Zuluft, $Abluft, $Fortluft) = split(' ', $readingVal);
readingsSingleUpdate($modHash, "Frischluft", $Frischluft, 1);
readingsSingleUpdate($modHash, "Zuluft", $Zuluft, 1);
readingsSingleUpdate($modHash, "Abluft", $Abluft, 1);
readingsSingleUpdate($modHash, "Fortluft", $Fortluft, 1);
return $ctrl;
}
1;
=pod
=begin html
<a name="ModbusPluggit"></a>
<h3>ModbusPluggit</h3>
<ul>
ModbusPluggit uses the low level Modbus module to provide a way to communicate with SDM221M smart electrical meter from B+G E-Tech & EASTON.
It defines the modbus input and holding registers and reads them in a defined interval.
<br>
<b>Prerequisites</b>
<ul>
<li>
This module requires the basic Modbus module which itsef requires Device::SerialPort or Win32::SerialPort module.
</li>
</ul>
<br>
<a name="ModbusPluggitDefine"></a>
<b>Define</b>
<ul>
<code>define <name> ModbusPluggit <Id> <Interval></code>
<br><br>
The module connects to the smart electrical meter with Modbus Id <Id> through an already defined modbus device and actively requests data from the
smart electrical meter every <Interval> seconds <br>
<br>
Example:<br>
<br>
<ul><code>define SDM221M ModbusPluggit 1 60</code></ul>
</ul>
<br>
<a name="ModbusPluggitConfiguration"></a>
<b>Configuration of the module</b><br><br>
<ul>
apart from the modbus id and the interval which both are specified in the define command there is nothing that needs to be defined.
However there are some attributes that can optionally be used to modify the behavior of the module. <br><br>
The attributes that control which messages are sent / which data is requested every <Interval> seconds are:
<pre>
poll-Energy_total__kWh
poll-Energy_import__kWh
</pre>
if the attribute is set to 1, the corresponding data is requested every <Interval> seconds. If it is set to 0, then the data is not requested.
by default the temperatures are requested if no attributes are set.
<br><br>
Example:
<pre>
define SDM221M ModbusPluggit 1 60
attr SDM221M poll-Energy_total__kWh 0
</pre>
</ul>
<a name="ModbusPluggit"></a>
<b>Set-Commands</b><br>
<ul>
The following set options are available:
<pre>
</pre>
</ul>
<br>
<a name="ModbusPluggitGet"></a>
<b>Get-Commands</b><br>
<ul>
All readings are also available as Get commands. Internally a Get command triggers the corresponding
request to the device and then interprets the data and returns the right field value. To avoid huge option lists in FHEMWEB, only the most important Get options
are visible in FHEMWEB. However this can easily be changed since all the readings and protocol messages are internally defined in the modue in a data structure
and to make a Reading visible as Get option only a little option (e.g. <code>showget => 1</code> has to be added to this data structure
</ul>
<br>
<a name="ModbusPluggitattr"></a>
<b>Attributes</b><br><br>
<ul>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
<br>
<li><b>poll-Energy_total__kWh</b></li>
<li><b>poll-Energy_import__kWh</b></li>
include a read request for the corresponding registers when sending requests every interval seconds <br>
<li><b>timeout</b></li>
set the timeout for reads, defaults to 2 seconds <br>
<li><b>minSendDelay</b></li>
minimal delay between two requests sent to this device
<li><b>minCommDelay</b></li>
minimal delay between requests or receptions to/from this device
</ul>
<br>
</ul>
=end html
=cut
Anscheinend wird die Subroutine in meinem Modul "98_ModbusPluggit.pm" nicht aufgerufen, ich bekomme immer wieder die Meldung "Undefined subroutine &Modbus::ModbusPluggit_Eval". Hier mal der Auszug aus dem Logfile:
2022.10.03 13:18:09 5: AP300: CreateDataObjects unpacked 10151511 with CCCC to 16, 21, 21, 17
2022.10.03 13:18:09 5: AP300: perl expression eval evaluated package main; my @val = @{$oRef->{'%val'}};sprintf("%d %d %d %d",
$val[0],
$val[1],
$val[2],
$val[3]) to 16 21 21 17
2022.10.03 13:18:09 5: AP300: CreateDataObjects is calling ModbusReadingsFn via TryCall for reading Temperaturen and val 16 21 21 17
2022.10.03 13:18:09 3: AP300: CreateDataObjects error calling ModbusReadingsFn: Undefined subroutine &Modbus::ModbusPluggit_Eval called at ./FHEM/98_Modbus.pm line 4995.
2022.10.03 13:18:09 4: AP300: CreateDataObjects assigns value 16 21 21 17 to Temperaturen
2022.10.03 13:18:09 5: AP300: ParseDataString created 1 readings
2022.10.03 13:18:09 4: AP300: HandleResponse done, current frame / read buffer: 0103041015151121ab, id 1, fCode 3,
request: id 1, read fc 3 h1032, len 2, master device AP300, reading Temperaturen (getUpdate for Temperaturen len 2), queued 0.12 secs ago, sent 0.11 secs ago,
response: id 1, fc 3, h1032, len 2, values 10151511
2022.10.03 13:18:09 5: AP300: ResetExpect for HandleResponse from response to idle
2022.10.03 13:18:09 5: AP300: DropFrame called from ReadFn - drop 0103041015151121ab
Zum testen habeich die subroutine "ModbusPluggit_Eval" in das Modbus Modul "98_Modbus.pm" kopiert, dann werden die 4 einzelnen Readings erzeugt und das reading "Temperaturen" wird nicht mehr aktualisiert, also so wie es eigentlich sein sollte.
Momentan komme ich mit meinen beschränkten perl Fähigkeiten nicht weiter, muss ich die subroutine noch irgendwie global bekannt machen?
Bin für jeden Tip dankbar.
Gruß Andreas