52_I2C_EEPROM.pm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. ##############################################################################
  2. # $Id: 52_I2C_EEPROM.pm 12059 2016-08-22 21:14:59Z klauswitt $
  3. ##############################################################################
  4. # Modul for I2C EEPROM
  5. #
  6. # contributed by Klaus Wittstock (2013) email: klauswittstock bei gmail
  7. ##############################################################################
  8. package main;
  9. use strict;
  10. use warnings;
  11. use SetExtensions;
  12. use Scalar::Util qw(looks_like_number);
  13. my %setsP = (
  14. 'byte' => 0,
  15. 'bit' => 1,
  16. 'word' => 2,
  17. 'dword' => 3,
  18. 'qword' => 4,
  19. );
  20. my $sets = "byte bit word dword qword";
  21. ###############################################################################
  22. sub I2C_EEPROM_Initialize($) {
  23. my ($hash) = @_;
  24. $hash->{DefFn} = "I2C_EEPROM_Define";
  25. $hash->{InitFn} = 'I2C_EEPROM_Init';
  26. $hash->{UndefFn} = "I2C_EEPROM_Undefine";
  27. $hash->{AttrFn} = "I2C_EEPROM_Attr";
  28. $hash->{SetFn} = "I2C_EEPROM_Set";
  29. $hash->{GetFn} = "I2C_EEPROM_Get";
  30. $hash->{I2CRecFn} = "I2C_EEPROM_I2CRec";
  31. $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
  32. "EEPROM_size:2k,128 poll_interval ".
  33. "$readingFnAttributes";
  34. }
  35. ###############################################################################
  36. sub I2C_EEPROM_Define($$) {
  37. my ($hash, $def) = @_;
  38. my @a = split("[ \t]+", $def);
  39. $hash->{STATE} = 'defined';
  40. if ($main::init_done) {
  41. eval { I2C_EEPROM_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
  42. return I2C_EEPROM_Catch($@) if $@;
  43. }
  44. return undef;
  45. }
  46. ###############################################################################
  47. sub I2C_EEPROM_Init($$) { #Geraet beim anlegen/booten/nach Neuverbindung (wieder) initialisieren
  48. my ( $hash, $args ) = @_;
  49. if (defined $args && int(@$args) != 1) {
  50. return "Define: Wrong syntax. Usage:\n" .
  51. "define <name> I2C_EEPROM <i2caddress>";
  52. }
  53. if (defined (my $address = shift @$args)) {
  54. $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
  55. } else {
  56. return "$hash->{NAME} I2C Address not valid";
  57. }
  58. AssignIoPort($hash);
  59. I2C_EEPROM_Get($hash, $hash->{NAME});
  60. $hash->{STATE} = 'Initialized';
  61. return undef;
  62. }
  63. ###############################################################################
  64. sub I2C_EEPROM_Catch($) { #Fehlermeldung von eval formattieren
  65. my $exception = shift;
  66. if ($exception) {
  67. $exception =~ /^(.*)( at.*FHEM.*)$/;
  68. return $1;
  69. }
  70. return undef;
  71. }
  72. ###############################################################################
  73. sub I2C_EEPROM_Undefine($$) {
  74. my ($hash, $arg) = @_;
  75. if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
  76. RemoveInternalTimer($hash);
  77. }
  78. }
  79. ###############################################################################
  80. sub I2C_EEPROM_Attr(@) {
  81. my ($command, $name, $attr, $val) = @_;
  82. my $hash = $defs{$name};
  83. my $msg = '';
  84. if ($command && $command eq "set" && $attr && $attr eq "IODev") {
  85. if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
  86. main::AssignIoPort($hash,$val);
  87. my @def = split (' ',$hash->{DEF});
  88. I2C_EEPROM_Init($hash,\@def) if (defined ($hash->{IODev}));
  89. }
  90. }
  91. if ($attr && $attr eq 'poll_interval') {
  92. #my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
  93. if (!defined($val) ) {
  94. RemoveInternalTimer($hash);
  95. } elsif ($val > 0) {
  96. RemoveInternalTimer($hash);
  97. InternalTimer(1, 'I2C_EEPROM_Poll', $hash, 0);
  98. } else {
  99. $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
  100. }
  101. }
  102. return ($msg) ? $msg : undef;
  103. }
  104. ###############################################################################
  105. sub I2C_EEPROM_Poll($) { #function for refresh intervall
  106. my ($hash) = @_;
  107. my $name = $hash->{NAME};
  108. # Read values
  109. I2C_EEPROM_Get($hash, $name);
  110. my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
  111. if ($pollInterval > 0) {
  112. InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_EEPROM_Poll', $hash, 0);
  113. }
  114. }
  115. ###############################################################################
  116. sub I2C_EEPROM_Set($@) {
  117. my ($hash, @a) = @_;
  118. my $name =$a[0];
  119. my $cmd = $a[1];
  120. my $val = $a[2];
  121. my $msg = undef;
  122. my $setList = " ";
  123. return "Unknown argument, choose one of $setList" if(defined($a[1]) && $a[1] eq '?');
  124. if (@a > 2) {
  125. if (@a == 4) {
  126. if ($a[2] =~ m/^(B|b)(it|)((0|)[0-7])$/i) {
  127. my $bit = $a[2];
  128. $bit =~ tr/(B|b)(it|)//d; #Nummer aus String extrahieren
  129. $bit = $bit =~ /^0.*$/ ? oct($bit) : $bit;
  130. my $val = hex( I2C_EEPROM_BytefromReading($hash, $cmd) );
  131. my $mask = 1 << $bit;
  132. if ($a[3] eq "1") {
  133. $val |= $mask; # set bit
  134. } else {
  135. $val &= ~$mask; # clear bit
  136. }
  137. } else {
  138. 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)";
  139. }
  140. }
  141. $val = $val =~ /^0.*$/ ? oct($val) : $val;
  142. $cmd = $cmd =~ /^0.*$/ ? oct($cmd) : $cmd;
  143. if (looks_like_number($cmd)) {
  144. my $nbyte = ( (AttrVal($hash->{NAME}, "EEPROM_size", "128") eq "2k") ? 256 : 16 );
  145. if ($nbyte > $cmd ) {
  146. $msg = I2C_EEPROM_SetReg($hash, $cmd, $val);
  147. } else {
  148. $msg = "$name error: $cmd is outside of address range (". $nbyte - 1 .")";
  149. }
  150. }
  151. }
  152. return ($msg) ? $msg : undef;
  153. }
  154. ###############################################################################
  155. sub I2C_EEPROM_Get($@) {
  156. my ($hash, @a) = @_;
  157. my $name =$a[0];
  158. my $nbyte = ( (AttrVal($hash->{NAME}, "EEPROM_size", "128") eq "2k") ? 256 : 16 );
  159. my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
  160. $sendpackage{reg} = 0;
  161. $sendpackage{nbyte} = $nbyte;
  162. return "$name: no IO device defined" unless ($hash->{IODev});
  163. my $phash = $hash->{IODev};
  164. my $pname = $phash->{NAME};
  165. CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
  166. my $setList = " ";
  167. return "Unknown argument, choose one of $setList" if(defined($a[1]) && $a[1] eq '?');
  168. if ( defined $a[1]) {
  169. $a[1] = $a[1] =~ /^0.*$/ ? oct($a[1]) : $a[1];
  170. if (looks_like_number($a[1]) ) {
  171. return "$name error: $a[1] is outside of address range (". $nbyte - 1 .")" unless ($nbyte > $a[1] );
  172. my $num = (defined $a[2] && $a[2] =~ m/^(dec|bin|hex)$/i) ? $a[2] : undef;
  173. my $rbyte = I2C_EEPROM_BytefromReading($hash, $a[1], $num);
  174. if ( defined $a[2] && $a[2] !~ m/^(dec|bin|hex)$/i ) {
  175. if ($a[2] =~ m/^b(it|)((0|)[0-7])$/i){
  176. $a[2] =~ tr/(B|b)(it|)//d; #Nummer aus String extrahieren
  177. $rbyte = (( hex($rbyte) >> $a[2] ) & 1) == 1 ? 1 : "0 " ;
  178. }else {
  179. return "$name error: $a[2] is outside of range (Bit0..Bit7)";
  180. }
  181. }
  182. return $rbyte;
  183. }
  184. }
  185. }
  186. ###############################################################################
  187. sub I2C_EEPROM_I2CRec($@) { #ueber CallFn vom physical aufgerufen
  188. my ($hash, $clientmsg) = @_;
  189. my $name = $hash->{NAME};
  190. my $phash = $hash->{IODev};
  191. my $pname = $phash->{NAME};
  192. while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
  193. $hash->{$k} = $v if $k =~ /^$pname/ ;
  194. }
  195. if ($clientmsg->{direction} && defined $clientmsg->{reg} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) {
  196. if ($clientmsg->{direction} eq "i2cread" && $clientmsg->{received}) {
  197. my @rec = split(" ",$clientmsg->{received});
  198. Log3 $hash, 3, "$name: wrong amount of registers transmitted from $pname" unless (@rec == $clientmsg->{nbyte});
  199. foreach (reverse 0..$#rec) {
  200. I2C_EEPROM_UpdReadings($hash, $_ + $clientmsg->{reg} , $rec[$_]);
  201. }
  202. readingsSingleUpdate($hash,"state", "Ok", 1);
  203. } elsif ($clientmsg->{direction} eq "i2cwrite" && defined $clientmsg->{data}) { #readings aktualisieren wenn uebertragung ok
  204. I2C_EEPROM_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
  205. readingsSingleUpdate($hash,"state", "Ok", 1);
  206. } else {
  207. readingsSingleUpdate($hash,"state", "transmission error", 1);
  208. Log3 $hash, 3, "$name: failurei in message from $pname";
  209. Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
  210. (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
  211. (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
  212. (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
  213. (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
  214. }
  215. } else {
  216. readingsSingleUpdate($hash,"state", "transmission error", 1);
  217. Log3 $hash, 3, "$name: failure in message from $pname";
  218. Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
  219. (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
  220. (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
  221. (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
  222. (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
  223. }
  224. }
  225. ###############################################################################
  226. sub I2C_EEPROM_UpdReadings($$$) { #nach Rueckmeldung readings updaten (ueber I2CRec aufgerufen)
  227. my ($hash, $reg, $inh) = @_;
  228. my $name = $hash->{NAME};
  229. Log3 $hash, 5, "$name UpdReadings Register: $reg, Inhalt: $inh";
  230. my $regb = $reg >> 4;
  231. my $regp = $reg & 15;
  232. my $bank = ReadingsVal($name,"0x".sprintf("%02X",$regb)."x",".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..");
  233. my $nbank = $bank;
  234. substr($nbank,$regp * 3,2,sprintf("%02X",$inh));
  235. if ($nbank ne $bank) { #bei Aenderung
  236. readingsBeginUpdate($hash);
  237. readingsBulkUpdate($hash, "0x".sprintf("%02X",$regb)."x" , $nbank);
  238. readingsEndUpdate($hash, 1);
  239. }
  240. return;
  241. }
  242. ###############################################################################
  243. sub I2C_EEPROM_SetReg { #set register
  244. my ($hash, $reg, $inh) = @_;
  245. if (defined (my $iodev = $hash->{IODev})) {
  246. CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
  247. direction => "i2cwrite",
  248. i2caddress => $hash->{I2C_Address},
  249. reg => $reg,
  250. data => $inh,
  251. }) if (defined $hash->{I2C_Address});
  252. } else {
  253. return "no IODev assigned to '$hash->{NAME}'";
  254. }
  255. }
  256. ###############################################################################
  257. sub I2C_EEPROM_BytefromReading($@) {
  258. my ($hash, $reg, $num) = @_;
  259. #$num = "hex" unless defined $num ;
  260. my $regb = $reg >> 4;
  261. my $regp = $reg & 15;
  262. my $bank = ReadingsVal($hash->{NAME},"0x".sprintf("%02X",$regb)."x",".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..");
  263. if ($num eq 'dec') {
  264. return hex(substr($bank,$regp * 3,2));
  265. } elsif ($num eq 'bin') {
  266. return sprintf ('0b%08b', hex(substr($bank,$regp * 3,2)));
  267. } else {
  268. return "0x" . substr($bank,$regp * 3,2);
  269. }
  270. }
  271. 1;
  272. =pod
  273. =item device
  274. =item summary reads the content from an via I2C connected EEPROM
  275. =item summary_DE lesen des Inhals eines &uuml;ber I2C angeschlossenen EEPROM
  276. =begin html
  277. <a name="I2C_EEPROM"></a>
  278. <h3>I2C_EEPROM</h3>
  279. <ul>
  280. <a name="I2C_EEPROM"></a>
  281. Provides an interface to an I2C EEPROM.<br>
  282. The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  283. or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
  284. <b>attribute IODev must be set</b><br>
  285. <a name="I2C_EEPROMDefine"></a><br>
  286. <b>Define</b>
  287. <ul>
  288. <code>define &lt;name&gt; I2C_EEPROM &lt;I2C Address&gt;</code><br>
  289. where <code>&lt;I2C Address&gt;</code> is without direction bit<br>
  290. </ul>
  291. <a name="I2C_EEPROMSet"></a>
  292. <b>Set</b>
  293. <ul>
  294. <code>set &lt;name&gt; &lt;byte address&gt; &lt;value&gt;</code><br><br>
  295. where <code>&lt;byte address&gt;</code> is a number (0..device specific) and <code>&lt;value&gt;</code> is a number (0..255)<br>
  296. both numbers can be written in decimal or hexadecimal notation.<br>
  297. <br>
  298. Example:
  299. <ul>
  300. <code>set eeprom1 0x02 0xAA</code><br>
  301. <code>set eeprom1 2 170</code><br>
  302. </ul><br>
  303. </ul>
  304. <a name="I2C_EEPROMGet"></a>
  305. <b>Get</b>
  306. <ul>
  307. <code>get &lt;name&gt;</code>
  308. <br><br>
  309. refreshes all readings
  310. </ul><br>
  311. <ul>
  312. <code>get &lt;name&gt; &lt;byte address&gt; [Bit&lt;bitnumber(0..7)&gt;]</code>
  313. <br><br>
  314. returnes actual reading of stated &lt;byte address&gt; or a single bit of &lt;byte address&gt;<br>
  315. Values are readout from readings, NOT from device!
  316. </ul><br>
  317. <a name="I2C_EEPROMAttr"></a>
  318. <b>Attributes</b>
  319. <ul>
  320. <li>poll_interval<br>
  321. Set the polling interval in minutes to query the EEPROM content<br>
  322. Default: -, valid values: decimal number<br><br>
  323. </li>
  324. <li>EEPROM_size<br>
  325. Sets the storage size of the EEPROM<br>
  326. Default: 128, valid values: 128 (128bit), 2k (2048bit)<br><br>
  327. </li>
  328. <li><a href="#IODev">IODev</a></li>
  329. <li><a href="#ignore">ignore</a></li>
  330. <li><a href="#do_not_notify">do_not_notify</a></li>
  331. <li><a href="#showtime">showtime</a></li>
  332. </ul>
  333. <br>
  334. </ul>
  335. =end html
  336. =begin html_DE
  337. <a name="I2C_EEPROM"></a>
  338. <h3>I2C_EEPROM</h3>
  339. <ul>
  340. <a name="I2C_EEPROM"></a>
  341. Erm&ouml;glicht die Verwendung I2C EEPROM.
  342. I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  343. oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
  344. <b>Das Attribut IODev muss definiert sein.</b><br>
  345. <a name="I2C_EEPROMDefine"></a><br>
  346. <b>Define</b>
  347. <ul>
  348. <code>define &lt;name&gt; I2C_EEPROM &lt;I2C Address&gt;</code><br>
  349. Der Wert <code>&lt;I2C Address&gt;</code> ist ohne das Richtungsbit
  350. </ul>
  351. <a name="I2C_EEPROMSet"></a>
  352. <b>Set</b>
  353. <ul>
  354. <code>set &lt;name&gt; &lt;byte address&gt; &lt;value&gt;</code><br><br>
  355. <code>&lt;byte address&gt;</code> ist die Registeradresse (0..IC abh&auml;ngig) und <code>&lt;value&gt;</code> der Registerinhalt (0..255)<br>
  356. Beide Zahlen k&ouml;nnen sowohl eine Dezimal- als auch eine Hexadezimalzahl sein.<br>
  357. <br>
  358. Beispiel:
  359. <ul>
  360. <code>set eeprom1 0x02 0xAA</code><br>
  361. <code>set eeprom1 2 170</code><br>
  362. </ul><br>
  363. </ul>
  364. <a name="I2C_EEPROMGet"></a>
  365. <b>Get</b>
  366. <ul>
  367. <code>get &lt;name&gt;</code>
  368. <br><br>
  369. Aktualisierung aller Werte
  370. </ul><br>
  371. <ul>
  372. <code>get &lt;name&gt; &lt;byte address&gt; [Bit&lt;bitnumber(0..7)&gt;]</code>
  373. <br><br>
  374. Gibt den Inhalt des in &lt;byte address&gt; angegebenen Registers zur&uuml;ck, bzw. ein einzelnes Bit davon.<br>
  375. Achtung mit diesem Befehl werden nur die Werte aus den Readings angezeigt und nicht der Registerinhalt selbst!
  376. </ul><br>
  377. <a name="I2C_EEPROMAttr"></a>
  378. <b>Attribute</b>
  379. <ul>
  380. <li>poll_interval<br>
  381. Aktualisierungsintervall aller Werte in Minuten.<br>
  382. Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br>
  383. </li>
  384. <li>EEPROM_size<br>
  385. Speichergröße des EEPROM<br>
  386. Standard: 128, g&uuml;ltige Werte: 128 (128bit), 2k (2048bit)<br><br>
  387. </li>
  388. <li><a href="#IODev">IODev</a></li>
  389. <li><a href="#ignore">ignore</a></li>
  390. <li><a href="#do_not_notify">do_not_notify</a></li>
  391. <li><a href="#showtime">showtime</a></li>
  392. </ul>
  393. <br>
  394. </ul>
  395. =end html_DE
  396. =cut