2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 18:59:33 +00:00

initial release

git-svn-id: https://svn.fhem.de/fhem/trunk@5323 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
klauswitt 2014-03-25 22:59:42 +00:00
parent b20a649c7e
commit bed08a8258
4 changed files with 1792 additions and 0 deletions

530
fhem/FHEM/00_RPII2C.pm Normal file
View File

@ -0,0 +1,530 @@
##############################################
# $Id: 00_RPII2C.pm klausw
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use Device::SMBus;
#my $clientsI2C = ":I2C_PC.*:I2C_SHT21:I2C_MCP23017:I2C_BMP180:";
my @clients = qw(
I2C_LCD
I2C_DS1307
I2C_PC.*
I2C_MCP23017
I2C_BMP180
I2C_SHT21
);
my $gpioprg = "/usr/local/bin/gpio"; #WiringPi GPIO utility
#my %matchListI2C = ( #kann noch weg?
# "1:I2C_PCF8574"=> ".*",
# "2:FHT" => "^81..(04|09|0d)..(0909a001|83098301|c409c401)..",
#);
sub RPII2C_Initialize($) {
my ($hash) = @_;
# Provider
$hash->{Clients} = join (':',@clients);
#$hash->{WriteFn} = "RPII2C_Write"; #wird vom client per IOWrite($@) aufgerufen
$hash->{I2CWrtFn} = "RPII2C_Write"; #zum testen als alternative für IOWrite
# Normal devices
$hash->{DefFn} = "RPII2C_Define";
$hash->{UndefFn} = "RPII2C_Undef";
$hash->{GetFn} = "RPII2C_Get";
$hash->{SetFn} = "RPII2C_Set";
#$hash->{AttrFn} = "RPII2C_Attr";
$hash->{NotifyFn} = "RPII2C_Notify";
$hash->{AttrList}= "do_not_notify:1,0 ignore:1,0 showtime:1,0 " .
"$readingFnAttributes";
}
#####################################
sub RPII2C_Define($$) { #
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
unless(@a == 3) {
my $msg = "wrong syntax: define <name> RPII2C <0|1>";
Log3 undef, 2, $msg;
return $msg;
}
if(-e $gpioprg) { #I2C Devices für FHEM User lesbar machen
if(-x $gpioprg) {
if(-u $gpioprg) {
my $exp = $gpioprg.' load i2c';
$exp = `$exp`;
} else {
Log3 $hash, 1, "file $gpioprg is not setuid";
}
} else {
Log3 $hash, 1, "file $gpioprg is not executable";
}
} else {
Log3 $hash, 1, "file $gpioprg doesnt exist";
} #system "/usr/local/bin/gpio load i2c";
my $name = $a[0];
my $dev = $a[2];
$hash->{NOTIFYDEV} = "global";
#$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/i2c-".$dev;
$hash->{STATE} = "initialized";
return undef;
}
#####################################
sub RPII2C_Notify { #
my ($hash,$dev) = @_;
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
RPII2C_forall_clients($hash,\&RPII2C_Init_Client,undef);;
} elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
}
}
#####################################
sub RPII2C_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 RPII2C_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,5,"im init client fuer $name ";
my $ret = CallFn($name,"InitFn",$hash,$args);
if ($ret) {
Log3 $name,2,"error initializing '".$hash->{NAME}."': ".$ret;
}
}
#####################################
sub RPII2C_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};
}
}
return undef;
}
#####################################
sub RPII2C_Set($@) { #writeBlock noch nicht fertig
my ($hash, @a) = @_;
my $name = shift @a;
my $type = shift @a;
my @sets = ('writeByte', 'writeByteReg', 'writeBlock'); #, 'writeNBlock');
return "Unknown argument $type, choose one of " . join(" ", @sets) if @a < 2;
foreach (@a) { #Hexwerte prüfen und in Dezimalwerte wandeln
return "$name: $_ is no 1byte hexadecimal value" if $_ !~ /^(0x|)[0-9A-F]{1,2}$/xi ;
$_ = hex;
}
my $i2ca = shift @a;
return "$name: I2C Address not valid" unless ($i2ca > 3 && $i2ca < 128); #prüfe auf Hexzahl zwischen 4 und 7F
my $i2chash = { i2caddress => $i2ca, direction => "i2cwrite" };
my ($reg, $nbyte, $data) = undef;
if ($type eq "writeByte") {
$data = join(" ", @a);
} elsif ($type eq "writeByteReg") {
$reg = shift @a;
$data = join(" ", @a);
} elsif ($type eq "writeBlock") {
$reg = shift @a;
$nbyte = int(@a);
return "$name maximal blocksize (32byte) exeeded" if $nbyte > 32;
$data = join(" ", @a);
$i2chash->{direction} = "i2cblockwrite";
#####kommt weg da sinnlos??!!! Achtung $nbyte stimmt derzeit nicht
# } elsif ($type eq "writeNBlock") {
# $reg = shift @a;
# return "$name register address must be a hexvalue" if (!defined($reg) || $reg !~ /^(0x|)[0-9A-F]{1,4}$/xi);
# $nbyte = shift @a;
# return "$name number of bytes must be decimal value" if (!defined($nbyte) || $nbyte !~ /^[0-9]{1,2}$/);
# return "$name data values must be n times number of bytes" if (int(@a) % $nbyte != 0);
# $data = join(" ", @a);
#########################################################################
}else {
return "Unknown argument $type, choose one of " . join(" ", @sets);
}
$i2chash->{reg} = $reg if defined($reg); #startadresse zum lesen
$i2chash->{nbyte} = $nbyte if defined($nbyte);
$i2chash->{data} = $data if defined($data);
RPII2C_HWACCESS($hash, $i2chash);
undef $i2chash; #Hash löschen
return undef;
}
##################################### fertig?
sub RPII2C_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);
}
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 not valid" unless ( $a[2] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi);
return "$name register address must be a hexvalue" if (defined($a[3]) && $a[3] !~ /^(0x|)[0-9A-F]{1,4}$/xi);
return "$name number of bytes must be decimal value" if (defined($a[4]) && $a[4] !~ /^[0-9]{1,2}$/);
my $i2chash = { i2caddress => hex($a[2]), direction => "i2cread" };
$i2chash->{reg} = hex($a[3]) if defined($a[3]); #startadresse zum lesen
$i2chash->{nbyte} = $a[4] if defined($a[4]);
Log3 $hash, 1, "Reg: ". $i2chash->{reg};
my $status = RPII2C_HWACCESS($hash, $i2chash);
#my $received = join(" ", @{$i2chash->{received}}); #als Array
my $received = $i2chash->{received}; #als Scalar
undef $i2chash; #Hash löschen
return "$received transmission: $status";
}
return undef;
}
#####################################
sub RPII2C_Write($$) { #wird vom Client aufgerufen
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $ankommen = "$name: vom client empfangen";
foreach my $av (keys %{$clientmsg}) { $ankommen .= "|" . $av . ": " . $clientmsg->{$av}; }
Log3 $hash, 5, $ankommen;
if ( $clientmsg->{direction} && $clientmsg->{i2caddress} ) {
$clientmsg->{$name . "_" . "SENDSTAT"} = RPII2C_HWACCESS($hash, $clientmsg);
}
foreach my $d ( sort keys %main::defs ) { #zur Botschaft passenden Clienten ermitteln geht auf Client: I2CRecFn
#Log3 $hash, 1, "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, "$name ->Client gefunden: $d". ($main::defs{$d}{I2C_Address} ? ", I2Caddress: $main::defs{$d}{I2C_Address}":"") . ($clientmsg->{data} ? " Data: $clientmsg->{data}" : "");
CallFn($d, "I2CRecFn", $chash, $clientmsg);
undef $clientmsg #Hash löschen nachdem Daten verteilt wurden
}
}
return undef;
}
#####################################
sub RPII2C_HWACCESS($$) {
my ($hash, $clientmsg) = @_;
my $status = "error";
my $inh = undef;
Log3 $hash, 5, "$hash->{NAME}: HWaccess I2CAddr: " . sprintf("%.2X", $clientmsg->{i2caddress});
my $dev = Device::SMBus->new(
I2CBusDevicePath => $hash->{DeviceName},
I2CDeviceAddress => hex( sprintf("%.2X", $clientmsg->{i2caddress}) ),
);
if (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cblockwrite") { #Registerblock beschreiben
my @data = split(" ", $clientmsg->{data});
my $dataref = \@data;
$inh = $dev->writeBlockData( $clientmsg->{reg} , $dataref );
my $wr = join(" ", @{$dataref});
Log3 $hash, 5, "$hash->{NAME}: Block schreiben Register: " . sprintf("%.2X", $clientmsg->{reg}) . " Inhalt: " . $wr . " N: ". int(@data) ." Returnvar.: $inh";
$status = "Ok" if $inh == 0;
#kommt wieder weg#################
} elsif (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Registerbereich (mehrfach) beschreiben
my @data = split(" ", $clientmsg->{data});
foreach (0..$#data) {
my $i = $_ -( int($_ / $clientmsg->{nbyte}) * $clientmsg->{nbyte} );
$inh = $dev->writeByteData( ($clientmsg->{reg} + $i ) ,$data[$_]);
Log3 $hash, 5, "$hash->{NAME} NReg schreiben; Reg: " . ($clientmsg->{reg} + $i) . " Inh: " . $data[$_] . " Returnvar.: $inh";
last if $inh != 0;
$status = "Ok" if $inh == 0;
}
#hier Mehrfachbeschreibung eines Registers noch entfernen und dafür Bereich mit Registeroperationen beschreiben
} elsif (defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Register beschreiben
my @data = split(" ", $clientmsg->{data});
foreach (@data) {
$inh = $dev->writeByteData($clientmsg->{reg},$_);
Log3 $hash, 5, "$hash->{NAME}; Register ".sprintf("%.2X", $clientmsg->{reg})." schreiben - Inhalt: " .sprintf("%.2X",$_) . " Returnvar.: $inh";
last if $inh != 0;
$status = "Ok" if $inh == 0;
}
} elsif (defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Byte(s) schreiben
my @data = split(" ", $clientmsg->{data});
foreach (@data) {
$inh = $dev->writeByte($_);
Log3 $hash, 5, "$hash->{NAME} Byte schreiben; Inh: " . $_ . " Returnvar.: $inh";
last if $inh != 0;
$status = "Ok" if $inh == 0;
}
} elsif (defined($clientmsg->{reg}) && $clientmsg->{direction} eq "i2cread") { #Register lesen
my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1;
my $rmsg = "";
for (my $n = 0; $n < $nbyte; $n++) {
$inh = $dev->readByteData($clientmsg->{reg} + $n );
Log3 $hash, 5, "$hash->{NAME}; Register ".sprintf("%.2X", $clientmsg->{reg} + $n )." lesen - Inhalt: ".sprintf("%.2X",$inh);
last if ($inh < 0);
#$rmsg .= sprintf("%.2X",$inh);
$rmsg .= $inh;
$rmsg .= " " if $n <= $nbyte;
$status = "Ok" if ($n + 1) == $nbyte;
}
#@{$clientmsg->{received}} = split(" ", $rmsg) if($rmsg); #Daten als Array übertragen
$clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar übertragen
} elsif ($clientmsg->{direction} eq "i2cread") { #Byte lesen
my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1;
my $rmsg = "";
for (my $n = 0; $n < $nbyte; $n++) {
$inh = $dev->readByte();
Log3 $hash, 5, "$hash->{NAME} Byte lesen; Returnvar.: $inh";
last if ($inh < 0);
$rmsg .= $inh;
$rmsg .= " " if $n <= $nbyte;
$status = "Ok" if ($n + 1) == $nbyte;
}
#@{$clientmsg->{received}} = split(" ", $rmsg) if($rmsg); #Daten als Array übertragen
$clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar übertragen
}
$hash->{STATE} = $status;
$hash->{ERRORCNT} = defined($hash->{ERRORCNT}) ? $hash->{ERRORCNT} += 1 : 1 if $status ne "Ok";
$clientmsg->{$hash->{NAME} . "_" . "RAWMSG"} = $inh;
return $status;
}
=pod
=begin html
<a name="RPII2C"></a>
<h3>RPII2C</h3>
<ul>
<a name="RPII2C"></a>
Provides access to Raspberry Pi's I2C interfaces for some logical modules and also directly.<br>
This modul will basically work on every linux system that provides <code>/dev/i2c-x</code>.<br><br>
<b>preliminary:</b><br>
<ul>
<li>
This module uses gpio utility from <a href="http://wiringpi.com/download-and-install/">WiringPi</a> library change access rights of I2C-Interface<br>
WiringPi installation is described here: <a href="#RPI_GPIO">RPI_GPIO</a><br>
Alternatively for other systems (BeagleBone, etc.) you can manually change access rights for <code>/dev/i2c-x</code>. You will need write-/read access for user that runs FHEM. This can be doen e.g. in etc/init.d/fhem<br>
</li>
<li>
installation of i2c dependencies:<br>
<code>sudo apt-get install libi2c-dev i2c-tools build-essential</code><br>
</li>
<li>
load I2C kernel modules:<br>
open /etc/modules<br>
<code>sudo nano /etc/modules</code><br>
add theese lines<br>
<code>
i2c-dev<br>
i2c-bcm2708<br>
</code>
</li>
<li>
To access the I2C-Bus the Device::SMBus module is necessary:<br>
<code>sudo cpan Device::SMBus</code><br>
</li>
</ul>
<a name="RPII2CDefine"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; RPII2C &lt;I2C Bus Number&gt;</code><br>
where <code>&lt;I2C Bus Number&gt;</code> is the number of the I2C bus that should be used (0 or 1)<br><br>
</ul>
<a name="RPII2CSet"></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>
<li>
Same as writeBlock but writes register range sequentially. The numbers of byte to write must be a multipe of the number of register.
<code>set &lt;name&gt; writeNBlock &lt;I2C Address&gt; &lt;Register Address&gt; &lt;number of registers&gt; &lt;value&gt;</code><br><br>
</li><br>
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 0x02 as two separate commands<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 an block command<br>
<code>set test1 writeBlock 60 03 A4 00 DA</code><br>
</ul><br>
</ul>
<a name="RPII2CGet"></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="RPII2CAttr"></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="RPII2C"></a>
<h3>RPII2C</h3>
<ul>
<a name="RPII2C"></a>
Erm&ouml;glicht den Zugriff auf die I2C Schnittstellen des Raspberry Pi &uuml;ber logische Module. Register von I2C IC's k&ouml;nnen auch direkt gelesen und geschrieben werden.<br><br>
Dieses Modul funktioniert gruns&auml;tzlich auf allen Linux Systemen, die <code>/dev/i2c-x</code> bereitstellen.<br><br>
<b>Vorbereitung:</b><br>
<ul>
<li>
Dieses Modul nutzt das gpio Utility der <a href="http://wiringpi.com/download-and-install/">WiringPi</a> Bibliothek um FHEM Schreibrechte auf die I2C Schnittstelle zu geben.<br>
WiringPi Installation ist hier beschrieben: <a href="#RPI_GPIO">RPI_GPIO</a><br>
F&uuml;r andere Systeme (BeagleBone, etc.) oder auch für das Raspberry kann auf WiringPi verzichtet werden. In diesem Fall müssen die Dateien <code>/dev/i2c-x</code> Schreib-/Leserechte, für den User unter dem FHEM läuft, gesetzt bekommen. (z.B. in der etc/init.d/fhem)<br>
</li>
<li>
Installation der I2C Abh&auml;ngigkeiten:<br>
<code>sudo apt-get install libi2c-dev i2c-tools build-essential</code><br>
</li>
<li>
I2C Kernelmodule laden:<br>
modules Datei &ouml;ffnen<br>
<code>sudo nano /etc/modules</code><br>
folgendes einf&uuml;gen<br>
<code>
i2c-dev<br>
i2c-bcm2708<br>
</code>
</li>
<li>
Desweiteren ist das Perl Modul Device::SMBus f&uuml;r den Zugrff auf den I2C Bus notwendig:<br>
<code>sudo cpan Device::SMBus</code><br>
</li>
</ul>
<a name="RPII2CDefine"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; RPII2C &lt;I2C Bus Number&gt;</code><br>
Die <code>&lt;I2C Bus Number&gt;</code> ist die Nummer des I2C Bus an den die I2C IC's angeschlossen werden (0 oder 1)<br><br>
</ul>
<a name="RPII2CSet"></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>
<li>
Identisch zu writeBlock nur kann der Registerbereich sequentiell beschrieben werden. Die Anzahl der Byte muss ein vielfaches der &lt;number of registers&gt; sein.
<code>set &lt;name&gt; writeNBlock &lt;I2C Address&gt; &lt;Register Address&gt; &lt;number of registers&gt; &lt;value&gt;</code><br><br>
</li><br>
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 in das Register 0x02 als einzelne Befehle<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 zusammen als ein Blockbefehl<br>
<code>set test1 writeBlock 60 03 A4 00 DA</code><br>
</ul><br>
</ul>
<a name="RPII2CGet"></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="RPII2CAttr"></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
1;

