############################################################################## # $Id$ # 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 Netzer :"; } #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 =item device =item summary controls/reads GPIO pins on an Netzer =item summary_DE steuern/lesen der GPIO Pins eines Netzer =begin html

Netzer

    The Netzer 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 configured 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.

    Define
      define <name> Netzer <host:port>

    Set
      set <name> <port[_counter]> <value>
      Where <value> is a character between a and m
      according to the port. If Port attr is cnt an aditional value <port_counter> can be set.
      Only ports with corresponding attr Port_[a-m] set to PWM or out can be used.
      If Port attr is:
      • PWM <value> can be a number between 0 and 1023
      • out <value> can be a number between 0 and 1
      • cnt <port_counter> <value> can be a number between 0 and 32767

    Get
      get <name> [<port[_counter]>]
      If no <port> is set, all readings will be updated.
      <port> is a character between a and m
      according to the port. If Port attr is cnt an aditional reading <port_counter> can be read.

    Attributes
    • poll_interval
      Set the polling interval in minutes to query the sensor for new measured values. Default: 5, valid values: decimal number

    • Port_<port>
        Configuration for Netzer port.
        <port> is a character between a and m.
      • in: Port is defined as input. Same behavior as no attribute. Set is not avaliable for this port.
        Can be used for all ports
      • out: Port is defined as output. Set is avaliable for this port with <value> between 0 and 1.
        Can be used for all ports
      • cnt: Port is defined as input. Set is not avaliable for this port.
        An second reading: Port_<port>_counter is avaiable. It can be updated with get an changed with set.
        Port_<port>_counter <value> = 0-32767 or overflow if outside this range.
        Can be used for ports a,b,c
      • ADC: Port is defined as analog input. Get <value> is 0-1023 according the voltage on port. Set is not avaliable for this port.
        Can be used for ports e,f
      • PWM: Port is defined as PWM output. Set and get <value> is 0-1023 according the duty cycle on the port.
        Can be used for ports d,j


=end html =begin html_DE

Netzer

    The Netzer 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 eingestellt werden. Ist einer der Eingänge als Inerrupteingang eingestellt, werden bei jedem Interrupereignis die Weter sämtlicher Ports aktualisiert.

    Define
      define <name> Netzer <host:port>

    Set
      set <name> <port[_counter]> <value>
      Dabei ist <value> ein dem Port entsprechender Buchstabe zwischen a und m. Besitzt der Port das Attribut cnt so kann ein weiterer Wert <port_counter> gesetzt werden.
      Ausschließlich Port's die über Attribut Port_[a-m] auf PWM oder out gesetzt sind können benutzt werden.
      Bei Port Attribut:
      • PWM <value> kann ein Wert zwischen 0 und 1023 sein
      • out <value> kann ein Wert zwischen 0 und 1 sein
      • cnt <port_counter> <value> kann ein Wert zwischen 0 und 32767 sein

    Get
      get <name> [<port[_counter]>]
      Ohne <port> werde alle Werte aktualisiert.
      Wenn <port> ein Buchstabe zwischen a und m
      ist, wird der Portwert aktualisiert und bei Port Attribut cnt kann ein weiterer Zählerwert <port_counter> gelesen werden.

    Attributes
    • poll_interval
      Aktualisierungsintervall aller Werte in Minuten.
      Standard: 5, gültige Werte: Dezimalzahl

    • Port_<port>
        Konfiguration des jeweiligen GPIO.
        <port> ist ein Buchstabe zwischen a und m.
      • in: Port ist Eingang. Kann auch weggelassen werden, da Standard. Set ist für diesen Port nicht verfügbar.
        Nutzbar für alle Port's
      • out: Port ist Ausgang. Set kann <value> zwischen 0 und 1 haben.
        Nutzbar für alle Port's
      • cnt: Port ist Eingang. Set ist für diesen Port nicht verfügbar.
        Ein weiteres Reading: Port_<port>_counter ist verfügbar. Dieses kann auch mit get gelesen und mit set verändert werden.
        Port_<port>_counter <value> = 0-32767 oder overflow wenn es ausserhalb dieses Bereichs liegt.
        Nutzbar für Port's a,b,c
      • ADC: Port ist Analogeingang. get <value> ist 0-1023 entsprechend der Spannung am Port. Set ist für diesen Port nicht verfügbar.
        Nutzbar für Port's e,f
      • PWM: Port ist PWM-Ausgang. set und get <value> ist 0-1023 entsprechend des Dutycycle am Port.
        Nutzbar für Port's d,j


=end html_DE =cut