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:
parent
58afd7a46c
commit
e54384987a
645
fhem/FHEM/00_NetzerI2C.pm
Normal file
645
fhem/FHEM/00_NetzerI2C.pm
Normal 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 <name> NetzerI2C <Device-Address:Port></code><br>
|
||||
where <code><Device-Address:Port></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 <name> writeByte <I2C Address> <value></code><br><br>
|
||||
</li>
|
||||
<li>
|
||||
Write one byte (or more bytes sequentially) to the specified register of an I2C device:<br>
|
||||
<code>set <name> writeByteReg <I2C Address> <Register Address> <value></code><br><br>
|
||||
</li>
|
||||
<li>
|
||||
Write n-bytes to an register range, beginning at the specified register:<br>
|
||||
<code>set <name> writeBlock <I2C Address> <Register Address> <value></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 <name> read <I2C Address> [<Register Address> [<number of registers>]] </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öglicht den Zugriff auf die I2C Schnittstelle des <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/index">Netzer</a>.<br> über logische Module. Register von I2C IC's kö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 <name> NetzerI2C <Device-Address:Port></code><br>
|
||||
<code><Device-Address:Port></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 <name> writeByte <I2C Address> <value></code><br><br>
|
||||
</li>
|
||||
<li>
|
||||
Schreibe ein Byte (oder auch mehrere nacheinander) direkt auf ein Register des adressierten I2C device:<br>
|
||||
<code>set <name> writeByteReg <I2C Address> <Register Address> <value></code><br><br>
|
||||
</li>
|
||||
<li>
|
||||
Schreibe n-bytes auf einen Registerbereich, beginnend mit dem angegebenen Register:<br>
|
||||
<code>set <name> writeBlock <I2C Address> <Register Address> <value></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 <name> read <I2C Address> [<Register Address> [<number of registers>]] </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
451
fhem/FHEM/51_Netzer.pm
Normal 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 <name> Netzer <host:port></code>
|
||||
<br><br>
|
||||
</ul>
|
||||
|
||||
<a name="NetzerSet"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<code>set <name> <port[_counter]> <value></code>
|
||||
<br>
|
||||
Where <value> 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 <port_counter> 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 <value> can be a number between 0 and 1023</li>
|
||||
<li>out <value> can be a number between 0 and 1</li>
|
||||
<li>cnt <port_counter> <value> can be a number between 0 and 32767</li>
|
||||
<br></ul>
|
||||
</ul>
|
||||
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<code>get <name> [<port[_counter]>]</code>
|
||||
<br>
|
||||
If no <port> is set, all readings will be updated.<br>
|
||||
<port> 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 <port_counter> 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_<port>"></a>
|
||||
<li>Port_<port><br>
|
||||
<ul>
|
||||
Configuration for Netzer port.<br>
|
||||
<port> 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 <value> 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_<port>_counter is avaiable.
|
||||
It can be updated with <code>get</code> an changed with <code>set</code>.<br>
|
||||
Port_<port>_counter <value> = 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> <value> 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> <value> 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önnen bis zu 13 GPIO Pins angesprochen (gelesen oder geschrieben) werden.
|
||||
This Modul ermö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änge, drei als Zähler sowie drei die einen Interrupt auslösen können.
|
||||
Die GPIO Pin's sind standardmäßig als Eingänge konfiguriert. Bevor ein Pin anderweitig genutzt werden kann, muss er über die eingebaute Website entsprechend <a href="http://www.mobacon.de/wiki/doku.php/de/netzer/io">eingestellt</a> werden.
|
||||
Ist einer der Eingänge als Inerrupteingang eingestellt, werden bei jedem Interrupereignis die Weter sämtlicher Ports aktualisiert.
|
||||
<br><br>
|
||||
|
||||
<a name="NetzerDefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> Netzer <host:port></code>
|
||||
<br><br>
|
||||
</ul>
|
||||
|
||||
<a name="NetzerSet"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<code>set <name> <port[_counter]> <value></code>
|
||||
<br>
|
||||
Dabei ist <value> 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 <port_counter> gesetzt werden.<br>
|
||||
Ausschließlich Port's die über Attribut Port_[a-m] auf <code>PWM</code> oder <code>out</code> gesetzt sind können benutzt werden.<br>
|
||||
Bei Port Attribut:<ul>
|
||||
<li>PWM <value> kann ein Wert zwischen 0 und 1023 sein</li>
|
||||
<li>out <value> kann ein Wert zwischen 0 und 1 sein</li>
|
||||
<li>cnt <port_counter> <value> kann ein Wert zwischen 0 und 32767 sein</li>
|
||||
<br></ul>
|
||||
</ul>
|
||||
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<code>get <name> [<port[_counter]>]</code>
|
||||
<br>
|
||||
Ohne <port> werde alle Werte aktualisiert.<br>
|
||||
Wenn <port> 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ählerwert <port_counter> 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ültige Werte: Dezimalzahl
|
||||
</li><br>
|
||||
|
||||
<a name="Port_<port>"></a>
|
||||
<li>Port_<port><br>
|
||||
<ul>
|
||||
Konfiguration des jeweiligen GPIO.<br>
|
||||
<port> 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ür diesen Port nicht verfügbar.<br>
|
||||
Nutzbar für alle Port's</li>
|
||||
<li><code>out</code>: Port ist Ausgang. Set kann <value> zwischen 0 und 1 haben.<br>
|
||||
Nutzbar für alle Port's</li>
|
||||
<li><code>cnt</code>: Port ist Eingang. Set ist für diesen Port nicht verfügbar.<br>
|
||||
Ein weiteres Reading: Port_<port>_counter ist verfügbar.
|
||||
Dieses kann auch mit <code>get</code> gelesen und mit <code>set</code> verändert werden.<br>
|
||||
Port_<port>_counter <value> = 0-32767 oder overflow wenn es ausserhalb dieses Bereichs liegt.<br>
|
||||
Nutzbar für Port's a,b,c</li>
|
||||
<li><code>ADC</code>: Port ist Analogeingang. <code>get</code> <value> ist 0-1023 entsprechend der Spannung am Port. Set ist für diesen Port nicht verfügbar.<br>
|
||||
Nutzbar für Port's e,f</li>
|
||||
<li><code>PWM</code>: Port ist PWM-Ausgang. <code>set</code> und <code>get</code> <value> ist 0-1023 entsprechend des Dutycycle am Port.<br>
|
||||
Nutzbar für Port's d,j</li>
|
||||
</ul>
|
||||
</li><br>
|
||||
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html_DE
|
||||
|
||||
=cut
|
Loading…
x
Reference in New Issue
Block a user