| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- ##############################################################################
- # $Id: 52_I2C_EEPROM.pm 12059 2016-08-22 21:14:59Z klauswitt $
- ##############################################################################
- # Modul for I2C EEPROM
- #
- # contributed by Klaus Wittstock (2013) email: klauswittstock bei gmail
- ##############################################################################
- package main;
- use strict;
- use warnings;
- use SetExtensions;
- use Scalar::Util qw(looks_like_number);
- my %setsP = (
- 'byte' => 0,
- 'bit' => 1,
- 'word' => 2,
- 'dword' => 3,
- 'qword' => 4,
- );
- my $sets = "byte bit word dword qword";
- ###############################################################################
- sub I2C_EEPROM_Initialize($) {
- my ($hash) = @_;
- $hash->{DefFn} = "I2C_EEPROM_Define";
- $hash->{InitFn} = 'I2C_EEPROM_Init';
- $hash->{UndefFn} = "I2C_EEPROM_Undefine";
- $hash->{AttrFn} = "I2C_EEPROM_Attr";
- $hash->{SetFn} = "I2C_EEPROM_Set";
- $hash->{GetFn} = "I2C_EEPROM_Get";
- $hash->{I2CRecFn} = "I2C_EEPROM_I2CRec";
- $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
- "EEPROM_size:2k,128 poll_interval ".
- "$readingFnAttributes";
- }
- ###############################################################################
- sub I2C_EEPROM_Define($$) {
- my ($hash, $def) = @_;
- my @a = split("[ \t]+", $def);
- $hash->{STATE} = 'defined';
- if ($main::init_done) {
- eval { I2C_EEPROM_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
- return I2C_EEPROM_Catch($@) if $@;
- }
- return undef;
- }
- ###############################################################################
- sub I2C_EEPROM_Init($$) { #Geraet beim anlegen/booten/nach Neuverbindung (wieder) initialisieren
- my ( $hash, $args ) = @_;
- if (defined $args && int(@$args) != 1) {
- return "Define: Wrong syntax. Usage:\n" .
- "define <name> I2C_EEPROM <i2caddress>";
- }
- if (defined (my $address = shift @$args)) {
- $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
- } else {
- return "$hash->{NAME} I2C Address not valid";
- }
- AssignIoPort($hash);
- I2C_EEPROM_Get($hash, $hash->{NAME});
- $hash->{STATE} = 'Initialized';
- return undef;
- }
- ###############################################################################
- sub I2C_EEPROM_Catch($) { #Fehlermeldung von eval formattieren
- my $exception = shift;
- if ($exception) {
- $exception =~ /^(.*)( at.*FHEM.*)$/;
- return $1;
- }
- return undef;
- }
- ###############################################################################
- sub I2C_EEPROM_Undefine($$) {
- my ($hash, $arg) = @_;
- if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
- RemoveInternalTimer($hash);
- }
- }
- ###############################################################################
- sub I2C_EEPROM_Attr(@) {
- my ($command, $name, $attr, $val) = @_;
- my $hash = $defs{$name};
- my $msg = '';
- if ($command && $command eq "set" && $attr && $attr eq "IODev") {
- if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
- main::AssignIoPort($hash,$val);
- my @def = split (' ',$hash->{DEF});
- I2C_EEPROM_Init($hash,\@def) if (defined ($hash->{IODev}));
- }
- }
- 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_EEPROM_Poll', $hash, 0);
- } else {
- $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
- }
- }
- return ($msg) ? $msg : undef;
- }
- ###############################################################################
- sub I2C_EEPROM_Poll($) { #function for refresh intervall
- my ($hash) = @_;
- my $name = $hash->{NAME};
- # Read values
- I2C_EEPROM_Get($hash, $name);
- my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
- if ($pollInterval > 0) {
- InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_EEPROM_Poll', $hash, 0);
- }
- }
- ###############################################################################
- sub I2C_EEPROM_Set($@) {
- my ($hash, @a) = @_;
- my $name =$a[0];
- my $cmd = $a[1];
- my $val = $a[2];
- my $msg = undef;
-
- my $setList = " ";
- return "Unknown argument, choose one of $setList" if(defined($a[1]) && $a[1] eq '?');
-
- if (@a > 2) {
- if (@a == 4) {
- if ($a[2] =~ m/^(B|b)(it|)((0|)[0-7])$/i) {
- my $bit = $a[2];
- $bit =~ tr/(B|b)(it|)//d; #Nummer aus String extrahieren
- $bit = $bit =~ /^0.*$/ ? oct($bit) : $bit;
- my $val = hex( I2C_EEPROM_BytefromReading($hash, $cmd) );
- my $mask = 1 << $bit;
- if ($a[3] eq "1") {
- $val |= $mask; # set bit
- } else {
- $val &= ~$mask; # clear bit
- }
- } else {
- return "Unknown argument $a[2] use \"set <register> [Bit<bitnumber>] <value>\" where <bitnumber> is 0..7 and value is 0..255 (or 0|1 if you use Bit)";
- }
- }
- $val = $val =~ /^0.*$/ ? oct($val) : $val;
- $cmd = $cmd =~ /^0.*$/ ? oct($cmd) : $cmd;
- if (looks_like_number($cmd)) {
- my $nbyte = ( (AttrVal($hash->{NAME}, "EEPROM_size", "128") eq "2k") ? 256 : 16 );
- if ($nbyte > $cmd ) {
- $msg = I2C_EEPROM_SetReg($hash, $cmd, $val);
- } else {
- $msg = "$name error: $cmd is outside of address range (". $nbyte - 1 .")";
- }
- }
- }
- return ($msg) ? $msg : undef;
- }
- ###############################################################################
- sub I2C_EEPROM_Get($@) {
- my ($hash, @a) = @_;
- my $name =$a[0];
- my $nbyte = ( (AttrVal($hash->{NAME}, "EEPROM_size", "128") eq "2k") ? 256 : 16 );
- my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
- $sendpackage{reg} = 0;
- $sendpackage{nbyte} = $nbyte;
- return "$name: no IO device defined" unless ($hash->{IODev});
- my $phash = $hash->{IODev};
- my $pname = $phash->{NAME};
- CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
-
- my $setList = " ";
- return "Unknown argument, choose one of $setList" if(defined($a[1]) && $a[1] eq '?');
-
- if ( defined $a[1]) {
- $a[1] = $a[1] =~ /^0.*$/ ? oct($a[1]) : $a[1];
- if (looks_like_number($a[1]) ) {
- return "$name error: $a[1] is outside of address range (". $nbyte - 1 .")" unless ($nbyte > $a[1] );
-
- my $num = (defined $a[2] && $a[2] =~ m/^(dec|bin|hex)$/i) ? $a[2] : undef;
-
- my $rbyte = I2C_EEPROM_BytefromReading($hash, $a[1], $num);
- if ( defined $a[2] && $a[2] !~ m/^(dec|bin|hex)$/i ) {
- if ($a[2] =~ m/^b(it|)((0|)[0-7])$/i){
- $a[2] =~ tr/(B|b)(it|)//d; #Nummer aus String extrahieren
- $rbyte = (( hex($rbyte) >> $a[2] ) & 1) == 1 ? 1 : "0 " ;
- }else {
- return "$name error: $a[2] is outside of range (Bit0..Bit7)";
- }
- }
- return $rbyte;
- }
- }
-
- }
- ###############################################################################
- sub I2C_EEPROM_I2CRec($@) { #ueber 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 fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
- $hash->{$k} = $v if $k =~ /^$pname/ ;
- }
- if ($clientmsg->{direction} && defined $clientmsg->{reg} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) {
- if ($clientmsg->{direction} eq "i2cread" && $clientmsg->{received}) {
- my @rec = split(" ",$clientmsg->{received});
- Log3 $hash, 3, "$name: wrong amount of registers transmitted from $pname" unless (@rec == $clientmsg->{nbyte});
- foreach (reverse 0..$#rec) {
- I2C_EEPROM_UpdReadings($hash, $_ + $clientmsg->{reg} , $rec[$_]);
- }
- readingsSingleUpdate($hash,"state", "Ok", 1);
- } elsif ($clientmsg->{direction} eq "i2cwrite" && defined $clientmsg->{data}) { #readings aktualisieren wenn uebertragung ok
- I2C_EEPROM_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
- readingsSingleUpdate($hash,"state", "Ok", 1);
- } else {
- readingsSingleUpdate($hash,"state", "transmission error", 1);
- Log3 $hash, 3, "$name: failurei in message from $pname";
- Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
- (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
- (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
- (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
- (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $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: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
- (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
- (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
- (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
- }
- }
- ###############################################################################
- sub I2C_EEPROM_UpdReadings($$$) { #nach Rueckmeldung readings updaten (ueber I2CRec aufgerufen)
- my ($hash, $reg, $inh) = @_;
- my $name = $hash->{NAME};
- Log3 $hash, 5, "$name UpdReadings Register: $reg, Inhalt: $inh";
- my $regb = $reg >> 4;
- my $regp = $reg & 15;
- my $bank = ReadingsVal($name,"0x".sprintf("%02X",$regb)."x",".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..");
- my $nbank = $bank;
- substr($nbank,$regp * 3,2,sprintf("%02X",$inh));
- if ($nbank ne $bank) { #bei Aenderung
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "0x".sprintf("%02X",$regb)."x" , $nbank);
- readingsEndUpdate($hash, 1);
- }
- return;
- }
- ###############################################################################
- sub I2C_EEPROM_SetReg { #set register
- my ($hash, $reg, $inh) = @_;
-
- if (defined (my $iodev = $hash->{IODev})) {
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cwrite",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- data => $inh,
- }) if (defined $hash->{I2C_Address});
- } else {
- return "no IODev assigned to '$hash->{NAME}'";
- }
- }
- ###############################################################################
- sub I2C_EEPROM_BytefromReading($@) {
- my ($hash, $reg, $num) = @_;
- #$num = "hex" unless defined $num ;
- my $regb = $reg >> 4;
- my $regp = $reg & 15;
- my $bank = ReadingsVal($hash->{NAME},"0x".sprintf("%02X",$regb)."x",".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..");
- if ($num eq 'dec') {
- return hex(substr($bank,$regp * 3,2));
- } elsif ($num eq 'bin') {
- return sprintf ('0b%08b', hex(substr($bank,$regp * 3,2)));
- } else {
- return "0x" . substr($bank,$regp * 3,2);
- }
-
- }
- 1;
- =pod
- =item device
- =item summary reads the content from an via I2C connected EEPROM
- =item summary_DE lesen des Inhals eines über I2C angeschlossenen EEPROM
- =begin html
- <a name="I2C_EEPROM"></a>
- <h3>I2C_EEPROM</h3>
- <ul>
- <a name="I2C_EEPROM"></a>
- Provides an interface to an I2C EEPROM.<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_EEPROMDefine"></a><br>
- <b>Define</b>
- <ul>
- <code>define <name> I2C_EEPROM <I2C Address></code><br>
- where <code><I2C Address></code> is without direction bit<br>
- </ul>
- <a name="I2C_EEPROMSet"></a>
- <b>Set</b>
- <ul>
- <code>set <name> <byte address> <value></code><br><br>
- where <code><byte address></code> is a number (0..device specific) and <code><value></code> is a number (0..255)<br>
- both numbers can be written in decimal or hexadecimal notation.<br>
- <br>
- Example:
- <ul>
- <code>set eeprom1 0x02 0xAA</code><br>
- <code>set eeprom1 2 170</code><br>
- </ul><br>
- </ul>
- <a name="I2C_EEPROMGet"></a>
- <b>Get</b>
- <ul>
- <code>get <name></code>
- <br><br>
- refreshes all readings
- </ul><br>
- <ul>
- <code>get <name> <byte address> [Bit<bitnumber(0..7)>]</code>
- <br><br>
- returnes actual reading of stated <byte address> or a single bit of <byte address><br>
- Values are readout from readings, NOT from device!
- </ul><br>
-
- <a name="I2C_EEPROMAttr"></a>
- <b>Attributes</b>
- <ul>
- <li>poll_interval<br>
- Set the polling interval in minutes to query the EEPROM content<br>
- Default: -, valid values: decimal number<br><br>
- </li>
- <li>EEPROM_size<br>
- Sets the storage size of the EEPROM<br>
- Default: 128, valid values: 128 (128bit), 2k (2048bit)<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_EEPROM"></a>
- <h3>I2C_EEPROM</h3>
- <ul>
- <a name="I2C_EEPROM"></a>
- Ermöglicht die Verwendung I2C EEPROM.
- I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</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_EEPROMDefine"></a><br>
- <b>Define</b>
- <ul>
- <code>define <name> I2C_EEPROM <I2C Address></code><br>
- Der Wert <code><I2C Address></code> ist ohne das Richtungsbit
- </ul>
- <a name="I2C_EEPROMSet"></a>
- <b>Set</b>
- <ul>
- <code>set <name> <byte address> <value></code><br><br>
- <code><byte address></code> ist die Registeradresse (0..IC abhängig) und <code><value></code> der Registerinhalt (0..255)<br>
- Beide Zahlen können sowohl eine Dezimal- als auch eine Hexadezimalzahl sein.<br>
- <br>
- Beispiel:
- <ul>
- <code>set eeprom1 0x02 0xAA</code><br>
- <code>set eeprom1 2 170</code><br>
- </ul><br>
- </ul>
- <a name="I2C_EEPROMGet"></a>
- <b>Get</b>
- <ul>
- <code>get <name></code>
- <br><br>
- Aktualisierung aller Werte
- </ul><br>
- <ul>
- <code>get <name> <byte address> [Bit<bitnumber(0..7)>]</code>
- <br><br>
- Gibt den Inhalt des in <byte address> angegebenen Registers zurück, bzw. ein einzelnes Bit davon.<br>
- Achtung mit diesem Befehl werden nur die Werte aus den Readings angezeigt und nicht der Registerinhalt selbst!
- </ul><br>
- <a name="I2C_EEPROMAttr"></a>
- <b>Attribute</b>
- <ul>
- <li>poll_interval<br>
- Aktualisierungsintervall aller Werte in Minuten.<br>
- Standard: -, gültige Werte: Dezimalzahl<br><br>
- </li>
- <li>EEPROM_size<br>
- Speichergröße des EEPROM<br>
- Standard: 128, gültige Werte: 128 (128bit), 2k (2048bit)<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
|