2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-04 05:16:45 +00:00

initial release

git-svn-id: https://svn.fhem.de/fhem/trunk@5480 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
klauswitt 2014-04-07 20:04:22 +00:00
parent 58afd7a46c
commit e54384987a
2 changed files with 1096 additions and 0 deletions

645
fhem/FHEM/00_NetzerI2C.pm Normal file
View File

@ -0,0 +1,645 @@
##############################################
# $Id: 00_Netzer_I2C.pm klausw
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
sub NetzerI2C_Attr(@);
sub NetzerI2C_HandleQueue($);
sub NetzerI2C_Read($);
#sub NetzerI2C_Ready($);
sub NetzerI2C_Write($$);
#my $clientsI2C = ":I2C_PCF8574:I2C_PCA9532:I2C_BMP180:FHT.*:";
#my %matchListI2C = (
# "1:I2C_PCF8574"=> ".*",
# "2:FHT" => "^81..(04|09|0d)..(0909a001|83098301|c409c401)..",
#);
my @clients = qw(
I2C_LCD
I2C_DS1307
I2C_PC.*
I2C_MCP23017
I2C_BMP180
I2C_SHT21
);
sub NetzerI2C_Initialize($) {
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
# Provider
$hash->{Clients} = join (':',@clients);
$hash->{ReadFn} = "NetzerI2C_Read"; #wird von der globalen loop aufgerufen (ueber $hash->{FD} gefunden), wenn Daten verfuegbar sind
$hash->{WriteFn} = "NetzerI2C_Write"; #wird vom client per IOWrite($@) aufgerufen
$hash->{ReadyFn} = "NetzerI2C_Ready";
$hash->{I2CWrtFn} = "NetzerI2C_Write"; #zum testen als alternative fuer IOWrite
# Normal devices
$hash->{DefFn} = "NetzerI2C_Define";
$hash->{UndefFn} = "NetzerI2C_Undef";
$hash->{GetFn} = "NetzerI2C_Get";
$hash->{SetFn} = "NetzerI2C_Set";
$hash->{NotifyFn}= "NetzerI2C_Notify";
$hash->{AttrFn} = "NetzerI2C_Attr";
$hash->{AttrList}= "do_not_notify:1,0 dummy:1,0 " .
"timeout socat:1,0";
}
#####################################
sub NetzerI2C_Define($$) { #
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
unless(@a == 3) {
my $msg = "wrong syntax: define <name> NetzerI2C {none | hostname:port}";
Log3 undef, 2, $msg;
return $msg;
}
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
#$hash->{Clients} = $clientsI2C;
#$hash->{MatchList} = \%matchListI2C;
if($dev eq "none") {
Log3 $name, 1, "$name device is none, commands will be echoed only";
$attr{$name}{dummy} = 1;
return undef;
}
$hash->{DeviceName} = $dev;
#my $ret = DevIo_OpenDev($hash, 0, "CUL_DoInit");
my $ret = DevIo_OpenDev($hash, 0, "");
return $ret;
}
#####################################
sub NetzerI2C_Notify { #
my ($hash,$dev) = @_;
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
NetzerI2C_forall_clients($hash,\&NetzerI2C_Init_Client,undef);;
} elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
}
}
#####################################
sub NetzerI2C_forall_clients($$$) { #
my ($hash,$fn,$args) = @_;
foreach my $d ( sort keys %main::defs ) {
if ( defined( $main::defs{$d} )
&& defined( $main::defs{$d}{IODev} )
&& $main::defs{$d}{IODev} == $hash ) {
&$fn($main::defs{$d},$args);
}
}
return undef;
}
#####################################
sub NetzerI2C_Init_Client($@) { #
my ($hash,$args) = @_;
if (!defined $args and defined $hash->{DEF}) {
my @a = split("[ \t][ \t]*", $hash->{DEF});
$args = \@a;
}
my $name = $hash->{NAME};
Log3 $name,1,"im init client fuer $name ";
my $ret = CallFn($name,"InitFn",$hash,$args);
if ($ret) {
Log3 $name,2,"error initializing '".$hash->{NAME}."': ".$ret;
}
}
#####################################
sub NetzerI2C_Undef($$) { #
my ($hash, $arg) = @_;
my $name = $hash->{NAME};
foreach my $d (sort keys %defs) {
if(defined($defs{$d}) &&
defined($defs{$d}{IODev}) &&
$defs{$d}{IODev} == $hash)
{
Log3 $name, 3, "deleting port for $d";
delete $defs{$d}{IODev};
}
}
DevIo_CloseDev($hash);
return undef;
}
#####################################
sub NetzerI2C_Set($@) { #
my ($hash, @a) = @_;
my $name = shift @a;
my $type = shift @a;
my $arg = join(" ", @a);
my @sets = ('writeByte', 'writeByteReg', 'writeBlock');
if($type eq "writeByte") {
return "Usage: set $name i2csend <i2caddress> [<register address>] <data> [...<data>]"
if(!$arg || $arg !~ /^[0-7][0-9A-F](\W[0-9A-F][0-9A-F]){0,64}$/xi);
foreach (@a) {
$_ = hex;
}
my $i2caddr = shift @a;
my %sendpackage = ( i2caddress => $i2caddr, direction => "i2cwrite", data => join(" ", @a), direct=>1 );
Log3 $hash, 1, "$sendpackage{data}";
NetzerI2C_Write($hash, \%sendpackage);
} elsif($type eq "writeByteReg") {
return "Usage: set $name writeByteReg <i2caddress> <register address> <data> [...<data>]"
if(!$arg || $arg !~ /^[0-7][0-9A-F](\W[0-9A-F][0-9A-F]){0,64}$/xi);
foreach (@a) {
$_ = hex;
}
my $i2caddr = shift @a;
my $reg = shift @a;
my %sendpackage = ( i2caddress => $i2caddr, direction => "i2cwrite", reg => $reg, data => join(" ", @a), direct=>1 );
NetzerI2C_Write($hash, \%sendpackage);
} elsif($type eq "writeBlock") {
return "Usage: set $name writeBlock <i2caddress> <register address> <data> [...<data>]"
if(!$arg || $arg !~ /^[0-7][0-9A-F](\W[0-9A-F][0-9A-F]){0,64}$/xi);
foreach (@a) {
$_ = hex;
}
my $i2caddr = shift @a;
my $reg = shift @a;
my %sendpackage = ( i2caddress => $i2caddr, direction => "i2cblockwrite", reg => $reg, data => join(" ", @a), direct=>1 );
NetzerI2C_Write($hash, \%sendpackage);
} else {
return "Unknown argument $type, choose one of " . join(" ", @sets);
}
return undef;
}
#####################################
sub NetzerI2C_Get($@) { #
my ($hash, @a) = @_;
my $nargs = int(@a);
my $name = $hash->{NAME};
my @gets = ('read');
unless ( exists($a[1]) && $a[1] ne "?" && grep {/^$a[1]$/} @gets ) {
return "Unknown argument $a[1], choose one of " . join(" ", @gets);
}
my ($msg, $err);
return "No $a[1] for dummies" if(IsDummy($name));
if ($a[1] eq "read") {
return "use: \"get $name $a[1] <i2cAddress> [<RegisterAddress> [<Number od bytes to get>]]\"" if(@a < 3);
return "$name I2C address must be 2-digit hexvalues" unless ($a[2] =~ /^(0x|)(|[0-7])[0-9A-F]$/xi); # && hex($a[2]) % 2 == 0);
return "$name register address must be a hexvalues" if (defined($a[3]) && $a[3] !~ /^(0x|)[0-9A-F]{1,2}$/xi);
return "$name number of bytes must be decimal value" if (defined($a[4]) && $a[4] !~ /^[0-9]{1,2}$/ && $a[4] < 65);
my $hmsg = chr( (hex( $a[2] ) << 1) + 1 ); #I2C Adresse (read) in Zeichen wandeln
if ( $a[3] ) { #Registeradresse in Hexwerte wandeln
$hmsg .= chr( hex("5C") ) if ( (hex($a[3])) == "00"); #wenn 0x00 gesendet mit 0x5C escapen
$hmsg .= chr( hex($a[3]) );
}
if ( $a[4] ) {
for(my $n=1; $n<$a[4]; $n++) { #Fuer jedes zu lesende Byte ein Byte rausschicken
$hmsg .= chr( hex("01") );
}
}
$hmsg .= chr( hex("00") ); #Endezeichen anhaengen
#nur zum testen mit socat#######################
$hmsg =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg if ( AttrVal($hash->{NAME}, 'socat', 0) == 1 );
################################################
DevIo_SimpleWrite($hash, $hmsg, undef);
my $buf = undef;
my $timeout = 10;
return $hash->{NAME} . " disconnected" unless $hash->{FD};
for(;;) { #Werte direkt lesen (mit Timeout)
my $rin = "";
vec($rin, $hash->{FD}, 1) = 1;
my $nfound = select($rin, undef, undef, $timeout);
last if($nfound <= 0);
my $r = DevIo_DoSimpleRead($hash);
if(!defined($r) || $r ne "") {
$buf = $r;
last;
}
}
if ($buf) {
if ( AttrVal($hash->{NAME}, 'socat', 0) == 0 ) {
$buf =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; #empfangene Zeichen in Hexwerte wandeln (fuer Socat auskommentieren)
} else {
chomp($buf); #weg nach testen mit Socat
$buf = uc($buf); #weg nach testen mit Socat
}
my @abuf = split (/ /,$buf);
for (my $i = 1; $i < (defined($a[3])? 3 : 2 ) ; $i++) { #pruefen, ob jedes gesendete Byte ein positives Ack bekommen hat
return "error, no Ack received for $a[$1]; received: $buf" if $abuf[0] ne "FF";
shift(@abuf);
}
my $rmsg = undef;
my $nrec = int(@abuf);
for (my $j = 0; $j < $nrec ; $j++) { #escape Zeichen fuer 0x00 entfernen
$rmsg .= " " if (defined($rmsg));
$rmsg .= $abuf[$j] unless( $abuf[$j] eq "5C" && defined($abuf[$j + 1]) && $abuf[$j + 1] eq "00" );
}
$buf = $rmsg;
} else {
$buf = "no Message received";
}
return $buf;
}
#$hash->{READINGS}{$a[1]}{VAL} = $msg;
#$hash->{READINGS}{$a[1]}{TIME} = TimeNow();
#return "$a[0] $a[1] => $msg";
return undef;
}
#####################################
sub NetzerI2C_DoInit($) { #ausfuehren beim start von devio evtl. loeschen oder reinit von clienten reinbauen
my $hash = shift;
my $name = $hash->{NAME};
# Reset the counter
delete($hash->{XMIT_TIME});
delete($hash->{NR_CMD_LAST_H});
return undef;
}
#####################################
sub NetzerI2C_Write($$) { #wird vom Client aufgerufen
my ($hash, $clientmsg) = @_;
foreach my $av (keys %{$clientmsg}) { Log3 $hash, 5, "$hash->{NAME} vom Clienten: $av= " . $clientmsg->{$av}; }
if ($clientmsg->{direction} && $clientmsg->{i2caddress}) {
if(!$hash->{QQUEUE} || 0 == scalar(@{$hash->{QQUEUE}})) {
$hash->{QQUEUE} = [ $clientmsg ];
NetzerI2C_SendFromQueue($hash, $clientmsg);
} else {
push(@{$hash->{QQUEUE}}, $clientmsg);
}
}
return undef;
}
#####################################
sub NetzerI2C_SendFromQueue($$) { #
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my (@msg,@adata) = ();
@adata = split(/ /,$clientmsg->{data}) if defined($clientmsg->{data});
if (defined($clientmsg->{reg}) && ($clientmsg->{direction} eq "i2cwrite" && int(@adata) > 1)
|| ($clientmsg->{nbyte} && $clientmsg->{nbyte} > 1)) { #klaeren, ob Register sequentiell geschrieben werden
$clientmsg->{smsg} = ( $clientmsg->{direction} eq "i2cwrite" ? int(@adata) : $clientmsg->{nbyte} ) if !$clientmsg->{smsg};
$clientmsg->{smsgcnt}++;
push(@msg, $clientmsg->{reg} + $clientmsg->{smsgcnt} - 1 ) if ($clientmsg->{reg}); #Registeradresse hochzaehlen wenn vorhanden
push(@msg, $adata[$clientmsg->{smsgcnt} - 1]) if ($clientmsg->{direction} eq "i2cwrite");
Log3 $hash, 5, $clientmsg->{direction} . " Nachricht zerteilen: ". ( defined($clientmsg->{data}) ? $clientmsg->{data} : "leer" ) ." Teil Nr: " .$clientmsg->{smsgcnt} ." = ". $clientmsg->{smsg};
} else { #oder alle auf einmal
Log3 $hash, 5, $clientmsg->{direction} . " Nachricht nicht zerteilen: ". ( defined($clientmsg->{data}) ? $clientmsg->{data} : "leer" ) ." Nbytes: " . int(@adata);
push(@msg, $clientmsg->{reg} ) if defined($clientmsg->{reg});
push(@msg, @adata);
}
my $hmsg = chr( ( $clientmsg->{i2caddress} << 1 ) + (($clientmsg->{direction} eq "i2cread")? 1 : 0) );
if ( int(@msg) > 0 ) {
foreach (@msg) { #Daten in Zeichen wandeln
$hmsg .= chr( hex("5C") ) if ( $_ == hex("00") ); #wenn 0x00 gesendet mit 0x5C escapen
$hmsg .= chr( $_ );
}
}
$hmsg .= chr( hex("00") ); #Endezeichen anhaengen
#nur zum Testen########
$clientmsg->{bytecount} = int(@msg) + 1; #Anzahl Nutzdaten + Adressbyte
(my $smsg = $hmsg) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg;
Log3 $hash, 5, "$name SendFromQueue: $clientmsg->{direction}, String: $smsg, Hex: $hmsg, NBytes: $clientmsg->{bytecount}";
#######################
#DevIo_SimpleWrite($hash, $hmsg, undef);
DevIo_SimpleWrite($hash, AttrVal($hash->{NAME}, 'socat', 0) == 1 ? $smsg : $hmsg, undef); #fuer Socat zum testen
NetzerI2C_InternalTimer("RecvTimeout", gettimeofday() + AttrVal($hash->{NAME}, 'timeout', 10), "NetzerI2C_TransceiveTimeout", $hash, 0);
}
#####################################
sub NetzerI2C_HandleQueue($) { #
my $hash = shift;
my $arr = $hash->{QQUEUE};
if(defined($arr) && @{$arr} > 0) {
shift(@{$arr}) unless $arr->[0]->{smsg} && $arr->[0]->{smsg} > $arr->[0]->{smsgcnt}; #nur auf naechste Botschaft wechseln wenn alle Byte gesendet wurden
if(@{$arr} == 0) {
delete($hash->{QQUEUE});
return;
}
my $clientmsg = $arr->[0];
if(defined($clientmsg) && $clientmsg eq "") {
NetzerI2C_HandleQueue($hash) if defined($hash);
} else {
NetzerI2C_SendFromQueue($hash, $clientmsg);
}
}
}
#####################################
sub NetzerI2C_TransceiveTimeout($) {#
#my $hash = shift;
#Hash finden wenn myinternaltimer genutzt wird#
my ($myHash) = @_; #
my $hash = $myHash->{HASH}; #
###############################################
my $name = $hash->{NAME};
Log3 $hash, 1, "$name: Timeout I2C response";
my $arr = $hash->{QQUEUE};
delete $arr->[0]->{smsg} if $arr->[0]->{smsg};
NetzerI2C_HandleQueue($hash);
}
#####################################
sub NetzerI2C_Read($) { # called from the global loop, when the select for hash->{FD} reports data
my ($hash) = @_;
my $buf = DevIo_SimpleRead($hash);
return undef if(!defined($buf)); #Aendern????
#Log3 $hash, 1, "$hash->{NAME} vom I2C empfangen 1: $buf";
#hier noch abfangen, wenn $buf leer ist
if ( AttrVal($hash->{NAME}, 'socat', 0) == 1 ) { #weg nach testen mit Socat
chomp($buf);
#$buf = hex($buf);
} else {
$buf =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg #empfangene Zeichen in Hexwerte wandeln -> in wandlung nach Zahl aendern
}
Log3 $hash, 5, "$hash->{NAME} vom I2C empfangen: $buf";
my @abuf = split (/ /,$buf);
foreach (@abuf) { #weg wenn Zeichen direkt gewandelt werden
$_ = hex;
#Log3 $hash, 1, "$hash->{NAME} vom I2C: $_";
}
my $name = $hash->{NAME};
#Log3 $hash, 1, "$hash->{NAME} vom I2C empfangen: $buf";
my $arr = $hash->{QQUEUE};
if(defined($arr) && @{$arr} > 0) {
my $clientmsg = $arr->[0];
NetzerI2C_RemoveInternalTimer("RecvTimeout", $hash);
my $status = "Ok";
for (my $i = 0; $i < $clientmsg->{bytecount} ; $i++) { #pruefen, ob jedes gesendete Byte ein positives Ack (FF) bekommen hat
$status = "error" . ($arr->[0]->{smsg} ? "@ reg: ". sprintf("%.2X ",($clientmsg->{reg} + $clientmsg->{smsgcnt} - 1)) :"") if !defined($abuf[0]) || $abuf[0] != 255;
shift(@abuf);
}
my $rmsg = undef;
my $nrec = int(@abuf);
for (my $i = 0; $i < $nrec ; $i++) { #escape Zeichen (0x5C) fuer 0x00 entfernen
$rmsg .= " " if (defined($rmsg));
#$rmsg .= $abuf[$i] unless( $abuf[$i] eq "5C" && defined($abuf[$i + 1]) && $abuf[$i + 1] eq "00" );
$rmsg .= $abuf[$i] unless( $abuf[$i] == 92 && defined($abuf[$i + 1]) && $abuf[$i + 1] == 0 );
}
if ( $arr->[0]->{smsg} && defined($rmsg) ) { #wenn Nachricht Teil einer Nachrichtenfolge, dann Daten anhaengen
$clientmsg->{received} .= ( defined($arr->[0]->{smsg}) && $arr->[0]->{smsg} == 1 ? "" : " ") . $rmsg;
} else {
$clientmsg->{received} = $rmsg;
}
unless ( $arr->[0]->{smsg} && $arr->[0]->{smsg} > $arr->[0]->{smsgcnt} && $status eq "Ok" ) { #erst senden, wenn Transfer abgeschlossen oder bei Fehler
delete $arr->[0]->{smsg} if $arr->[0]->{smsg} && $status ne "Ok"; #aktuellen Einzeltransfer durch loeschen der Botschaftszahl abbrechen
#$clientmsg->{received} = $rmsg if defined($rmsg);
$clientmsg->{$name . "_" . "RAWMSG"} = $buf;
$clientmsg->{$name . "_" . "SENDSTAT"} = $status;
if ($clientmsg->{direct}) { #Vorgang wurde von diesem Modul ausgeloest
$hash->{direct_send} = $clientmsg->{data};
$hash->{direct_answer} = $clientmsg->{$name . "_" . "RAWMSG"};
$hash->{direct_I2Caddr} = $clientmsg->{i2caddress};
$hash->{direct_SENDSTAT} = $status;
}
########################################### neue Variante zum senden an client
foreach my $d ( sort keys %main::defs ) { #zur Botschaft passenden Clienten ermitteln geht auf Client: I2CRecFn
#Log3 $hash, 1, "Clients suchen d: $d". ($main::defs{$d}{IODev}? ", IODev: $main::defs{$d}{IODev}":"") . ($main::defs{$d}{I2C_Address} ? ", I2C: $main::defs{$d}{I2C_Address}":"") . ($clientmsg->{i2caddress} ? " CI2C: $clientmsg->{i2caddress}" : "");
if ( defined( $main::defs{$d} )
&& defined( $main::defs{$d}{IODev} ) && $main::defs{$d}{IODev} == $hash
&& defined( $main::defs{$d}{I2C_Address} ) && defined( $clientmsg->{i2caddress} )
&& $main::defs{$d}{I2C_Address} eq $clientmsg->{i2caddress} ) {
my $chash = $main::defs{$d};
Log3 $hash, 5, "Client gefunden d: $d". ($main::defs{$d}{IODev}? ", IODev: $main::defs{$d}{IODev}":"") . ($main::defs{$d}{I2C_Address} ? ", I2C: $main::defs{$d}{I2C_Address}":"") . ($clientmsg->{i2caddress} ? " CI2C: $clientmsg->{i2caddress}" : "");
CallFn($d, "I2CRecFn", $chash, $clientmsg);
}
}
######################################## alte Variante ueber Dispatch ######################
# my $dir = $clientmsg->{direction}; #
# my $sid = $clientmsg->{id}; #
# if($dir eq "i2cread" || $dir eq "i2cwrite") { #
# my $dev = $clientmsg->{i2caddress}; #
# my %addvals = (RAWMSG => $buf, SENDSTAT => $status); #
# $rmsg = ( defined($rmsg) ? ($sid . " " . $dev . " " . $rmsg) : ($sid . " " . $dev) ); #
# Log 1, "wird an Client geschickt: $rmsg"; #
# Dispatch($hash, $rmsg, \%addvals); #
# } #
###########################################################################################
undef $clientmsg; #Hash loeschen nachdem Daten verteilt wurden
}
NetzerI2C_HandleQueue($hash);
} else {
Log3 $hash, 1, "$name: unknown data received: $buf";
}
}
#####################################
sub NetzerI2C_Ready($) {############# kann geloescht werden?
my ($hash) = @_;
return DevIo_OpenDev($hash, 1, "")
if($hash->{STATE} eq "disconnected");
# This is relevant for windows/USB only
my $po = $hash->{USBDev};
my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
if($po) {
($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
}
return ($InBytes && $InBytes>0);
}
#####################################
sub NetzerI2C_Attr(@) { #
my ($cmd,$name,$aName,$aVal) = @_;
my $msg = undef;
if($aName eq "timeout") {
if ( defined($aVal) ) {
unless ( looks_like_number($aVal) && $aVal >= 0.1 && $aVal <= 20 ) {
$msg = "$name: Wrong $aName defined. Value must be a number between 0.1 and 20";
}
}
}
return $msg;
}
#####################################
sub NetzerI2C_InternalTimer($$$$$) {#(von Dietmar63)
my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
my $mHash;
if ($modifier eq "") {
$mHash = $hash;
} else {
my $timerName = "$hash->{NAME}_$modifier";
if (exists ($hash->{TIMER}{$timerName})) {
$mHash = $hash->{TIMER}{$timerName};
} else {
$mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier};
$hash->{TIMER}{$timerName} = $mHash;
}
}
InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
}
#####################################
sub NetzerI2C_RemoveInternalTimer($$) {
my ($modifier, $hash) = @_;
my $timerName = "$hash->{NAME}_$modifier";
if ($modifier eq "") {
RemoveInternalTimer($hash);
} else {
my $myHash = $hash->{TIMER}{$timerName};
if (defined($myHash)) {
delete $hash->{TIMER}{$timerName};
RemoveInternalTimer($myHash);
}
}
}
1;
=pod
=begin html
<a name="NetzerI2C"></a>
<h3>NetzerI2C</h3>
<ul>
<a name="NetzerI2C"></a>
Provides access to <a href="http://www.mobacon.de/wiki/doku.php/en/netzer/index">Netzer's</a> I2C interfaces for some logical modules and also directly.<br><br>
<b>preliminary:</b><br>
Serial Server of Netzer must be <a href="http://www.mobacon.de/wiki/doku.php/en/netzer/serialserveraktiviert"> activated and configured for I2C </a>.<br>
<a name="NetzerI2CDefine"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; NetzerI2C &lt;Device-Address:Port&gt;</code><br>
where <code>&lt;Device-Address:Port&gt;</code> Device Address/ IP-Address and Serial Server TCP Port of the Netzer<br><br>
</ul>
<a name="NetzerI2CSet"></a>
<b>Set</b>
<ul>
<li>
Write one byte (or more bytes sequentially) directly to an I2C device (for devices that have only one register to write):<br>
<code>set &lt;name&gt; writeByte &lt;I2C Address&gt; &lt;value&gt;</code><br><br>
</li>
<li>
Write one byte (or more bytes sequentially) to the specified register of an I2C device:<br>
<code>set &lt;name&gt; writeByteReg &lt;I2C Address&gt; &lt;Register Address&gt; &lt;value&gt;</code><br><br>
</li>
<li>
Write n-bytes to an register range, beginning at the specified register:<br>
<code>set &lt;name&gt; writeBlock &lt;I2C Address&gt; &lt;Register Address&gt; &lt;value&gt;</code><br><br>
</li>
Examples:
<ul>
Write 0xAA to device with I2C address 0x60<br>
<code>set test1 writeByte 60 AA</code><br>
Write 0xAA to register 0x01 of device with I2C address 0x6E<br>
<code>set test1 writeByteReg 6E 01 AA</code><br>
Write 0xAA to register 0x01 of device with I2C address 0x6E, after it write 0x55 to register 0x02<br>
<code>set test1 writeByteReg 6E 01 AA 55</code><br>
Write 0xA4 to register 0x03, 0x00 to register 0x04 and 0xDA to register 0x05 of device with I2C address 0x60 as block operation<br>
<code>set test1 writeBlock 60 03 A4 00 DA</code><br>
</ul><br>
</ul>
<a name="NetzerI2CGet"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt; read &lt;I2C Address&gt; [&lt;Register Address&gt; [&lt;number of registers&gt;]] </code>
<br>
gets value of I2C device's registers<br><br>
Examples:
<ul>
Reads byte from device with I2C address 0x60<br>
<code>get test1 writeByte 60</code><br>
Reads register 0x01 of device with I2C address 0x6E.<br>
<code>get test1 read 6E 01 AA 55</code><br>
Reads register 0x03 to 0x06 of device with I2C address 0x60.<br>
<code>get test1 read 60 03 4</code><br>
</ul><br>
</ul><br>
<a name="NetzerI2CAttr"></a>
<b>Attributes</b>
<ul>
<li><a href="#ignore">ignore</a></li>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#showtime">showtime</a></li>
</ul>
<br>
</ul>
=end html
=begin html_DE
<a name="NetzerI2C"></a>
<h3>NetzerI2C</h3>
<ul>
<a name="NetzerI2C"></a>
Erm&ouml;glicht den Zugriff auf die I2C Schnittstelle des <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/index">Netzer</a>.<br> &uuml;ber logische Module. Register von I2C IC's k&ouml;nnen auch direkt gelesen und geschrieben werden.<br><br>
<b>Vorbereitung:</b><br>
Bevor dieses Modul verwendet werden kann muss der Serielle Server des Netzers <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/serialserveraktiviert"> und auf I2C gestellt</a> werden.
<a name="NetzerI2CDefine"></a><br><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; NetzerI2C &lt;Device-Address:Port&gt;</code><br>
<code>&lt;Device-Address:Port&gt;</code> ist die Adresse/IP-Adresse und Serial Server TCP-Port des Netzer<br><br>
</ul>
<a name="NetzerI2CSet"></a>
<b>Set</b>
<ul>
<li>
Schreibe ein Byte (oder auch mehrere nacheinander) direkt auf ein I2C device (manche I2C Module sind so einfach, das es nicht einmal mehrere Register gibt):<br>
<code>set &lt;name&gt; writeByte &lt;I2C Address&gt; &lt;value&gt;</code><br><br>
</li>
<li>
Schreibe ein Byte (oder auch mehrere nacheinander) direkt auf ein Register des adressierten I2C device:<br>
<code>set &lt;name&gt; writeByteReg &lt;I2C Address&gt; &lt;Register Address&gt; &lt;value&gt;</code><br><br>
</li>
<li>
Schreibe n-bytes auf einen Registerbereich, beginnend mit dem angegebenen Register:<br>
<code>set &lt;name&gt; writeBlock &lt;I2C Address&gt; &lt;Register Address&gt; &lt;value&gt;</code><br><br>
</li>
Beispiele:
<ul>
Schreibe 0xAA zu Modul mit I2C Addresse 0x60<br>
<code>set test1 writeByte 60 AA</code><br>
Schreibe 0xAA zu Register 0x01 des Moduls mit der I2C Adresse 0x6E<br>
<code>set test1 writeByteReg 6E 01 AA</code><br>
Schreibe 0xAA zu Register 0x01 des Moduls mit der I2C Adresse 0x6E, schreibe danach 0x55 zu Register 0x01<br>
<code>set test1 writeByteReg 6E 01 AA 55</code><br>
Schreibe 0xA4 zu Register 0x03, 0x00 zu Register 0x04 und 0xDA zu Register 0x05 des Moduls mit der I2C Adresse 0x60 als Block<br>
<code>set test1 writeBlock 60 03 A4 00 DA</code><br>
</ul><br>
</ul>
<a name="NetzerI2CGet"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt; read &lt;I2C Address&gt; [&lt;Register Address&gt; [&lt;number of registers&gt;]] </code>
<br>
Auslesen der Registerinhalte des I2C Moduls<br><br>
Examples:
<ul>
Lese Byte vom Modul mit der I2C Adresse 0x60<br>
<code>get test1 writeByte 60</code><br>
Lese den Inhalt des Registers 0x01 vom Modul mit der I2C Adresse 0x6E.<br>
<code>get test1 read 6E 01 AA 55</code><br>
Lese den Inhalt des Registerbereichs 0x03 bis 0x06 vom Modul mit der I2C Adresse 0x60.<br>
<code>get test1 read 60 03 4</code><br>
</ul><br>
</ul><br>
<a name="NetzerI2CAttr"></a>
<b>Attribute</b>
<ul>
<li><a href="#ignore">ignore</a></li>
<li><a href="#do_not_notify">do_not_notify</a></li>
<li><a href="#showtime">showtime</a></li>
</ul>
<br>
</ul>
=end html_DE