488
fhem/FHEM/52_I2C_PCA9532.pm Normal file
View File

@ -0,0 +1,488 @@
##############################################################################
#
# 52_I2C_PCA9532.pm
#
##############################################################################
# Modul for I2C PWM Driver PCA9532
#
# define <name> I2C_PCA9532 <I2C-Adresse>
# set <name> <port> <value>
#
# contributed by Klaus Wittstock (2014) email: klauswittstock bei gmail punkt com
#
##############################################################################
#zu tun:
#bei Set sollten Input Values nicht aktualisiert werden
#$sendpackage{data} als Array ?
#$clientmsg->{received} als Array ?
#Inhalte des Hashes:
#i2caddress 00-7F I2C-Adresse
#direction i2cread|i2cwrite Richtung
#reg 00-FF|"" Registeradresse (kann weggelassen werden für IC's ohne Registeradressierung)
#nbyte Zahl Anzahl Register, die bearbeitet werden sollen (im mom 0-99)
#data 00-FF ... Daten die an I2C geschickt werden sollen (müssen, wenn nbyte benutzt wird immer ein Vielfaches Desselben sein)
#received 00-FF ... Daten die vom I2C empfangen wurden, durch Leerzeichen getrennt (bleibt leer wenn Daten geschrieben werden)
#pname_SENDSTAT Ok|error zeigt Übertragungserfolg an
package main;
use strict;
use warnings;
use SetExtensions;
#use POSIX;
use Scalar::Util qw(looks_like_number);
#use Error qw(:try);
my $setdim = ":slider,0,1,255 ";
my %setsP = (
'off' => 0,
'on' => 1,
'PWM0' => 2,
'PWM1' => 3,
);
###############################################################################
sub I2C_PCA9532_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = "I2C_PCA9532_Define";
$hash->{InitFn} = 'I2C_PCA9532_Init';
$hash->{UndefFn} = "I2C_PCA9532_Undefine";
$hash->{AttrFn} = "I2C_PCA9532_Attr";
#$hash->{StateFn} = "I2C_PCA9532_SetState";
$hash->{SetFn} = "I2C_PCA9532_Set";
$hash->{GetFn} = "I2C_PCA9532_Get";
$hash->{I2CRecFn} = "I2C_PCA9532_I2CRec";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0".
"poll_interval T0:slider,0,1,255 T1:slider,0,1,255 InputPorts ".
"$readingFnAttributes";
}
###############################################################################
sub I2C_PCA9532_SetState($$$$) {
my ($hash, $tim, $vt, $val) = @_;
$val = $1 if($val =~ m/^(.*) \d+$/);
#return "Undefined value $val" if(!defined($it_c2b{$val}));
return undef;
}
###############################################################################
sub I2C_PCA9532_Define($$) {
my ($hash, $def) = @_;
my @a = split("[ \t]+", $def);
$hash->{STATE} = 'defined';
if ($main::init_done) {
eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_PCA9532_Catch($@) if $@;
}
return undef;
}
###############################################################################
sub I2C_PCA9532_Init($$) {
my ( $hash, $args ) = @_;
#my @a = split("[ \t]+", $args);
my $name = $hash->{NAME};
if (defined $args && int(@$args) != 1) {
return "Define: Wrong syntax. Usage:\n" .
"define <name> I2C_PCA9532 <i2caddress>";
}
#return "$name I2C Address not valid" unless ($a[0] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi);
if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
} else {
return "$name I2C Address not valid";
}
#$hash->{I2C_Address} = hex($a[0]);
AssignIoPort($hash);
$hash->{STATE} = 'Initialized';
return;
}
###############################################################################
sub I2C_PCA9532_Catch($) {
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
###############################################################################
sub I2C_PCA9532_Undefine($$) {
my ($hash, $arg) = @_;
if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
RemoveInternalTimer($hash);
}
}
###############################################################################
sub I2C_PCA9532_Attr(@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if ($attr && $attr eq 'poll_interval') {
#my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
if (!defined($val) ) {
RemoveInternalTimer($hash);
} elsif ($val > 0) {
RemoveInternalTimer($hash);
InternalTimer(1, 'I2C_PCA9532_Poll', $hash, 0);
} else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
}
} elsif ($attr && $attr =~ m/^T[0-1]$/i) {
return "wrong value: $val for \"set $name $attr\" use 0-255"
unless(looks_like_number($val) && $val >= 0 && $val < 256);
substr($attr,0,1,"");
my $regaddr = ($attr == 0 ? 2 : 4);
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" );
#$sendpackage{data} = sprintf("%.2X", $val);
#$sendpackage{reg} = sprintf("%.2X", $regaddr);
$sendpackage{data} = $val;
$sendpackage{reg} = $regaddr;
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
} elsif ($attr && $attr eq "InputPorts") {
my @inp = split(" ", $val);
foreach (@inp) {
return "wrong value: $_ for \"set $name $attr\" use space separated numbers 0-15" unless ($_ >= 0 && $_ < 16);
}
}
return ($msg) ? $msg : undef;
}
###############################################################################
sub I2C_PCA9532_Poll($) {
my ($hash) = @_;
my $name = $hash->{NAME};
# Read values
I2C_PCA9532_Get($hash, $name);
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCA9532_Poll', $hash, 0);
}
}
###############################################################################
sub I2C_PCA9532_Set($@) {
my ($hash, @a) = @_;
my $name =$a[0];
my $cmd = $a[1];
my $val = $a[2];
my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
unless (@a == 3) {
}
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" );
if ( $cmd && $cmd =~ m/^Port((0|)[0-9]|1[0-5])$/i) {
return "wrong value: $val for \"set $name $cmd\" use one of: " .
join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) )
unless(exists($setsP{$val}));
substr($cmd,0,4,"");
return "$name error: Port$cmd is defined as input" if ( $cmd ~~ @inports ); #Prüfen ob entsprechender Port Input ist
my $LSreg = int($cmd / 4); #Nummer des entspechenden LS Registers
my $regaddr = $LSreg + 6; #Adresse für Controlregister berechnen (LS0 = 0x06)
my $n = $LSreg * 4; #Erster Port in LSx
my $sbyte = 0;
foreach (reverse 0..3) { #ensprechendes Controlregister füllen
my $portn = $_ + $n;
#hier noch alle inputs auf rezessiv setzen
if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert
$sbyte += $setsP{$val} << (2 * $_);
next;
}
$sbyte += $setsP{ReadingsVal($name,'Port'.$portn,"off")} << (2 * $_); #->sonst aus dem Reading holen
}
#$sendpackage{data} = sprintf("%.2X",$sbyte);
$sendpackage{data} = $sbyte;
#$sendpackage{reg} = sprintf("%.2X", $regaddr);
$sendpackage{reg} = $regaddr;
} elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) {
return "wrong value: $val for \"set $name $cmd\" use 0-255"
unless(looks_like_number($val) && $val >= 0 && $val < 256);
substr($cmd,0,3,"");
my $regaddr = ($cmd == 0 ? 3 : 5);
#$sendpackage{data} = sprintf("%.2X", $val);
$sendpackage{data} = $val;
$sendpackage{reg} = sprintf("%.2X", $regaddr);
} else {
my $list = undef;
foreach (0..15) {
next if ( $_ ~~ @inports ); #Inputs überspringen
#$list .= "Port" . $_ . ":" . join(',', sort keys %setsP) . " ";
$list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " ";
}
$list .= join($setdim, ("PWM0", "PWM1")) . $setdim;
return "Unknown argument $a[1], choose one of " . $list;
}
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
}
###############################################################################
sub I2C_PCA9532_Get($@) {
my ($hash, @a) = @_;
my $name =$a[0];
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
$sendpackage{reg} = 0; #startadresse zum lesen
$sendpackage{nbyte} = 10;
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
}
###############################################################################
sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals für alle Keys in $clientmsg die mit dem physical Namen beginnen
$hash->{$k} = $v if $k =~ /^$pname/ ;
}
#hier noch überprüfen, ob Register und Daten ok
if ($clientmsg->{direction} && defined($clientmsg->{reg}) && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") {
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) { # =~ m/^[a-f0-9]{2}$/i) {
#my @rec = @{$clientmsg->{received}}; #bei übergabe im hash als array
my @rec = split(" ",$clientmsg->{received}); #bei übergabe im als skalar
Log3 $hash, 3, "$name: wrong amount of registers transmitted from $pname" unless (@rec == $clientmsg->{nbyte});
foreach (reverse 0..$#rec) { #reverse, damit Inputs (Register 0 und 1 als letztes geschrieben werden)
I2C_PCA9532_UpdReadings($hash, $_ + $clientmsg->{reg} , $rec[$_]);
}
readingsSingleUpdate($hash,"state", "Ok", 1);
} elsif ( $clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data}) ) { # =~ m/^[a-f0-9]{2}$/i) {#readings aktualisieren wenn Übertragung ok
I2C_PCA9532_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
readingsSingleUpdate($hash,"state", "Ok", 1);
} else {
readingsSingleUpdate($hash,"state", "transmission error", 1);
Log3 $hash, 3, "$name: failure in message from $pname";
Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef ").
(defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef ").
(defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef ").
(defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef ").
(defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef ");
}
} else {
readingsSingleUpdate($hash,"state", "transmission error", 1);
Log3 $hash, 3, "$name: failure in message from $pname";
Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef ").
(defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef ").
(defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef ").
(defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef ").
(defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef ");
#my $cmsg = undef;
#foreach my $av (keys %{$clientmsg}) { $cmsg .= "|" . $av . ": " . $clientmsg->{$av}; }
#Log3 $hash, 3, $cmsg;
}
#undef $clientmsg;
}
###############################################################################
sub I2C_PCA9532_UpdReadings($$$) {
my ($hash, $reg, $inh) = @_;
my $name = $hash->{NAME};
#$inh = hex($inh);
Log3 $hash, 5, "$name UpdReadings Register: $reg, Inhalt: $inh";
readingsBeginUpdate($hash);
if ( $reg < 10 && $reg > 5) { #Wenn PortRegister
my %rsetsP = reverse %setsP;
my $LSreg = $reg - 6; #Nummer des entspechenden LS Registers
my $n = $LSreg * 4; #Erster Port in LSx
foreach (reverse 0..3) { #Ports aus Controlregister abarbeiten
my $pval = 3 & ( $inh >> ($_ * 2) );
my $port = $_ + $n;
readingsBulkUpdate($hash, 'Port'.$port , $rsetsP{$pval})
if (ReadingsVal($name, 'Port'.$port,"nix") ne $rsetsP{$pval}); #nur wenn Wert geändert
}
} elsif ( $reg == 3) { #wenn PWM0 Register
readingsBulkUpdate($hash, 'PWM0' , $inh);
} elsif ( $reg == 5) { #wenn PWM1 Register
readingsBulkUpdate($hash, 'PWM1' , $inh);
} elsif ( $reg == 2) { #wenn Frequenz0 Register
$hash->{Frequency_0} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz";
} elsif ( $reg == 4) { #wenn Frequenz0 Register
$hash->{Frequency_1} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz";
} elsif ( $reg >= 0 && $reg < 2 ) { #Input Register
my $j = 8 * $reg;
Log3 $hash, 5, "Register $reg Inh: $inh";
my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
for (my $i = 0; $i < 8; $i++) {
Log3 $hash, 5, "Register $reg Forschleife i: $i";
if ( ($i + $j) ~~ @inports ) { #nur als Input definierte Ports aktualisieren
my $sval = $inh & (1 << $i);
$sval = $sval == 0 ? "0" :"1";
readingsBulkUpdate($hash, 'Port'.($i + $j) , $sval) if (ReadingsVal($name,'Port'.($i + $j),2) ne $sval);
Log3 $hash, 5, "Register $reg wert: $sval";
}
}
}
readingsEndUpdate($hash, 1);
return;
}
###############################################################################
1;
=pod
=begin html
<a name="I2C_PCA9532"></a>
<h3>I2C_PCA9532</h3>
<ul>
<a name="I2C_PCA9532"></a>
Provides an interface to the PCA9532 I2C 16 channel PWM IC.
The PCA9532 has 2 independent PWM stages and every channel can be attached to on of these stages or directly turned on or off.
The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
<b>attribute IODev must be set</b><br>
<a name="I2C_PCA9532Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_PCA9532 &lt;I2C Address&gt;</code><br>
where <code>&lt;I2C Address&gt;</code> is an 2 digit hexadecimal value<br>
</ul>
<a name="I2C_PCA9532Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
<ul>
<li>if <code>&lt;port&gt;</code> is one of Port0 to Port15, then <code>&lt;value&gt;</code> will be one of:<br>
<ul>
<code>
off<br>
on<br>
PWM0 (output is switched with PWM0 frequency and duty cycle)<br>
PWM1 (output is switched with PWM1 frequency and duty cycle)<br>
</code>
</ul>
</li>
<li>
if <code>&lt;port&gt;</code> is PWM0 or PWM1, then <code>&lt;value&gt;</code> is an value between 0 and 255 and stands for the duty cycle of the PWM stage.
</li>
</ul>
<br>
Examples:
<ul>
<code>set mod1 Port4 PWM1</code><br>
<code>set mod1 PWM1 128</code><br>
</ul><br>
</ul>
<a name="I2C_PCA9532Get"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt;</code>
<br><br>
refreshes all readings
</ul><br>
<a name="I2C_PCA9532Attr"></a>
<b>Attributes</b>
<ul>
<li>poll_interval<br>
Set the polling interval in minutes to query the GPIO's level<br>
Default: -, valid values: decimal number<br><br>
</li>
<li>InputPorts<br>
Space separated list of Portnumers that are used as Inputs<br>
Ports in this list can't be written<br>
Default: no, valid values: 0 1 2 .. 15<br><br>
</li>
<li>T0/T1<br>
Sets PWM0/PWM1 to another Frequency. The Formula is: Fx = 152/(Tx + 1) The corresponding frequency value is shown under internals.<br>
Default: 0 (152Hz), valid values: 0-255<br><br>
</li>
<li><a href="#IODev">IODev</a></li>
<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="I2C_PCA9532"></a>
<h3>I2C_PCA9532</h3>
<ul>
<a name="I2C_PCA9532"></a>
Erm&ouml;glicht die Verwendung eines PCA9532 I2C 16 Kanal PWM IC.
Das PCA9532 hat 2 unabh&auml;ngige PWM Stufen. Jeder Kanal kanne einer der Stufen zugeordnet werden oder direkt auf off/on gesetzt werden.
I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
<b>Das Attribut IODev muss definiert sein.</b><br>
<a name="I2C_PCA9532Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_PCA9532 &lt;I2C Address&gt;</code><br>
Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert<br>
</ul>
<a name="I2C_PCA9532Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
<ul>
<li>wenn als <code>&lt;port&gt;</code> Port0 bis Port15 verwendet wird, dann ist <code>&lt;value&gt;</code> einer dieser Werte:<br>
<ul>
<code>
off<br>
on<br>
PWM0 (Port wird auf PWM0 Frequenz- und Pulsweiteneinstellung gesetzt)<br>
PWM1 (Port wird auf PWM1 Frequenz- und Pulsweiteneinstellung gesetzt)<br>
</code>
</ul>
</li>
<li>
wenn als <code>&lt;port&gt;</code> PWM0 oder PWM1 verwendet wird, ist <code>&lt;value&gt;</code> ein Wert zwischen 0 und 255 ensprechend der Pulsweite der PWM Stufe.
</li>
</ul>
<br>
Beispiele:
<ul>
<code>set mod1 Port4 PWM1</code><br>
<code>set mod1 PWM1 128</code><br>
</ul><br>
</ul>
<a name="I2C_PCA9532Get"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt;</code>
<br><br>
Aktualisierung aller Werte
</ul><br>
<a name="I2C_PCA9532Attr"></a>
<b>Attribute</b>
<ul>
<li>poll_interval<br>
Aktualisierungsintervall aller Werte in Minuten.<br>
Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br>
</li>
<li>InputPorts<br>
Durch Leerzeichen getrennte Portnummern die als Inputs genutzt werden.<br>
Ports in dieser Liste können nicht geschrieben werden.<br>
Standard: no, g&uuml;ltige Werte: 0 1 2 .. 15<br><br>
</li>
<li>T0/T1<br>
&Auml;nderung der Frequenzwerte von PWM0/PWM1 nach der Formel: Fx = 152/(Tx + 1). Der entsprechende Frequenzwert wird unter Internals angezeigt.<br>
Standard: 0 (152Hz), g&uuml;ltige Werte: 0-255<br><br>
</li>
<li><a href="#IODev">IODev</a></li>
<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
=cut

416
fhem/FHEM/52_I2C_PCF8574.pm Normal file
View File

@ -0,0 +1,416 @@
##############################################
# $Id: 52_I2C_PCF8574.pm 3764 2014-01-22 07:09:38Z klausw $
package main;
use strict;
use warnings;
use SetExtensions;
use Scalar::Util qw(looks_like_number);
my %setsP = (
'off' => 0,
'on' => 1,
);
sub I2C_PCF8574_Initialize($) {
my ($hash) = @_;
#$hash->{Match} = ".*";
$hash->{DefFn} = "I2C_PCF8574_Define";
$hash->{InitFn} = 'I2C_PCF8574_Init';
$hash->{AttrFn} = "I2C_PCF8574_Attr";
$hash->{SetFn} = "I2C_PCF8574_Set";
$hash->{GetFn} = "I2C_PCF8574_Get";
$hash->{UndefFn} = "I2C_PCF8574_Undef";
$hash->{ParseFn} = "I2C_PCF8574_Parse";
$hash->{I2CRecFn} = "I2C_PCF8574_I2CRec";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
"poll_interval InputPorts ".
"$readingFnAttributes";
}
###################################
sub I2C_PCF8574_Set($@) { #
my ($hash, @a) = @_;
my $name =$a[0];
my $cmd = $a[1];
my $val = $a[2];
my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" );
if ( $cmd && $cmd =~ m/^Port((0|)[0-7])$/i) {
return "wrong value: $val for \"set $name $cmd\" use one of: off, on"
unless(exists($setsP{$val}));
substr($cmd,0,4,""); #Nummer aus String extrahieren
return "$name error: Port$cmd is defined as input" if ( $cmd ~~ @inports ); #Prüfen ob entsprechender Port Input ist
my $sbyte = 0;
foreach (0..7) {
if ($_ == $cmd) { #Port der geändert werden soll
$sbyte += $setsP{$val} << (1 * $_);
} elsif ($_ ~~ @inports) {#Port der als Input konfiguriert ist wird auf 1 gesetzt
$sbyte += 1 << (1 * $_);
} else { #alle anderen Portwerte werden den Readings entnommen
$sbyte += $setsP{ReadingsVal($name,'Port'.$_,"off")} << (1 * $_); #->sonst aus dem Reading holen
}
}
#$sendpackage{data} = sprintf("%.2X",$sbyte);
$sendpackage{data} = $sbyte;
} else {
my $list = undef;
foreach (0..7) {
next if ( $_ ~~ @inports ); #Inputs überspringen
$list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " ";
}
return "Unknown argument $a[1], choose one of " . $list;
}
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
##########################################################
# IOWrite($hash, \%sendpackage);
##########################################################
##########################################################
# Look for all devices with the same code, and set state, timestamp
#my $code = "$hash->{I2C_Address} $hash->{BTN}";
#my $code = "$hash->{NAME} $hash->{I2C_Address}";
#my $tn = TimeNow();
#my $defptr = $modules{I2C_PCF8574}{defptr}{$code};
#foreach my $n (keys %{ $defptr }) {
# readingsSingleUpdate($defptr->{$n}, "state", $v, 1);
# }
##########################################################
return undef;
}
###################################
sub I2C_PCF8574_Get($@) {
my ($hash, @a) = @_;
my $name =$a[0];
my %sendpackage = ();
#%sendpackage = ( direction => "i2cread", id => (defined( $hash->{ID} )? $hash->{ID} : "00"), i2caddress => $hash->{I2C_Address});
%sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread");
return "$name: no IO device defined" unless ($hash->{IODev});
#neu: über CallFn auf eigene Funktion
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
#alt: für IOWrite
#IOWrite($hash, \%sendpackage);
}
###################################
sub I2C_PCF8574_Attr(@) { #
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if ($attr eq 'poll_interval') {
if ( defined($val) ) {
if ( looks_like_number($val) && $val > 0) {
RemoveInternalTimer($hash);
InternalTimer(1, 'I2C_PCF8574_Poll', $hash, 0);
} else {
$msg = "$hash->{NAME}: Wrong poll intervall defined. poll_interval must be a number > 0";
}
} else {
RemoveInternalTimer($hash);
}
} elsif ($attr && $attr eq "InputPorts") {
if ( defined($val) ) {
my @inp = split(" ", $val);
foreach (@inp) {
$msg = "wrong value: $_ for \"set $name $attr\" use space separated numbers 0-7" unless ($_ >= 0 && $_ < 8);
}
}
}
return $msg
}
###################################
sub I2C_PCF8574_Define($$) { #
my ($hash, $def) = @_;
my @a = split("[ \t]+", $def);
$hash->{STATE} = 'defined';
if ($main::init_done) {
eval { I2C_PCF8574_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_PCF8574_Catch($@) if $@;
}
return undef;
}
###################################
sub I2C_PCF8574_Init($$) { #
my ( $hash, $args ) = @_;
#my @a = split("[ \t]+", $args);
my $name = $hash->{NAME};
if (defined $args && int(@$args) != 1) {
return "Define: Wrong syntax. Usage:\n" .
"define <name> I2C_PCA9532 <i2caddress>";
}
if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
} else {
return "$name I2C Address not valid";
}
#für die Nutzung von IOWrite
#my $code = ( defined( $hash->{ID} )? $hash->{ID} : "00" ) . " " . $hash->{I2C_Address};
#my $ncode = 1;
#my $name = $a[0];
#$hash->{CODE}{$ncode++} = $code;
#$modules{I2C_PCF8574}{defptr}{$code}{$name} = $hash;
AssignIoPort($hash);
$hash->{STATE} = 'Initialized';
return;
}
###################################
sub I2C_PCF8574_Catch($) {
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
###################################
sub I2C_PCF8574_Undef($$) { #
my ($hash, $name) = @_;
if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
RemoveInternalTimer($hash);
}
#foreach my $c (keys %{ $hash->{CODE} } ) {
# $c = $hash->{CODE}{$c};
# my $c = ( defined( $hash->{ID} )? $hash->{ID} : "00" ) . " " . $hash->{I2C_Address};
# As after a rename the $name my be different from the $defptr{$c}{$n}
# we look for the hash.
# foreach my $dname (keys %{ $modules{I2C_PCF8574}{defptr}{$c} }) {
# delete($modules{I2C_PCF8574}{defptr}{$c}{$dname})
# if($modules{I2C_PCF8574}{defptr}{$c}{$dname} == $hash);
# }
# }
return undef;
}
###################################
sub I2C_PCF8574_Poll($) { #for attr poll_intervall -> readout pin values
my ($hash) = @_;
my $name = $hash->{NAME};
# Read values
I2C_PCF8574_Get($hash, $name);
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCF8574_Poll', $hash, 0);
}
}
###################################
sub I2C_PCF8574_I2CRec($@) { # über CallFn vom physical aufgerufen
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals für alle Keys in $clientmsg die mit dem physical Namen beginnen
$hash->{$k} = $v if $k =~ /^$pname/ ;
}
my $sval;
if ($clientmsg->{direction} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") {
readingsBeginUpdate($hash);
if ($clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received})) { # =~ m/^(0x|)[0-9A-F]{1,2}$/xi) {
foreach (0..7) {
#$sval = hex($clientmsg->{received}) & (1 << $_);
$sval = $clientmsg->{received} & (1 << $_);
$sval = $sval == 0 ? "off" :"on";
readingsBulkUpdate($hash, 'Port'.$_ , $sval) if (ReadingsVal($name,'Port'.$_,"") ne $sval);
}
readingsBulkUpdate($hash, 'state', $clientmsg->{received});
} elsif ($clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data})) { # =~ m/^(0x|)[0-9A-F]{1,2}$/xi) {
my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
foreach (0..7) {
#$sval = hex($clientmsg->{data}) & (1 << $_);
$sval = $clientmsg->{data} & (1 << $_);
$sval = $sval == 0 ? "off" :"on";
readingsBulkUpdate($hash, 'Port'.$_ , $sval) unless (ReadingsVal($name,'Port'.$_,"") eq $sval || $_ ~~ @inports );
}
readingsBulkUpdate($hash, 'state', $clientmsg->{data});
}
#readingsBulkUpdate($hash, 'state', join(" ", $clientmsg->{received}));
readingsEndUpdate($hash, 1);
}
}
###################################
sub I2C_PCF8574_Parse($$) { #wird über dispatch vom physical device aufgerufen (dispatch wird im mom nicht verwendet)
my ($hash, $msg) = @_;
my($sid, $addr, @msg) = split(/ /,$msg);
#Log3 $hash, 4, "Vom Netzerparse $hash->{NAME}: sid: $sid, Msg: @msg";
my $def = $modules{I2C_PCF8574}{defptr}{"$sid $addr"};
if($def) {
my @list;
foreach my $n (keys %{ $def }) {
my $lh = $def->{$n}; # Hash bekommen
$n = $lh->{NAME}; # It may be renamed
return "" if(IsIgnored($n)); # Little strange.
################################################
my $cde = join(" ",@msg);
my $sval;
readingsBeginUpdate($lh);
if ( int(@msg) == 1) {
for (my $i = 0; $i < 8; $i++) {
#$sval = hex($cde) & (1 << $i);
$sval = $cde & (1 << $i);
$sval = $sval == 0 ? "0" :"1";
readingsBulkUpdate($lh, 'P'.$i , $sval) if (ReadingsVal($n,'P'.$i,2) ne $sval);
}
}
readingsBulkUpdate($lh, 'state', join(" ", @msg));
readingsEndUpdate($lh, 1);
################################################
Log3 $n, 4, "I2C_PCF8574 $n $cde";
push(@list, $n);
}
return @list;
} else {
Log3 $hash, 3, "I2C_PCF8574 Unknown device $addr Id $sid";
#return "UNDEFINED I2C_PCF8574_$addr$sid I2C_PCF8574 $addr $sid";
}
}
###################################
1;
=pod
=begin html
<a name="I2C_PCF8574"></a>
<h3>I2C_PCF8574</h3>
<ul>
<a name="I2C_PCF8574"></a>
Provides an interface to the PCA9532 8 channel port extender IC. On Raspberry Pi the Interrupt Pin can be connected to an GPIO and <a href="#RPI_GPIO">RPI_GPIO</a> can be used to get the port values if an interrupt occurs.<br>
The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
<b>attribute IODev must be set</b><br>
<a name="I2C_PCF8574Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_PCF8574 &lt;I2C Address&gt;</code><br>
where <code>&lt;I2C Address&gt;</code> is an 2 digit hexadecimal value<br>
</ul>
<a name="I2C_PCF8574Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
<ul>
<li><code>&lt;port&gt;</code> is one of Port0 to Port7 and <code>&lt;value&gt;</code> is one of:<br>
<ul>
<code>
off<br>
on<br>
</code>
</ul>
</li>
</ul>
<br>
Example:
<ul>
<code>set mod1 Port4 on</code><br>
</ul><br>
</ul>
<a name="I2C_PCF8574Get"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt;</code>
<br><br>
refreshes all readings
</ul><br>
<a name="I2C_PCF8574Attr"></a>
<b>Attributes</b>
<ul>
<li>poll_interval<br>
Set the polling interval in minutes to query the GPIO's level<br>
Default: -, valid values: decimal number<br><br>
</li>
<li>InputPorts<br>
Space separated list of Portnumers that are used as Inputs<br>
Ports in this list can't be written<br>
Default: no, valid values: 0 1 2 .. 7<br><br>
</li>
<li><a href="#IODev">IODev</a></li>
<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="I2C_PCF8574"></a>
<h3>I2C_PCF8574</h3>
<ul>
<a name="I2C_PCF8574"></a>
Erm&ouml;glicht die Verwendung eines PCF8574 I2C 8 Bit Portexenders.
Auf einem Raspberry Pi kann der Interrupt Pin des PCF8574 mit einem GPIO verbunden werden und &uml;ber die Interrupt Funktionen von <a href="#RPI_GPIO">RPI_GPIO</a> l&aml;sst sich dann ein get für den PCF8574 bei Pegel&aml;nderung ausl&oml;sen.<br>
I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
<b>Das Attribut IODev muss definiert sein.</b><br>
<a name="I2C_PCF8574Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_PCF8574 &lt;I2C Address&gt;</code><br>
Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert<br>
</ul>
<a name="I2C_PCF8574Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
<ul>
<li><code>&lt;port&gt;</code> kann Port0 bis Port7 annehmen und <code>&lt;value&gt;</code> folgende Werte:<br>
<ul>
<code>
off<br>
on<br>
</code>
</ul>
</li>
</ul>
<br>
Beispiel:
<ul>
<code>set mod1 Port4 on</code><br>
</ul><br>
</ul>
<a name="I2C_PCF8574Get"></a>
<b>Get</b>
<ul>
<code>get &lt;name&gt;</code>
<br><br>
Aktualisierung aller Werte
</ul><br>
<a name="I2C_PCF8574Attr"></a>
<b>Attribute</b>
<ul>
<li>poll_interval<br>
Aktualisierungsintervall aller Werte in Minuten.<br>
Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br>
</li>
<li>InputPorts<br>
Durch Leerzeichen getrennte Portnummern die als Inputs genutzt werden.<br>
Ports in dieser Liste können nicht geschrieben werden.<br>
Standard: no, g&uuml;ltige Werte: 0 1 2 .. 15<br><br>
</li>
<li><a href="#IODev">IODev</a></li>
<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
=cut

