98_Modbus.pm: Bugfixes und neue Features

git-svn-id: https://svn.fhem.de/fhem/trunk@15034 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
StefanStrobel 2017-09-09 12:01:43 +00:00
parent 682baf5753
commit 96fc9b7051
2 changed files with 248 additions and 34 deletions

View File

@ -105,15 +105,24 @@
# 2017-04-21 optimize call to _send in GetUpdate, new attribute nonPrioritizedSet # 2017-04-21 optimize call to _send in GetUpdate, new attribute nonPrioritizedSet
# remove unused variables for devInfo / parseInfo in ParseObj # remove unused variables for devInfo / parseInfo in ParseObj
# 2017-05-08 better warning handler restore (see $oldSig) # 2017-05-08 better warning handler restore (see $oldSig)
# # 2017-07-15 new attribute sortUpdate to sort the requests by their object address
# new attribute brokenFC5 for misbehaving devices that don't understand the normal ff00 to set a coil to 1
# set this attr e.g. to 0100 if the device wants 0100 instead of ff00
# 2017-07-18 started implementing data types (3.6.0)
# 2017-07-25 set saveAsModule
# 2017-08-17 nicer logging of timeouts
# #
# #
# ToDo / Ideas : # ToDo / Ideas :
# get reading key (type / adr)
# filterEcho (wie in private post im Forum vorgeschlagen)
# set saveAsModule to save attr definitions as module
# define data types VT_R4 -> revregs, len2, unpack f> ...
# async output for scan? table? with revregs etc.? # async output for scan? table? with revregs etc.?
# get object-interpretations h123 -> Alle Variationen mit revregs und bswap und unpacks ... # get object-interpretations h123 -> Alle Variationen mit revregs und bswap und unpacks ...
# nonblocking disable attr für xp # nonblocking disable attr für xp
# #
# attr with a lits of set commands / requests to launch when polling (Helios support) # attr with a lits of set commands / requests to launch when polling (Helios support)
# #
# passive listening to other modbus traffic (state machine, parse requests of others in special queue # passive listening to other modbus traffic (state machine, parse requests of others in special queue
# #
@ -148,6 +157,7 @@ sub Modbus_ParseFrames($);
sub Modbus_HandleSendQueue($;$); sub Modbus_HandleSendQueue($;$);
sub Modbus_TimeoutSend($); sub Modbus_TimeoutSend($);
sub Modbus_CRC($); sub Modbus_CRC($);
sub ModbusLD_ObjInfo($$$;$$);
# functions to be used from logical modules # functions to be used from logical modules
sub ModbusLD_ExpandParseInfo($); sub ModbusLD_ExpandParseInfo($);
@ -161,7 +171,7 @@ sub ModbusLD_GetUpdate($);
sub ModbusLD_GetIOHash($); sub ModbusLD_GetIOHash($);
sub ModbusLD_Send($$$;$$$); sub ModbusLD_Send($$$;$$$);
my $Modbus_Version = '3.5.25 - 8.5.2017'; my $Modbus_Version = '3.7.0 - 20.8.2017';
my $Modbus_PhysAttrs = my $Modbus_PhysAttrs =
"queueDelay " . "queueDelay " .
"busDelay " . "busDelay " .
@ -181,6 +191,7 @@ my $Modbus_LogAttrs =
"alignTime " . "alignTime " .
"enableControlSet:0,1 " . "enableControlSet:0,1 " .
"nonPrioritizedSet:0,1 " . "nonPrioritizedSet:0,1 " .
"sortUpdate:0,1 " .
"scanDelay "; "scanDelay ";
my $Modbus_CommonAttrs = my $Modbus_CommonAttrs =
@ -402,6 +413,19 @@ sub ModbusLD_ObjInfo($$$;$$) {
return $parseInfo->{$key}{$oName} return $parseInfo->{$key}{$oName}
if (defined($parseInfo->{$key}) && defined($parseInfo->{$key}{$oName})); if (defined($parseInfo->{$key}) && defined($parseInfo->{$key}{$oName}));
# check for type entry / attr ...
if ($oName ne "type") {
my $dType = ModbusLD_ObjInfo($hash, $key, 'type', 'noDefaultDevAttrForType', '***NoTypeInfo***');
if ($dType ne '***NoTypeInfo***') {
#Log3 $name, 5, "$name: ObjInfo for $key and $oName found type $dType";
my $typeSpec = ModbusLD_DevInfo($hash, "type-$dType", $oName, '***NoTypeInfo***');
if ($typeSpec ne '***NoTypeInfo***') {
#Log3 $name, 5, "$name: $dType specifies $typeSpec for $oName";
return $typeSpec;
}
}
}
# default for object type in deviceInfo / in attributes for device / type # default for object type in deviceInfo / in attributes for device / type
if ($defName) { if ($defName) {
my $type = substr($key, 0, 1); my $type = substr($key, 0, 1);
@ -532,8 +556,9 @@ sub Modbus_ParseObj($$$;$$) {
Log3 $name, 3, "$name: ScanIds got reply from Id $1 - set internal MODBUSID to $1"; Log3 $name, 3, "$name: ScanIds got reply from Id $1 - set internal MODBUSID to $1";
} elsif ($op eq 'scanobj') { # scan Modbus objects } elsif ($op eq 'scanobj') { # scan Modbus objects
if (!$reading) { if (!$reading) {
$reading = "scan-$key"; my $fKey = $type . sprintf ("%06d", $startAdr);
CommandAttr(undef, "$name obj-${key}-reading $reading"); $reading = "scan-$fKey";
CommandAttr(undef, "$name obj-${fKey}-reading $reading");
} }
if ($type =~ "[hi]") { if ($type =~ "[hi]") {
my $l = length($rest) / 2; my $l = length($rest) / 2;
@ -879,13 +904,19 @@ sub Modbus_ParseFrames($)
} elsif ($fCode == 3 || $fCode == 4) { # read holding/input registers, pdu: bytes, registers } elsif ($fCode == 3 || $fCode == 4) { # read holding/input registers, pdu: bytes, registers
($headerLen, $values) = unpack ('Ca*', $data); ($headerLen, $values) = unpack ('Ca*', $data);
if (ModbusLD_DevInfo($logHash, "h", "brokenFC3", 0)) { if (ModbusLD_DevInfo($logHash, "h", "brokenFC3", 0)) {
Log3 $name, 5, "$name: ParseFrames uses fix for broken fcode 3";
($parseAdr, $values) = unpack ('na*', $data); ($parseAdr, $values) = unpack ('na*', $data);
$headerLen = 4; $headerLen = 4;
} }
$actualLen = length ($values); $actualLen = length ($values);
} elsif ($fCode == 5) { # write single coil, pdu: adr, coil (FF00) } elsif ($fCode == 5) { # write single coil, pdu: adr, coil (FF00)
($parseAdr, $values) = unpack ('nH4', $data); ($parseAdr, $values) = unpack ('nH4', $data);
$values = ($values eq "ff00" ? 1 : 0); if (ModbusLD_DevInfo($logHash, "c", "brokenFC5", 0)) {
Log3 $name, 5, "$name: ParseFrames uses fix for broken fcode 5";
$values = ($values eq "0000" ? 0 : 1);
} else {
$values = ($values eq "ff00" ? 1 : 0);
}
$quantity = 1; $quantity = 1;
# length of $data should be 4 # length of $data should be 4
} elsif ($fCode == 6) { # write single (holding) register, pdu: adr, register } elsif ($fCode == 6) { # write single (holding) register, pdu: adr, register
@ -904,7 +935,7 @@ sub Modbus_ParseFrames($)
my $hexdata = unpack ("H2", $data); my $hexdata = unpack ("H2", $data);
my $hexFCode = unpack ("H*", pack("C", $fCode)); my $hexFCode = unpack ("H*", pack("C", $fCode));
my $errCode = $Modbus_errCodes{$hexdata}; my $errCode = $Modbus_errCodes{$hexdata};
Log3 $name, 5, "$name: ParseFrames got error code $hexFCode / $hexdata" . Log3 $name, 4, "$name: ParseFrames got error code $hexFCode / $hexdata" .
($errCode ? ", $errCode" : ""); ($errCode ? ", $errCode" : "");
return "device replied with exception code $hexFCode / $hexdata" . ($errCode ? ", $errCode" : ""); return "device replied with exception code $hexFCode / $hexdata" . ($errCode ? ", $errCode" : "");
} else { } else {
@ -1129,8 +1160,8 @@ sub Modbus_TimeoutSend($)
my $logLvl = AttrVal($name, "timeoutLogLevel", 3); my $logLvl = AttrVal($name, "timeoutLogLevel", 3);
Log3 $name, $logLvl, "$name: timeout waiting for fc $ioHash->{REQUEST}{FCODE} " . Log3 $name, $logLvl, "$name: timeout waiting for fc $ioHash->{REQUEST}{FCODE} " .
"from id $ioHash->{REQUEST}{MODBUSID}, " . "from id $ioHash->{REQUEST}{MODBUSID}, " .
"($ioHash->{REQUEST}{TYPE}$ioHash->{REQUEST}{ADR} / $ioHash->{REQUEST}{READING}), " .
"Request was $ioHash->{REQUESTHEX}" . "Request was $ioHash->{REQUESTHEX}" .
" ($ioHash->{REQUEST}{TYPE}$ioHash->{REQUEST}{ADR} / $ioHash->{REQUEST}{READING}, len $ioHash->{REQUEST}{LEN})" .
($ioHash->{helper}{buffer} ? ", Buffer contains " . unpack ("H*", $ioHash->{helper}{buffer}) : ""); ($ioHash->{helper}{buffer} ? ", Buffer contains " . unpack ("H*", $ioHash->{helper}{buffer}) : "");
Modbus_Statistics($ioHash, "Timeouts", 1); Modbus_Statistics($ioHash, "Timeouts", 1);
@ -1277,7 +1308,12 @@ sub Modbus_HandleSendQueue($;$)
} elsif ($fCode == 3 || $fCode == 4) { # read holding/input registers, pdu: StartAdr, Len (=number of regs) } elsif ($fCode == 3 || $fCode == 4) { # read holding/input registers, pdu: StartAdr, Len (=number of regs)
$data = pack ('nn', $adr, $len); $data = pack ('nn', $adr, $len);
} elsif ($fCode == 5) { # write single coil, pdu: StartAdr, Value (1-bit as FF00) } elsif ($fCode == 5) { # write single coil, pdu: StartAdr, Value (1-bit as FF00)
$data = pack ('nH4', $adr, (unpack ('n',$v1) ? "FF00" : "0000")); if (ModbusLD_DevInfo($logHash, "c", "brokenFC5", 0)) {
my $oneCode = lc ModbusLD_DevInfo($logHash, "c", "brokenFC5");
$data = pack ('nH4', $adr, (unpack ('n',$v1) ? $oneCode : "0000"));
} else {
$data = pack ('nH4', $adr, (unpack ('n',$v1) ? "FF00" : "0000"));
}
} elsif ($fCode == 6) { # write single register, pdu: StartAdr, Value } elsif ($fCode == 6) { # write single register, pdu: StartAdr, Value
$data = pack ('n', $adr) . $v1; $data = pack ('n', $adr) . $v1;
} elsif ($fCode == 15) { # write multiple coils, pdu: StartAdr, NumOfCoils, ByteCount, Values } elsif ($fCode == 15) { # write multiple coils, pdu: StartAdr, NumOfCoils, ByteCount, Values
@ -1391,6 +1427,7 @@ sub ModbusLD_Initialize($ )
"obj-[cdih][0-9]+-expr " . "obj-[cdih][0-9]+-expr " .
"obj-[cdih][0-9]+-ignoreExpr " . "obj-[cdih][0-9]+-ignoreExpr " .
"obj-[cdih][0-9]+-format " . "obj-[cdih][0-9]+-format " .
"obj-[ih][0-9]+-type " .
"obj-[cdih][0-9]+-showGet " . "obj-[cdih][0-9]+-showGet " .
"obj-[cdih][0-9]+-poll " . "obj-[cdih][0-9]+-poll " .
"obj-[cdih][0-9]+-polldelay "; "obj-[cdih][0-9]+-polldelay ";
@ -1415,6 +1452,17 @@ sub ModbusLD_Initialize($ )
"dev-([cdih]-)*defShowGet " . "dev-([cdih]-)*defShowGet " .
"dev-([cdih]-)*defPoll " . "dev-([cdih]-)*defPoll " .
"dev-h-brokenFC3 " . "dev-h-brokenFC3 " .
"dev-c-brokenFC5 " .
"dev-type-[A-Za-z0-9_]+-unpack " .
"dev-type-[A-Za-z0-9_]+-len " .
"dev-type-[A-Za-z0-9_]+-encode " .
"dev-type-[A-Za-z0-9_]+-decode " .
"dev-type-[A-Za-z0-9_]+-revRegs " .
"dev-type-[A-Za-z0-9_]+-bswapRegs " .
"dev-type-[A-Za-z0-9_]+-format " .
"dev-type-[A-Za-z0-9_]+-expr " .
"dev-type-[A-Za-z0-9_]+-map " .
"dev-timing-timeout " . "dev-timing-timeout " .
"dev-timing-sendDelay " . "dev-timing-sendDelay " .
@ -1818,6 +1866,21 @@ sub ModbusLD_Get($@)
} }
sub Modbus_compObjAttrs ($$) {
my ($a, $b) = @_;
my $aType = substr($a, 4, 1);
my $aStart = substr($a, 5);
my $bType = substr($b, 4, 1);
my $bStart = substr($b, 5);
my $result = ($aType cmp $bType);
if ($result) {
return $result;
}
$result = $aStart <=> $bStart;
return $result;
}
# #
# SET command - handle predifined control sets # SET command - handle predifined control sets
################################################ ################################################
@ -1922,7 +1985,90 @@ sub ModbusLD_ControlSet($$$)
RemoveInternalTimer ("scan:$name"); RemoveInternalTimer ("scan:$name");
InternalTimer($now+$scanDelay, "ModbusLD_ScanObjects", "scan:$name", 0); InternalTimer($now+$scanDelay, "ModbusLD_ScanObjects", "scan:$name", 0);
return "0"; return "0";
} elsif ($setName eq 'saveAsModule') {
my $fName = $setVal;
my $out;
my $last = "x";
if (!open($out, ">", "/tmp/98_ModbusGen$fName.pm")) {
Log3 $name, 3, "$name: Cannot create output file $hash->{OUTPUT}";
return;
};
print $out "
##############################################
# \$Id: 98_ModbusGen${fName}.pm \$
# von ModbusAttr generiertes Modul
package main;
use strict;
use warnings;
";
print $out "sub ModbusGen${fName}_Initialize(\$);\n";
print $out "my %ModbusGen${fName}parseInfo = (\n";
foreach my $a (sort keys %{$attr{$name}}) {
if ($a =~ /^obj-([^\-]+)-(.*)$/) {
if ($1 ne $last) {
if ($last ne "x") {
# Abschluss des letzten Eintrags
printf $out "%26s", "},\n";
}
# Neuer Key
printf $out "%2s", " ";
printf $out "%16s%s", "\"$1\"", " => { ";
$last = $1;
} else {
printf $out "%25s", " ";
}
printf $out "%15s%s", "\'".$2."\'", " => \'$attr{$name}{$a}\',\n";
}
}
printf $out "%28s", "}\n";
print $out ");\n\n";
print $out "my %ModbusGen${fName}deviceInfo = (\n";
$last = "x";
foreach my $a (sort keys %{$attr{$name}}) {
if ($a =~ /^dev-((type-)?[^\-]+)-(.*)$/) {
if ($1 ne $last) {
if ($last ne "x") {
printf $out "%26s", "},\n";
}
printf $out "%2s", " ";
printf $out "%16s%s", "\"$1\"", " => { ";
$last = $1;
} else {
printf $out "%25s", " ";
}
printf $out "%15s%s", "\'".$3."\'", " => \'$attr{$name}{$a}\',\n";
}
}
printf $out "%28s", "}\n";
print $out ");\n\n";
print $out "
#####################################
sub ModbusGen${fName}_Initialize(\$)
{
my (\$modHash) = \@_;
require \"\$attr{global}{modpath}/FHEM/98_Modbus.pm\";
\$modHash->{parseInfo} = \\%ModbusGen${fName}parseInfo; # defines registers, inputs, coils etc. for this Modbus Defive
\$modHash->{deviceInfo} = \\%ModbusGen${fName}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
}
";
return "0";
} }
return undef; # no control set identified - continue with other sets return undef; # no control set identified - continue with other sets
} }
@ -1950,7 +2096,7 @@ sub ModbusLD_ScanObjects($) {
Log3 $name, 5, "$name: ScanObjects waits until queue gets smaller"; Log3 $name, 5, "$name: ScanObjects waits until queue gets smaller";
return; return;
} }
if ($hash->{scanOAdr}) { if ($hash->{scanOAdr} || $hash->{scanOAdr} eq "0") {
if ($hash->{scanOAdr} < $hash->{scanOEnd}) { if ($hash->{scanOAdr} < $hash->{scanOEnd}) {
$hash->{scanOAdr}++; $hash->{scanOAdr}++;
} else { } else {
@ -2046,8 +2192,16 @@ sub ModbusLD_ScanFormat($$)
", i>=" . unpack("s>", $val) . ", i>=" . unpack("s>", $val) .
", I=" . unpack("S", $val) . ", I=" . unpack("S", $val) .
", I>=" . unpack("S>", $val); ", I>=" . unpack("S>", $val);
$ret .= ", f=" . unpack("f", $val) . $ret .= ", f=" . unpack("f", $val) .
", f>=" . unpack("f>", $val); ", f>=" . unpack("f>", $val);
#my $r1 = substr($h, 0, 4);
#my $r2 = substr($h, 4, 4);
#my $rev = pack ("H*", $r2 . $r1);
#$ret .= ", revf=" . unpack("f", $rev) .
#", revf>=" . unpack("f>", $rev);
} }
return $ret; return $ret;
} }
@ -2302,6 +2456,20 @@ sub ModbusLD_ReadAnswer($;$)
} }
sub Modbus_compObjKeys ($$) {
my ($a, $b) = @_;
my $aType = substr($a, 0, 1);
my $aStart = substr($a, 1);
my $bType = substr($b, 0, 1);
my $bStart = substr($b, 1);
my $result = ($aType cmp $bType);
if ($result) {
return $result;
}
$result = $aStart <=> $bStart;
return $result;
}
##################################### #####################################
# called via internal timer from # called via internal timer from
# logical device module with # logical device module with
@ -2347,6 +2515,7 @@ sub ModbusLD_GetUpdate($) {
push @ObjList, keys (%{$parseInfo}); push @ObjList, keys (%{$parseInfo});
Log3 $name, 5, "$name: GetUpdate full object list: " . join (" ", sort @ObjList); Log3 $name, 5, "$name: GetUpdate full object list: " . join (" ", sort @ObjList);
# create readList by checking delays and poll settings for ObjList
foreach my $objCombi (sort @ObjList) { foreach my $objCombi (sort @ObjList) {
#my $type = substr($objCombi, 0, 1); #my $type = substr($objCombi, 0, 1);
#my $adr = substr($objCombi, 1); #my $adr = substr($objCombi, 1);
@ -2380,8 +2549,8 @@ sub ModbusLD_GetUpdate($) {
my $maxLen; my $maxLen;
$adr = 0; $type = ""; $span = 0; $nextSpan = 0; $adr = 0; $type = ""; $span = 0; $nextSpan = 0;
# combine objects in Readlist by increasing the length of a first obejct and removing the second # combine objects in Readlist by increasing the length of a first object and removing the second
foreach $nextObj (sort keys %readList) { foreach $nextObj (sort Modbus_compObjKeys keys %readList) {
$nextType = substr($nextObj, 0, 1); $nextType = substr($nextObj, 0, 1);
$nextAdr = substr($nextObj, 1); $nextAdr = substr($nextObj, 1);
$nextReading = ModbusLD_ObjInfo($hash, $nextObj, "reading"); $nextReading = ModbusLD_ObjInfo($hash, $nextObj, "reading");
@ -2406,14 +2575,20 @@ sub ModbusLD_GetUpdate($) {
$maxLen = ModbusLD_DevInfo($hash, $type, "combine", 1); $maxLen = ModbusLD_DevInfo($hash, $type, "combine", 1);
# Log3 $name, 5, "$name: GetUpdate: combine for $type is $maxLen"; # Log3 $name, 5, "$name: GetUpdate: combine for $type is $maxLen";
} }
Modbus_Profiler($ioHash, "Idle");
while (my ($objCombi, $span) = each %readList) { if (AttrVal($name, "sortUpdate", 0)) {
# helios: Log3 $name, 5, "$name: sort objList before sending requests";
# val force len foreach my $objCombi (sort Modbus_compObjKeys keys %readList) {
#ModbusLD_Send($hash, h100, "write", $adr, 0, $span); my $span = $readList{$objCombi};
#ModbusLD_Send($hash, h100, "read", 0, 0, 20); ModbusLD_Send($hash, $objCombi, "read", 0, 0, $span);
ModbusLD_Send($hash, $objCombi, "read", 0, 0, $span); }
} else {
Log3 $name, 5, "$name: don't sort objList before sending requests";
while (my ($objCombi, $span) = each %readList) {
ModbusLD_Send($hash, $objCombi, "read", 0, 0, $span);
}
} }
Modbus_Profiler($ioHash, "Idle");
return; return;
} }
@ -2494,7 +2669,7 @@ sub Modbus_SwpRegs($$$) {
##################################### #####################################
# called from logical device fuctions # called from logical device functions
# with log dev hash # with log dev hash
sub ModbusLD_Send($$$;$$$){ sub ModbusLD_Send($$$;$$$){
my ($hash, $objCombi, $op, $v1, $force, $reqLen) = @_; my ($hash, $objCombi, $op, $v1, $force, $reqLen) = @_;

View File

@ -35,15 +35,16 @@
# 2016-12-18 documentation added # 2016-12-18 documentation added
# 2016-12-24 documentation added # 2016-12-24 documentation added
# 2017-01-02 allowShortResponses documented # 2017-01-02 allowShortResponses documented
# 2017-01-25 documentation for ignoreExpr # 2017-01-25 documentation for ignoreExpr
# 2017-03-12 fixed documentation for logical attrs that were wrongly defined as physical ones # 2017-03-12 fixed documentation for logical attrs that were wrongly defined as physical ones
# 2017-07-15 added documentation for new attributes
# 2017-07-25 documentation for data type attributes
# #
package main; package main;
use strict; use strict;
use warnings; use warnings;
##################################### #####################################
sub sub
ModbusAttr_Initialize($) ModbusAttr_Initialize($)
@ -148,12 +149,12 @@ ModbusAttr_Initialize($)
of the data object. <br> of the data object. <br>
Modbus devices offer the following types of data objects: Modbus devices offer the following types of data objects:
<ul> <ul>
<li> holding registers (16 bit objects that can be read and written)</li> <li> holding registers (16 bit objects that can be read and written)</li>
<li> input registers (16 bit objects that can only be read)</li> <li> input registers (16 bit objects that can only be read)</li>
<li> coils (single bit objects that can be read and written)</li> <li> coils (single bit objects that can be read and written)</li>
<li> discrete inputs (single bit objects that can only be read)</li> <li> discrete inputs (single bit objects that can only be read)</li>
</ul> </ul>
<br> <br>
The module uses the first character of these data object types to define attributes. The module uses the first character of these data object types to define attributes.
@ -212,6 +213,11 @@ ModbusAttr_Initialize($)
<code>scanId-5-Response-h770 hex=0064, string=.d, s=25600, s>=100, S=25600, S>=100</code> <code>scanId-5-Response-h770 hex=0064, string=.d, s=25600, s>=100, S=25600, S>=100</code>
<li><code>scanStop</code></li> <li><code>scanStop</code></li>
stops any running scans. stops any running scans.
<li><code>saveAsModule &lt;name&gt;</code></li>
experimental: saves the definitions of obj- and dev- attributes in a new fhem module file as /tmp/98_ModbusGen&lt;name&gt;.pm.<br>
if this file is copied into the fhem module subdirectory (e.g. /opt/fhem/FHEM) and fhem is restarted then instead of defining a device
as ModbusAttr with all the attributes to define objects, you can just define a device of the new type ModbusGen&lt;name&gt; and all the
objects will be there by default. However all definitions can still be changed / overriden with the attribues defined in ModbusAttr if needed.
</ul> </ul>
</ul> </ul>
<br> <br>
@ -261,7 +267,7 @@ ModbusAttr_Initialize($)
<br> <br>
<li><b>obj-[cdih][1-9][0-9]*-expr</b></li> <li><b>obj-[cdih][1-9][0-9]*-expr</b></li>
defines a perl expression that converts the raw value read from the device. defines a perl expression that converts the raw value read from the device.
<br> <br>
<li><b>obj-[cdih][1-9][0-9]*-ignoreExpr</b></li> <li><b>obj-[cdih][1-9][0-9]*-ignoreExpr</b></li>
defines a perl expression that returns 1 if a value should be ignored and the existing reading should not be modified defines a perl expression that returns 1 if a value should be ignored and the existing reading should not be modified
<br> <br>
@ -309,6 +315,21 @@ ModbusAttr_Initialize($)
defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading. defines an encoding to be used in a call to the perl function encode to convert the raw data string read from the device to a reading.
This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8. This can be used if the device delivers strings in an encoding like cp850 and after decoding it you want to reencode it to e.g. utf8.
<br> <br>
<li><b>obj-[ih][1-9][0-9]*-type</b></li>
defines that this object has a user defined data type. Data types can be defined using the dev-type- attribues.<br>
If a device with many objects uses for example floating point values that span two swapped registers with the unpack code f>, then instead of specifying the -unpack, -revRegs, -len, -format and other attributes over and over again, you could define a data type with attributes that start with dev-type-VT_R4- and then
use this definition for each object as e.g. obj-h1234-type VT_R4<br>
example:<br>
<pre>
attr WP dev-type-VT_R4-format %.1f
attr WP dev-type-VT_R4-len 2
attr WP dev-type-VT_R4-revRegs 1
attr WP dev-type-VT_R4-unpack f>
attr WP obj-h1234-reading Temp_Ist
attr WP obj-h1234-type VT_R4
</pre>
<br>
<li><b>obj-[cdih][1-9][0-9]*-showGet</b></li> <li><b>obj-[cdih][1-9][0-9]*-showGet</b></li>
every reading can also be requested by a get command. However these get commands are not automatically offered in fhemweb. every reading can also be requested by a get command. However these get commands are not automatically offered in fhemweb.
@ -347,7 +368,7 @@ ModbusAttr_Initialize($)
<br> <br>
<li><b>dev-([cdih]-)*defExpr</b></li> <li><b>dev-([cdih]-)*defExpr</b></li>
defines a default Perl expression to use for this object type to convert raw values read. defines a default Perl expression to use for this object type to convert raw values read.
<br> <br>
<li><b>dev-([cdih]-)*defIgnoreExpr</b></li> <li><b>dev-([cdih]-)*defIgnoreExpr</b></li>
defines a default Perl expression to decide when values should be ignored. defines a default Perl expression to decide when values should be ignored.
<br> <br>
@ -374,9 +395,22 @@ ModbusAttr_Initialize($)
<li><b>dev-([cdih]-)*defShowGet</b></li> <li><b>dev-([cdih]-)*defShowGet</b></li>
if set to 1 then all objects of this type will have a visible get by default. if set to 1 then all objects of this type will have a visible get by default.
<br> <br>
<li><b>dev-type-XYZ-unpack, -len, -encode, -decode, -revRegs, -bswapRegs, -format, -expr, -map</b></li>
define the unpack code, length and other details of a user defined data type. XYZ has to be replaced with the name of a user defined data type.
use obj-h123-type XYZ to assign this type to an object.
<br>
<li><b>dev-([cdih]-)*allowShortResponses</b></li> <li><b>dev-([cdih]-)*allowShortResponses</b></li>
if set to 1 the module will accept a response with valid checksum but data lengh < lengh in header if set to 1 the module will accept a response with valid checksum but data lengh < lengh in header
<br> <br>
<li><b>dev-h-brokenFC3</b></li>
if set to 1 the module will change the parsing of function code 3 and 4 responses for devices that
send the register address instead of the length in the response
<br>
<li><b>dev-c-brokenFC5</b></li>
if set the module will use the hex value specified here instead of ff00 as value 1 for setting coils
<br>
<li><b>dev-timing-timeout</b></li> <li><b>dev-timing-timeout</b></li>
timeout for the device (defaults to 2 seconds) timeout for the device (defaults to 2 seconds)
<br> <br>
@ -388,7 +422,7 @@ ModbusAttr_Initialize($)
<br> <br>
<li><b>queueMax</b></li> <li><b>queueMax</b></li>
max length of the send queue, defaults to 100 max length of the send queue, defaults to 100
<br> <br>
<li><b>nextOpenDelay</b></li> <li><b>nextOpenDelay</b></li>
delay for Modbus-TCP connections. This defines how long the module should wait after a failed TCP connection attempt before the next reconnection attempt. This defaults to 60 seconds. delay for Modbus-TCP connections. This defines how long the module should wait after a failed TCP connection attempt before the next reconnection attempt. This defaults to 60 seconds.
<li><b>openTimeout</b></li> <li><b>openTimeout</b></li>
@ -399,6 +433,11 @@ ModbusAttr_Initialize($)
if set to 1, then it will set the loglevel for "disconnected" and "reappeared" messages to 4 instead of 3 if set to 1, then it will set the loglevel for "disconnected" and "reappeared" messages to 4 instead of 3
<li><b>maxTimeoutsToReconnect</b></li> <li><b>maxTimeoutsToReconnect</b></li>
this attribute is only valid for TCP connected devices. In such cases a disconnected device might stay undetected and lead to timeouts until the TCP connection is reopened. This attribute specifies after how many timeouts an automatic reconnect is tried. this attribute is only valid for TCP connected devices. In such cases a disconnected device might stay undetected and lead to timeouts until the TCP connection is reopened. This attribute specifies after how many timeouts an automatic reconnect is tried.
<li><b>nonPrioritizedSet</b></li>
if set to 1, then set commands will not be sent on the bus before other queued requests and the response will not be waited for.
<li><b>sortUpdate</b></li>
if set to 1, the requests during a getUpdate cycle will be sorted before queued.
<li><b>disable</b></li> <li><b>disable</b></li>
stop communication with the device while this attribute is set to 1. For Modbus over TCP this also closes the TCP connection. stop communication with the device while this attribute is set to 1. For Modbus over TCP this also closes the TCP connection.
<br> <br>