451
fhem/FHEM/51_Netzer.pm Normal file
View File

@ -0,0 +1,451 @@
##############################################################################
#
# 51_Netzer.pm
#
##############################################################################
# Modul for Netzer access
#
#
#
##############################################################################
package main;
use strict;
use warnings;
use POSIX;
use Scalar::Util qw(looks_like_number);
use IO::File;
#vorhandene Ports
my @ports = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m" );
sub Netzer_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "Netzer_Define";
$hash->{ReadFn} = "Netzer_Read";
$hash->{GetFn} = "Netzer_Get";
$hash->{SetFn} = "Netzer_Set";
$hash->{AttrFn} = "Netzer_Attr";
$hash->{UndefFn} = "Netzer_Undef";
$hash->{ExceptFn} = "Netzer_Except";
$hash->{AttrList} = "poll_interval" .
" Port_a:in,out,cnt" . " Port_b:in,out,cnt" . " Port_c:in,out,cnt" .
" Port_d:in,out,PWM" .
" Port_e:in,out,ADC" . " Port_f:in,out,ADC" .
" Port_g:in,out" . " Port_h:in,out" . " Port_i:in,out" .
" Port_j:in,out,PWM" .
" Port_k:in,out" . " Port_l:in,out" . " Port_m:in,out";
}
#############################################
sub Netzer_Define($$) {
my ($hash, $def) = @_;
my @args = split("[ \t]+", $def);
my $menge = int(@args);
if (int(@args) < 2) {
return "Define: to less arguments. Usage:\n" .
"define <name> Netzer <Host>:<port>";
}
#Pruefen, ob GPIO bereits verwendet
foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) {
if ($args[2] eq InternalVal($dev,"DeviceName","")) {
return "IP-Address $args[2] already used by $dev";
}
}
my $name = $args[0];
$hash->{DeviceName} = $args[2];
Netzer_conn($hash);
# create default attributes
#my $msg = CommandAttr(undef, $name . ' direction input');
#return $msg if ($msg);
return undef;
}
#############################################
sub Netzer_Get($;$) {
my (@a) = @_;
my $hash = $a[0];
my $name = $hash->{NAME};
my ($port,$cnt) = split("_", $a[2]) if ( defined($a[2]) );
my $function = $attr{$name}{"Port_".$port} if defined($port) && defined($attr{$name}{"Port_".$port});
my $buf = "";
if ( !( defined($a[2]) ) ) {
foreach (@ports) {
if(defined($attr{$name}) && defined($attr{$name}{"Port_".$_})) {
my $function = $attr{$name}{"Port_".$_};
if ($function =~ m/^(PWM|ADC)$/i) {
$buf = $_."=?\r\n";
Netzer_send($hash, $buf);
} elsif ($function =~ m/^(cnt)$/i) {
$buf = "z".$_."=?\r\n";
Netzer_send($hash, $buf);
}
}
}
$buf = "x=?\r\n";
} elsif ( defined($port) && grep( /^$port$/, @ports ) && defined($cnt) && $cnt eq "counter" && defined($function) && $function eq "cnt" ) {
$buf = "z" . $port ."=?\r\n";
} elsif ( defined($port) && grep( /^$a[2]$/, @ports ) && $port ne "?" ) {
$buf = $a[2]."=?\r\n";
} else {
my $list = "";
foreach (@ports) {
#next if (wenn port nicht genutzt werden soll);
$list .= " " unless ($list eq "");
$list .= $_ . ':noArg';
$list .= " " . $_ . "_counter:noArg" if( defined($attr{$name}) && defined($attr{$name}{"Port_".$_}) && $attr{$name}{"Port_".$_} eq "cnt" );
}
return 'Unknown argument ' . $a[2] . ', choose one of ' . $list;
}
Netzer_send($hash, $buf);
return;
}
#############################################
sub Netzer_Set($@) {
my ($hash, @a) = @_;
my $name = $a[0];
my $port = $a[1] if (defined ($a[1]));
my $val = $a[2] if (defined ($a[2]));
($port, my $cnt) = split("_", $port) if defined($port) && $port =~ m/(_counter)$/;
my $function = $attr{$name}{"Port_".$port} if defined($port) && defined($attr{$name}{"Port_".$port});
if ( defined($function) && ( ( $function=~ m/^(PWM|out)$/i && !defined($cnt) ) || ( $function=~ m/^(cnt)$/i && defined($cnt) ) ) ) {
my $msg = "$name: wrong value ". (defined($val)?"$val ":"") . "for Port_$a[1], valid values: 0-";
my $buf = undef;
if ( $function eq "cnt" && defined($cnt) ) {
return $msg . "32767" if !defined($val) || $val > 32767 || $val < 0;
$buf = "z" . $port;
} else {
return $msg . "1023" if ($function eq "PWM" && (!defined($val) || $val > 1023 || $val < 0));
return $msg . "1" if ($function eq "out" && (!defined($val) || $val > 1 || $val < 0));
$buf = $port;
}
$val = sprintf("%x",$val);
$buf .= "=$val\r\n";
Netzer_send($hash, $buf);
} else {
my $list = "";
foreach (@ports) {
if(defined($attr{$name}) && defined($attr{$name}{"Port_".$_})) {
my $function = $attr{$name}{"Port_".$_};
if ($function eq "out") {
$list .= " " unless ($list eq "");
$list .= $_ . ':0,1';
} elsif ($function eq "PWM") {
$list .= " " unless ($list eq "");
$list .= $_ . ':slider,0,1,1023';
} elsif ($function eq "cnt") {
$list .= " " unless ($list eq "");
$list .= $_ . "_counter" ;
}
}
}
return 'Unknown argument ' . (defined($port)?$port:"") . (defined($cnt)?"_$cnt":"") . ', choose one of ' . $list;
}
return;
}
#############################################
sub Netzer_Attr(@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
#Log3 $name, 1, "Name: $name Attr: $attr Wert: $val";
if ($attr eq 'poll_interval') {
if ( defined($val) ) {
if ( looks_like_number($val) && $val > 0) {
RemoveInternalTimer($hash);
InternalTimer(1, 'Netzer_Poll', $hash, 0);
} else {
$msg = "$hash->{NAME}: Wrong poll intervall defined. poll_interval must be a number > 0";
}
} else { #wird auch aufgerufen wenn $val leer ist, aber der attribut wert wird auf 1 gesetzt
RemoveInternalTimer($hash);
}
}
if (!$val) {
delete ($hash->{READINGS}{$attr."_counter"}) if defined($hash->{READINGS}{$attr."_counter"});
} elsif ($val =~ m/^(in|out)$/) {
delete ($hash->{READINGS}{$attr."_counter"}) if defined($hash->{READINGS}{$attr."_counter"});
} elsif ($attr =~ m/^(Port_[a-c])$/) {
$msg = "$hash->{NAME}: $attr wrong function $val. Use in, out or cnt" if $val !~ m/^(cnt)$/;
delete ($hash->{READINGS}{$attr."_counter"}) if defined($hash->{READINGS}{$attr."_counter"}) && $val !~ m/^(cnt)$/;
} elsif ($attr =~ m/^(Port_[d|j])$/) {
$msg = "$hash->{NAME}: $attr wrong function $val. Use in, out or PWM" if $val !~ m/^(PWM)$/;
delete ($hash->{READINGS}{$attr."_counter"}) if defined($hash->{READINGS}{$attr."_counter"});
} elsif ($attr =~ m/^(Port_[e|f])$/) {
$msg = "$hash->{NAME}: $attr wrong function $val. Use in, out or ADC" if $val !~ m/^(ADC)$/;
delete ($hash->{READINGS}{$attr."_counter"}) if defined($hash->{READINGS}{$attr."_counter"});
}
return ($msg) ? $msg : undef;
}
#############################################
sub Netzer_Read($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $buf;
my $ret = sysread($hash->{CD}, $buf, 1024*1024);
if(!defined($ret) || $ret <= 0) {
Netzer_conn($hash);
return;
} else {
chomp ($buf);
my @msg = split(" ", $buf);
readingsBeginUpdate($hash);
foreach (@msg) {
#abfangen wenn mehrere botschafen
my ($port, $val) = split("=", $_);
$val =~ s/ //g;
$val = hex($val);
my $sval;
#my ($bufc) = $_ =~ /(\d+)/;
if ($port eq "x") {
for (my $i = 0; $i <= 12; $i++) {
next if defined($attr{$name}{"Port_".$ports[$i]}) && $attr{$name}{"Port_".$ports[$i]} =~ m/^(PWM|ADC)$/;
$sval = hex($val) & (1 << $i);
$sval = $sval == 0 ? "0" :"1";
readingsBulkUpdate($hash, 'Port_'.$ports[$i] , $sval) if (ReadingsVal($name,'Port_'.$ports[$i],0) ne $sval);
}
} elsif ( grep( /^$port$/, @ports ) ) {
readingsBulkUpdate($hash, 'Port_'.$port , $val);
} elsif ( grep( ($port =~ s/z// ), @ports ) ) {
readingsBulkUpdate($hash, 'Port_'.$port.'_counter' , ($val > 32767?"overflow":$val));
}
}
readingsBulkUpdate($hash, 'received', $buf);
#readingsBulkUpdate($hash, 'zeichenmenge', $ret);
readingsEndUpdate($hash, 1);
}
}
#############################################
sub Netzer_Poll($) {#Update of all Readings
my ($hash) = @_;
my $name = $hash->{NAME};
Netzer_Get($hash);
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'Netzer_Poll', $hash, 0);
}
return;
}
#############################################
sub Netzer_Undef($$) {
my ($hash, $arg) = @_;
if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
RemoveInternalTimer($hash);
}
Netzer_disconn($hash,0);
return undef;
}
#############################################
sub Netzer_send($$) {
my ($hash, $buf) = @_;
my $cnt= length ($buf);
if (not defined($hash->{CD})) {
Log3 $hash, 1, "$hash->{NAME}: Verbindung unterbrochen, versuche Verbindungsaufbau";
Netzer_conn($hash);
}
if ($hash->{CD}) {
syswrite($hash->{CD}, $buf, $cnt) ;
} else {
Log3 $hash, 1, "$hash->{NAME}: Daten konnten nicht gesendet werden";
}
}
#############################################
sub Netzer_conn($) {
my ($hash) = @_;
my $name = $hash->{NAME};
Netzer_disconn($hash,0);
my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3;
my $conn = IO::Socket::INET->new(PeerAddr=>"$hash->{DeviceName}", Timeout => $timeout);
if($conn) {
$hash->{STATE} = "Connected";
$hash->{FD} = $conn->fileno();
$hash->{CD} = $conn; # sysread / close won't work on fileno
$hash->{CONNECTS}++;
$selectlist{$name} = $hash;
Log(GetLogLevel($name,3), "$name: connected to $hash->{DeviceName}");
} else {
Netzer_disconn($hash, 1);
}
}
#############################################
sub Netzer_disconn($$) {
my ($hash, $connect) = @_;
my $name = $hash->{NAME};
return if( !$hash->{CD} );
close($hash->{CD}) if($hash->{CD});
delete($hash->{FD});
delete($hash->{CD});
delete($selectlist{$name});
$hash->{STATE} = "Disconnected";
if($connect) {
Log3 $name, 4, "$name: Connect failed.";
} else {
Log3 $name, 4, "$name: Disconnected";
}
}
1;
=pod
=begin html
<a name="Netzer"></a>
<h3>Netzer</h3>
<ul>
The <a href="http://www.mobacon.de/wiki/doku.php/en/netzer/index">Netzer</a> realizes an Ethernet interface on a PIC-based platform. As a gateway module it enables communication between standard TCP/IP sockets and serial busses like I2C, SPI and UART.
Also up to 13 GPIO pins can be accessed. This Modul provides access to these GPIO pins on a Netzer running IO_base in Version 1.5.
There are two pins usable as ADC channel, two as PMW outputs, three as counter and three can generate an interrupt.
The GPIO pins are configured a input per default. Before a port can be used as output it must be <a href="http://www.mobacon.de/wiki/doku.php/en/netzer/io">configured</a> via the embedded webpage.
If one of the input ports is configured to send interrupt events on GPIO Server, on every event all port values will be updated.
All ports can be read and controlled individually by the function readingsProxy.
<br><br>
<a name="NetzerDefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; Netzer &lt;host:port&gt;</code>
<br><br>
</ul>
<a name="NetzerSet"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port[_counter]&gt; &lt;value&gt;</code>
<br>
Where &lt;value&gt is a character between <code>a</code> and <code>m</code> <br> according to the port. If Port attr is <code>cnt</code> an aditional value &lt;port_counter&gt; can be set.<br>
Only ports with corresponding attr Port_[a-m] set to <code>PWM</code> or <code>out</code> can be used.<br>
If Port attr is:<ul>
<li>PWM &lt;value&gt can be a number between 0 and 1023</li>
<li>out &lt;value&gt can be a number between 0 and 1</li>
<li>cnt &lt;port_counter&gt; &lt;value&gt can be a number between 0 and 32767</li>
<br></ul>
</ul>
<b>Get</b>
<ul>
<code>get &lt;name&gt; [&lt;port[_counter]&gt;]</code>
<br>
If no &lt;port&gt; is set, all readings will be updated.<br>
&lt;port&gt is a character between <code>a</code> and <code>m</code><br> according to the port. If Port attr is <code>cnt</code> an aditional reading &lt;port_counter&gt; can be read.
<br><br>
</ul>
<a name="NetzerAttr"></a>
<b>Attributes</b>
<ul>
<a name="poll_interval"></a>
<li>poll_interval<br>
Set the polling interval in minutes to query the sensor for new measured values.
Default: 5, valid values: decimal number</li><br>
<a name="Port_&lt;port&gt;"></a>
<li>Port_&lt;port&gt;<br>
<ul>
Configuration for Netzer port.<br>
&lt;port&gt; is a character between <code>a</code> and <code>m</code>.<br>
<li><code>in</code>: Port is defined as input. Same behavior as no attribute. Set is not avaliable for this port.<br>
Can be used for all ports</li>
<li><code>out</code>: Port is defined as output. Set is avaliable for this port with &lt;value&gt; between 0 and 1.<br>
Can be used for all ports</li>
<li><code>cnt</code>: Port is defined as input. Set is not avaliable for this port.<br>
An second reading: Port_&lt;port&gt;_counter is avaiable.
It can be updated with <code>get</code> an changed with <code>set</code>.<br>
Port_&lt;port&gt;_counter &lt;value&gt; = 0-32767 or overflow if outside this range.<br>
Can be used for ports a,b,c</li>
<li><code>ADC</code>: Port is defined as analog input. <code>Get</code> &lt;value&gt; is 0-1023 according the voltage on port. Set is not avaliable for this port.<br>
Can be used for ports e,f</li>
<li><code>PWM</code>: Port is defined as PWM output. <code>Set</code> and <code>get</code> &lt;value&gt; is 0-1023 according the duty cycle on the port.<br>
Can be used for ports d,j</li>
</ul>
</li><br>
</ul>
<br>
</ul>
=end html
=begin html_DE
<a name="Netzer"></a>
<h3>Netzer</h3>
<ul>
The <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/index">Netzer</a> realisiert ein Ethernetinterface auf PIC-Basis. Es agiert als Gateway zwischen TCP/IP und verschiedenen seriellen Busses wie I2C, SPI oder UART. Es k&ouml;nnen bis zu 13 GPIO Pins angesprochen (gelesen oder geschrieben) werden.
This Modul erm&ouml;glicht den Zugriff auf diese GPIO Pin's auf einem Netzer mit IO_base in Version 1.5.
Es gibt zwei als ADC nutzbare Pin's channel, 2 als PMW Ausg&auml;nge, drei als Z&auml;hler sowie drei die einen Interrupt ausl&ouml;sen k&ouml;nnen.
Die GPIO Pin's sind standardm&auml;ßig als Eing&auml;nge konfiguriert. Bevor ein Pin anderweitig genutzt werden kann, muss er &uuml;ber die eingebaute Website entsprechend <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/io">eingestellt</a> werden.
Ist einer der Eing&auml;nge als Inerrupteingang eingestellt, werden bei jedem Interrupereignis die Weter s&auml;mtlicher Ports aktualisiert.
<br><br>
<a name="NetzerDefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; Netzer &lt;host:port&gt;</code>
<br><br>
</ul>
<a name="NetzerSet"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port[_counter]&gt; &lt;value&gt;</code>
<br>
Dabei ist &lt;value&gt ein dem Port entsprechender Buchstabe zwischen <code>a</code> und <code>m</code>. Besitzt der Port das Attribut <code>cnt</code> so kann ein weiterer Wert &lt;port_counter&gt; gesetzt werden.<br>
Ausschließlich Port's die &uuml;ber Attribut Port_[a-m] auf <code>PWM</code> oder <code>out</code> gesetzt sind k&ouml;nnen benutzt werden.<br>
Bei Port Attribut:<ul>
<li>PWM &lt;value&gt kann ein Wert zwischen 0 und 1023 sein</li>
<li>out &lt;value&gt kann ein Wert zwischen 0 und 1 sein</li>
<li>cnt &lt;port_counter&gt; &lt;value&gt kann ein Wert zwischen 0 und 32767 sein</li>
<br></ul>
</ul>
<b>Get</b>
<ul>
<code>get &lt;name&gt; [&lt;port[_counter]&gt;]</code>
<br>
Ohne &lt;port&gt; werde alle Werte aktualisiert.<br>
Wenn &lt;port&gt ein Buchstabe zwischen <code>a</code> und <code>m</code><br> ist, wird der Portwert aktualisiert und bei Port Attribut <code>cnt</code> kann ein weiterer Z&auml;hlerwert &lt;port_counter&gt; gelesen werden.<br>
<br>
</ul>
<a name="NetzerAttr"></a>
<b>Attributes</b>
<ul>
<a name="poll_interval"></a>
<li>poll_interval<br>
Aktualisierungsintervall aller Werte in Minuten.<br>
Standard: 5, g&uuml;ltige Werte: Dezimalzahl
</li><br>
<a name="Port_&lt;port&gt;"></a>
<li>Port_&lt;port&gt;<br>
<ul>
Konfiguration des jeweiligen GPIO.<br>
&lt;port&gt; ist ein Buchstabe zwischen <code>a</code> und <code>m</code>.<br>
<li><code>in</code>: Port ist Eingang. Kann auch weggelassen werden, da Standard. Set ist f&uuml;r diesen Port nicht verf&uuml;gbar.<br>
Nutzbar f&uuml;r alle Port's</li>
<li><code>out</code>: Port ist Ausgang. Set kann &lt;value&gt; zwischen 0 und 1 haben.<br>
Nutzbar f&uuml;r alle Port's</li>
<li><code>cnt</code>: Port ist Eingang. Set ist f&uuml;r diesen Port nicht verf&uuml;gbar.<br>
Ein weiteres Reading: Port_&lt;port&gt;_counter ist verf&uuml;gbar.
Dieses kann auch mit <code>get</code> gelesen und mit <code>set</code> ver&auml;ndert werden.<br>
Port_&lt;port&gt;_counter &lt;value&gt; = 0-32767 oder overflow wenn es ausserhalb dieses Bereichs liegt.<br>
Nutzbar f&uuml;r Port's a,b,c</li>
<li><code>ADC</code>: Port ist Analogeingang. <code>get</code> &lt;value&gt; ist 0-1023 entsprechend der Spannung am Port. Set ist f&uuml;r diesen Port nicht verf&uuml;gbar.<br>
Nutzbar f&uuml;r Port's e,f</li>
<li><code>PWM</code>: Port ist PWM-Ausgang. <code>set</code> und <code>get</code> &lt;value&gt; ist 0-1023 entsprechend des Dutycycle am Port.<br>
Nutzbar f&uuml;r Port's d,j</li>
</ul>
</li><br>
</ul>
<br>
</ul>
=end html_DE
=cut