358
fhem/FHEM/52_I2C_SHT21.pm Normal file
View File

@ -0,0 +1,358 @@
##############################################
# $Id: 52_I2C_SHT21.pm 3764 2014-01-22 07:09:38Z klausw $
package main;
use strict;
use warnings;
use Time::HiRes qw(usleep);
use Scalar::Util qw(looks_like_number);
#use Error qw(:try);
use constant {
SHT21_I2C_ADDRESS => '0x40',
};
##################################################
# Forward declarations
#
sub I2C_SHT21_Initialize($);
sub I2C_SHT21_Define($$);
sub I2C_SHT21_Attr(@);
sub I2C_SHT21_Poll($);
sub I2C_SHT21_Set($@);
sub I2C_SHT21_Undef($$);
my %sets = (
'readValues' => 1,
);
sub I2C_SHT21_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'I2C_SHT21_Define';
$hash->{InitFn} = 'I2C_SHT21_Init';
$hash->{AttrFn} = 'I2C_SHT21_Attr';
$hash->{SetFn} = 'I2C_SHT21_Set';
$hash->{UndefFn} = 'I2C_SHT21_Undef';
$hash->{I2CRecFn} = 'I2C_SHT21_I2CRec';
$hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 poll_interval:1,2,5,10,20,30 ' .
'roundHumidityDecimal:0,1,2 roundTemperatureDecimal:0,1,2 ' .
$readingFnAttributes;
}
sub I2C_SHT21_Define($$) {
my ($hash, $def) = @_;
my @a = split('[ \t][ \t]*', $def);
$hash->{STATE} = "defined";
if ($main::init_done) {
eval { I2C_SHT21_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_SHT21_Catch($@) if $@;
}
return undef;
}
sub I2C_SHT21_Init($$) {
my ( $hash, $args ) = @_;
my $name = $hash->{NAME};
if (defined $args && int(@$args) > 1)
{
return "Define: Wrong syntax. Usage:\n" .
"define <name> I2C_SHT21 [<i2caddress>]";
}
if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
return "$name I2C Address not valid" unless ($address < 128 && $address > 3);
} else {
$hash->{I2C_Address} = hex(SHT21_I2C_ADDRESS);
}
my $msg = '';
# create default attributes
$msg = CommandAttr(undef, $name . ' poll_interval 5');
if ($msg) {
Log3 ($hash, 1, $msg);
return $msg;
}
AssignIoPort($hash);
$hash->{STATE} = 'Initialized';
# my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
# $sendpackage{reg} = hex("AA");
# $sendpackage{nbyte} = 22;
# return "$name: no IO device defined" unless ($hash->{IODev});
# my $phash = $hash->{IODev};
# my $pname = $phash->{NAME};
# CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
return undef;
}
sub I2C_SHT21_Catch($) {
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
sub I2C_SHT21_Attr (@) {# hier noch Werteüberprüfung einfügen
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if ($attr eq 'poll_interval') {
#my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
if ($val > 0) {
RemoveInternalTimer($hash);
InternalTimer(1, 'I2C_SHT21_Poll', $hash, 0);
} else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
}
} elsif ($attr eq 'roundHumidityDecimal') {
$msg = 'Wrong $attr defined. Use one of 0, 1, 2' if defined($val) && $val >= 0 && $val <= 2 ;
} elsif ($attr eq 'roundTemperatureDecimal') {
$msg = 'Wrong $attr defined. Use one of 0, 1, 2' if defined($val) && $val >= 0 && $val <= 2 ;
}
return ($msg) ? $msg : undef;
}
sub I2C_SHT21_Poll($) {
my ($hash) = @_;
my $name = $hash->{NAME};
# Read values
I2C_SHT21_Set($hash, ($name, 'readValues'));
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_SHT21_Poll', $hash, 0);
}
}
sub I2C_SHT21_Set($@) {
my ($hash, @a) = @_;
my $name = $a[0];
my $cmd = $a[1];
if(!defined($sets{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
}
if ($cmd eq 'readValues') {
I2C_SHT21_readTemperature($hash);
I2C_SHT21_readHumidity($hash);
}
}
sub I2C_SHT21_Undef($$) {
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
return undef;
}
sub I2C_SHT21_I2CRec ($$) {
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals für alle Keys in $clientmsg die mit dem physical Namen beginnen
$hash->{$k} = $v if $k =~ /^$pname/ ;
}
if ($clientmsg->{direction} && $clientmsg->{type} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") {
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
Log3 $hash, 5, "empfangen: $clientmsg->{received}";
I2C_SHT21_GetTemp ($hash, $clientmsg->{received}) if $clientmsg->{type} eq "temp" && $clientmsg->{nbyte} == 2;
I2C_SHT21_GetHum ($hash, $clientmsg->{received}) if $clientmsg->{type} eq "hum" && $clientmsg->{nbyte} == 2;
}
}
}
sub I2C_SHT21_GetTemp ($$) {
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my $temperature = $raw[0] << 8 | $raw[1];
$temperature = ( 175.72 * $temperature / 2**16 ) - 46.85;
$temperature = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
$temperature
);
readingsSingleUpdate($hash,"temperature", $temperature, 1);
}
sub I2C_SHT21_GetHum ($$) {
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my $name = $hash->{NAME};
my $temperature = ReadingsVal($name,"temperature","0");
my $humidity = $raw[0] << 8 | $raw[1];
$humidity = ( 125 * $humidity / 2**16 ) - 6;
$humidity = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundHumidityDecimal', 1) . 'f',
$humidity
);
readingsBeginUpdate($hash);
readingsBulkUpdate(
$hash,
'state',
'T: ' . $temperature . ' H: ' . $humidity
);
#readingsBulkUpdate($hash, 'temperature', $temperature);
readingsBulkUpdate($hash, 'humidity', $humidity);
readingsEndUpdate($hash, 1);
}
sub I2C_SHT21_readTemperature($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
# Write 0xF3 to device. This requests a temperature reading
my $i2creq = { i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" };
$i2creq->{data} = hex("F3");
CallFn($pname, "I2CWrtFn", $phash, $i2creq);
usleep(85000); #für 14bit
# Read the two byte result from device
my $i2cread = { i2caddress => $hash->{I2C_Address}, direction => "i2cread" };
$i2cread->{nbyte} = 2;
$i2cread->{type} = "temp";
CallFn($pname, "I2CWrtFn", $phash, $i2cread);
return;
}
sub I2C_SHT21_readHumidity($) {
my ($hash) = @_;
my $name = $hash->{NAME};
return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev};
my $pname = $phash->{NAME};
# Write 0xF5 to device. This requests a humidity reading
my $i2creq = { i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" };
$i2creq->{data} = hex("F5");
CallFn($pname, "I2CWrtFn", $phash, $i2creq);
usleep(39000); #für 12bit
# Read the two byte result from device
my $i2cread = { i2caddress => $hash->{I2C_Address}, direction => "i2cread" };
$i2cread->{nbyte} = 2;
$i2cread->{type} = "hum";
CallFn($pname, "I2CWrtFn", $phash, $i2cread);
return; # $retVal;
}
1;
=pod
=begin html
<a name="I2C_SHT21"></a>
<h3>I2C_SHT21</h3>
<ul>
<a name="I2C_SHT21"></a>
Provides an interface to the SHT21 I2C Humidity sensor from <a href="www.sensirion.com">Sensirion</a>.
The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
<b>attribute IODev must be set</b><br>
<a name="I2C_SHT21Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_SHT21 [&lt;I2C Address&gt;]</code><br>
where <code>&lt;I2C Address&gt;</code> is an 2 digit hexadecimal value<br>
</ul>
<a name="I2C_SHT21Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; readValues</code><br>
Reads the current temperature and humidity values from sensor.<br><br>
</ul>
<a name="I2C_SHT21Attr"></a>
<b>Attributes</b>
<ul>
<li>poll_interval<br>
Set the polling interval in minutes to query data from sensor<br>
Default: 5, valid values: 1,2,5,10,20,30<br><br>
</li>
<li>roundHumidityDecimal<br>
Number of decimal places for humidity value<br>
Default: 1, valid values: 0 1 2<br><br>
</li>
<li>roundTemperatureDecimal<br>
Number of decimal places for temperature value<br>
Default: 1, valid values: 0,1,2<br><br>
</li>
<li><a href="#IODev">IODev</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="I2C_SHT21"></a>
<h3>I2C_SHT21</h3>
<ul>
<a name="I2C_SHT21"></a>
Erm&ouml;glicht die Verwendung eines SHT21 I2C Feuchtesensors von <a href="www.sensirion.com">Sensirion</a>.
I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
<b>Das Attribut IODev muss definiert sein.</b><br>
<a name="I2C_SHT21Define"></a><br>
<b>Define</b>
<ul>
<code>define &lt;name&gt; I2C_SHT21 [&lt;I2C Address&gt;]</code><br>
Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert<br>
</ul>
<a name="I2C_SHT21Set"></a>
<b>Set</b>
<ul>
<code>set &lt;name&gt; readValues</code><br>
Aktuelle Temperatur und Feuchte Werte vom Sensor lesen.<br><br>
</ul>
<a name="I2C_SHT21Attr"></a>
<b>Attribute</b>
<ul>
<li>poll_interval<br>
Aktualisierungsintervall aller Werte in Minuten.<br>
Standard: 5, g&uuml;ltige Werte: 1,2,5,10,20,30<br><br>
</li>
<li>roundHumidityDecimal<br>
Anzahl Dezimalstellen für den Feuchtewert<br>
Standard: 1, g&uuml;ltige Werte: 0 1 2<br><br>
</li>
<li>roundTemperatureDecimal<br>
Anzahl Dezimalstellen für den Temperaturwert<br>
Standard: 1, g&uuml;ltige Werte: 0,1,2<br><br>
</li>
<li><a href="#IODev">IODev</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
=cut