52_I2C_PCA9532.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  1. ##############################################################################
  2. # $Id: 52_I2C_PCA9532.pm 12059 2016-08-22 21:14:59Z klauswitt $
  3. #
  4. ##############################################################################
  5. # Modul for I2C PWM Driver PCA9532
  6. #
  7. # define <name> I2C_PCA9532 <I2C-Adresse>
  8. # set <name> <port> <value>
  9. #
  10. # contributed by Klaus Wittstock (2014) email: klauswittstock bei gmail punkt com
  11. #
  12. ##############################################################################
  13. #Inhalte des Hashes:
  14. #i2caddress 00-127(7F) I2C-Adresse
  15. #direction i2cread|i2cwrite Richtung
  16. #reg 00-255|"" Registeradresse (kann weggelassen werden fuer IC's ohne Registeradressierung)
  17. #nbyte Zahl Anzahl Register, die bearbeitet werden sollen (im mom 0-99)
  18. #data 00-255 ... Daten die an I2C geschickt werden sollen (muessen, wenn nbyte benutzt wird immer ein Vielfaches Desselben sein)
  19. #received 00-255 ... Daten die vom I2C empfangen wurden, durch Leerzeichen getrennt (bleibt leer wenn Daten geschrieben werden)
  20. #pname_SENDSTAT Ok|error zeigt uebertragungserfolg an
  21. package main;
  22. use strict;
  23. use warnings;
  24. use SetExtensions;
  25. no if $] >= 5.017011, warnings => 'experimental::smartmatch';
  26. #use POSIX;
  27. use Scalar::Util qw(looks_like_number);
  28. my $setdim = ":slider,0,1,255 ";
  29. my %setsP = (
  30. 'off' => 0,
  31. 'on' => 1,
  32. 'PWM0' => 2,
  33. 'PWM1' => 3,
  34. );
  35. ###############################################################################
  36. sub I2C_PCA9532_Initialize($) {
  37. my ($hash) = @_;
  38. $hash->{DefFn} = "I2C_PCA9532_Define";
  39. $hash->{InitFn} = 'I2C_PCA9532_Init';
  40. $hash->{UndefFn} = "I2C_PCA9532_Undefine";
  41. $hash->{AttrFn} = "I2C_PCA9532_Attr";
  42. $hash->{StateFn} = "I2C_PCA9532_State";
  43. $hash->{SetFn} = "I2C_PCA9532_Set";
  44. $hash->{GetFn} = "I2C_PCA9532_Get";
  45. $hash->{I2CRecFn} = "I2C_PCA9532_I2CRec";
  46. $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
  47. "poll_interval T0:slider,0,1,255 T1:slider,0,1,255 OnStartup InputPorts ".
  48. "OutputPorts:multiple-strict,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 ".
  49. "$readingFnAttributes";
  50. }
  51. ###############################################################################
  52. sub I2C_PCA9532_SetState($$$$) {
  53. my ($hash, $tim, $vt, $val) = @_;
  54. $val = $1 if($val =~ m/^(.*) \d+$/);
  55. #return "Undefined value $val" if(!defined($it_c2b{$val}));
  56. return undef;
  57. }
  58. ###############################################################################
  59. sub I2C_PCA9532_Define($$) {
  60. my ($hash, $def) = @_;
  61. my @a = split("[ \t]+", $def);
  62. $hash->{STATE} = 'defined';
  63. if ($main::init_done) {
  64. eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
  65. return I2C_PCA9532_Catch($@) if $@;
  66. }
  67. return undef;
  68. }
  69. ###############################################################################
  70. sub I2C_PCA9532_Init($$) {
  71. my ( $hash, $args ) = @_;
  72. #my @a = split("[ \t]+", $args);
  73. my $name = $hash->{NAME};
  74. if (defined $args && int(@$args) != 1) {
  75. return "Define: Wrong syntax. Usage:\n" .
  76. "define <name> I2C_PCA9532 <i2caddress>";
  77. }
  78. #return "$name I2C Address not valid" unless ($a[0] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi);
  79. if (defined (my $address = shift @$args)) {
  80. $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
  81. } else {
  82. return "$name I2C Address not valid";
  83. }
  84. AssignIoPort($hash);
  85. #PWM Frequenzen wiederherstellen
  86. I2C_PCA9532_I2CSet($hash,AttrVal($name, "T0", 0),2);
  87. I2C_PCA9532_I2CSet($hash,AttrVal($name, "T1", 0),4);
  88. #PWM Werte wiederherstellen
  89. I2C_PCA9532_Set($hash, $name,"PWM0", ReadingsVal($name,"PWM0",0) );
  90. I2C_PCA9532_Set($hash, $name,"PWM1", ReadingsVal($name,"PWM1",0) );
  91. #Portzustände wiederherstellen
  92. my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", "")));
  93. foreach (0..15) {
  94. I2C_PCA9532_Set($hash, $name,"Port".$_, ReadingsVal($name,"Port".$_,0) ) if ( $_ ~~ @outports);
  95. }
  96. $hash->{STATE} = 'Initialized';
  97. return;
  98. }
  99. ###############################################################################
  100. sub I2C_PCA9532_Catch($) {
  101. my $exception = shift;
  102. if ($exception) {
  103. $exception =~ /^(.*)( at.*FHEM.*)$/;
  104. return $1;
  105. }
  106. return undef;
  107. }
  108. ###############################################################################
  109. sub I2C_PCA9532_State($$$$) { #reload readings at FHEM start
  110. my ($hash, $tim, $sname, $sval) = @_;
  111. Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim";
  112. if ($sname =~ m/^Port([0-9]|1[0-5])$/i) {
  113. my $po = $sname; #noch ändern
  114. $po =~ tr/[a-zA-Z]//d; #Nummer aus String extrahieren
  115. my @outports = sort(split(/,/,AttrVal($hash->{NAME}, "OutputPorts", "")));
  116. if ( $po ~~ @outports) {
  117. my %onstart = split /[,=]/, AttrVal($hash->{NAME}, "OnStartup", "");
  118. if ( exists($onstart{$po}) && exists($setsP{$onstart{$po}})) {
  119. Log3 $hash, 5, "$hash->{NAME}: $sname soll auf $onstart{$po} gesetzt werden";
  120. readingsSingleUpdate($hash,$sname, $onstart{$po}, 1);
  121. } else {
  122. Log3 $hash, 5, "$hash->{NAME}: $sname soll auf Altzustand: $sval gesetzt werden";
  123. $hash->{READINGS}{$sname}{VAL} = $sval;
  124. $hash->{READINGS}{$sname}{TIME} = $tim;
  125. }
  126. } else {
  127. Log3 $hash, 5, "$hash->{NAME}: $sname ist Eingang";
  128. }
  129. } elsif ($sname =~ m/^PWM[0-1]$/i) {
  130. my %onstart = split /[,=]/, AttrVal($hash->{NAME}, "OnStartup", "");
  131. if ( exists($onstart{$sname}) && looks_like_number($onstart{$sname}) && $onstart{$sname} >= 0 && $onstart{$sname} < 256 ) {
  132. Log3 $hash, 5, "$hash->{NAME}: $sname soll auf $onstart{$sname} gesetzt werden";
  133. readingsSingleUpdate($hash,$sname, $onstart{$sname}, 1);
  134. } else {
  135. Log3 $hash, 5, "$hash->{NAME}: $sname soll auf Altzustand: $sval gesetzt werden";
  136. $hash->{READINGS}{$sname}{VAL} = $sval;
  137. $hash->{READINGS}{$sname}{TIME} = $tim;
  138. }
  139. }
  140. return undef;
  141. }
  142. ###############################################################################
  143. sub I2C_PCA9532_Undefine($$) {
  144. my ($hash, $arg) = @_;
  145. if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
  146. RemoveInternalTimer($hash);
  147. }
  148. }
  149. ###############################################################################
  150. sub I2C_PCA9532_Attr(@) {
  151. my ($command, $name, $attr, $val) = @_;
  152. my $hash = $defs{$name};
  153. my $msg = '';
  154. if ($command && $command eq "set" && $attr && $attr eq "IODev") {
  155. if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
  156. main::AssignIoPort($hash,$val);
  157. my @def = split (' ',$hash->{DEF});
  158. I2C_PCA9532_Init($hash,\@def) if (defined ($hash->{IODev}));
  159. }
  160. } elsif ($attr && $attr eq 'poll_interval') {
  161. if (!defined($val) ) {
  162. RemoveInternalTimer($hash);
  163. } elsif ( looks_like_number($val) && $val > 0) {
  164. RemoveInternalTimer($hash);
  165. InternalTimer(1, 'I2C_PCA9532_Poll', $hash, 0);
  166. } else {
  167. $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
  168. }
  169. } elsif ($attr && $attr =~ m/^T[0-1]$/i) {
  170. return "wrong value: $val for \"set $name $attr\" use 0-255"
  171. unless(looks_like_number($val) && $val >= 0 && $val < 256);
  172. substr($attr,0,1,"");
  173. my $regaddr = ($attr == 0 ? 2 : 4);
  174. $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr);
  175. ###################################################################irgendwann raus damit
  176. } elsif ($attr && $attr eq "InputPorts") {
  177. my @inp = split(" ", $val) if defined($val);
  178. my $oval = 65535;
  179. foreach (@inp) {
  180. return "wrong value: $_ for \"attr $name $attr\" use space separated numbers 0-15" unless ($_ >= 0 && $_ < 16);
  181. $oval -= 1 << $_;
  182. }
  183. my $outports = undef;
  184. foreach (0..15) {
  185. if ( ($oval & (1 << $_)) != 0 ) {
  186. $outports .= "," if defined($outports);
  187. $outports .= $_;
  188. }
  189. }
  190. $msg .= CommandAttr(undef, $name . ' OutputPorts ' . $outports);
  191. ########das noch auf 1 setzen!!!!!!!!!!1
  192. $val = "" unless defined($val);
  193. Log3 $hash ,1 , "\"attr $name $attr $val\" will removed in further versions, generated: \"attr $name OutputPorts $outports\" please store it in your config and remove InputPorts";
  194. ###############################################################################################
  195. } elsif ($attr && $attr eq "OutputPorts") {
  196. my @inp = split(/,/, $val) if defined($val);
  197. foreach (@inp) {
  198. return "wrong value: $_ for \"attr $name $attr\" use comma separated numbers 0-15" unless ($_ >= 0 && $_ < 16);
  199. }
  200. $msg = I2C_PCA9532_ResetInputs($hash, @inp) unless $msg;
  201. } elsif ($attr && $attr eq "OnStartup") {
  202. if (defined $val) {
  203. foreach (split (/,/,$val)) {
  204. my @pair = split (/=/,$_);
  205. $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated <port>=on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where <port> = 0 - 15 "
  206. unless ( scalar(@pair) == 2 &&
  207. (($pair[0] =~ m/^[0-9]|1[0-5]$/i && ( $pair[1] eq "last" || exists($setsP{$pair[1]}) ) ) ||
  208. ( $pair[0] =~ m/^PWM(0|1)$/i && looks_like_number($pair[1]) && $pair[1] >=0 && $pair[1] < 256))
  209. );
  210. }
  211. }
  212. }
  213. return ($msg) ? $msg : undef;
  214. }
  215. ###############################################################################
  216. sub I2C_PCA9532_Poll($) {
  217. my ($hash) = @_;
  218. my $name = $hash->{NAME};
  219. # Read values
  220. I2C_PCA9532_Get($hash, $name);
  221. my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
  222. if ($pollInterval > 0) {
  223. InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCA9532_Poll', $hash, 0);
  224. }
  225. }
  226. ###############################################################################
  227. sub I2C_PCA9532_ResetInputs{
  228. my ($hash, @outports) = @_;
  229. my $msg = undef;
  230. foreach my $LSreg (0..3) { #Nummer des entspechenden LS Registers
  231. my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06)
  232. my $n = $LSreg * 4; #Erster Port in LSx
  233. my $sbyte = 0;
  234. foreach (reverse 0..3) { #ensprechendes Controlregister fuellen
  235. my $portn = $_ + $n;
  236. if ($portn ~~ @outports) { #output aus dem Reading holen
  237. my $pval = ReadingsVal($hash->{NAME},'Port'.$portn,"off");
  238. $sbyte += $setsP{ ($pval =~ m/^(0|1)$/i ? "off" : $pval) } << (2 * $_);
  239. } else { #wenn Input ist auf rezessiv (high) setzen
  240. $sbyte += $setsP{"off"} << (2 * $_);
  241. }
  242. }
  243. $msg = I2C_PCA9532_I2CSet($hash,$sbyte,$regaddr);
  244. last if $msg;
  245. }
  246. return defined $msg ? $msg : undef
  247. }
  248. ###############################################################################
  249. sub I2C_PCA9532_Set($@) {
  250. my ($hash, @a) = @_;
  251. my $name =$a[0];
  252. my $cmd = $a[1];
  253. my $val = $a[2];
  254. my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", "")));
  255. #my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
  256. unless (@a == 3) {
  257. }
  258. my $msg;
  259. #my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" );
  260. if ( $cmd && $cmd =~ m/^Port((0|)[0-9]|1[0-5])$/i) {
  261. return "wrong value: $val for \"set $name $cmd\" use one of: " .
  262. join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) )
  263. unless(exists($setsP{$val}));
  264. substr($cmd,0,4,"");
  265. return "$name error: Port$cmd is defined as input" unless ( $cmd ~~ @outports ); #Pruefen ob entsprechender Port Input ist
  266. my $LSreg = int($cmd / 4); #Nummer des entspechenden LS Registers
  267. my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06)
  268. my $n = $LSreg * 4; #Erster Port in LSx
  269. my $sbyte = 0;
  270. foreach (reverse 0..3) { #ensprechendes Controlregister fuellen
  271. my $portn = $_ + $n;
  272. if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert
  273. $sbyte += $setsP{$val} << (2 * $_);
  274. #next;
  275. } elsif ($portn ~~ @outports) {#->sonst aus dem Reading holen
  276. $sbyte += $setsP{ReadingsVal($name,'Port'.$portn,"off")} << (2 * $_);
  277. } else { #wenn Input ist auf rezessiv (high) setzen
  278. $sbyte += $setsP{"off"} << (2 * $_);
  279. }
  280. }
  281. $msg = I2C_PCA9532_I2CSet($hash,$sbyte,$regaddr);
  282. #$sendpackage{data} = sprintf("%.2X",$sbyte);
  283. #$sendpackage{data} = $sbyte;
  284. #$sendpackage{reg} = sprintf("%.2X", $regaddr);
  285. #$sendpackage{reg} = $regaddr;
  286. } elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) {
  287. return "wrong value: $val for \"set $name $cmd\" use 0-255"
  288. unless(looks_like_number($val) && $val >= 0 && $val < 256);
  289. substr($cmd,0,3,"");
  290. my $regaddr = ($cmd == 0 ? 3 : 5);
  291. $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr);
  292. #return $msg if $msg;
  293. #$sendpackage{data} = sprintf("%.2X", $val);
  294. #$sendpackage{data} = $val;
  295. #$sendpackage{reg} = sprintf("%.2X", $regaddr);
  296. } else {
  297. my $list = undef;
  298. foreach (0..15) {
  299. next unless ( $_ ~~ @outports ); #Inputs ueberspringen
  300. #$list .= "Port" . $_ . ":" . join(',', sort keys %setsP) . " ";
  301. $list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " ";
  302. }
  303. $list .= join($setdim, ("PWM0", "PWM1")) . $setdim;
  304. $msg = "Unknown argument $a[1], choose one of " . $list;
  305. }
  306. #return "$name: no IO device defined" unless ($hash->{IODev});
  307. #my $phash = $hash->{IODev};
  308. #my $pname = $phash->{NAME};
  309. #CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
  310. return defined $msg ? $msg : undef
  311. }
  312. ###############################################################################
  313. sub I2C_PCA9532_I2CSet {
  314. my ($hash, $regval, $reg) = @_;
  315. if (defined (my $iodev = $hash->{IODev})) {
  316. CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
  317. direction => "i2cwrite",
  318. i2caddress => $hash->{I2C_Address},
  319. reg => $reg,
  320. data => $regval,
  321. }) if (defined $hash->{I2C_Address});
  322. } else {
  323. return "no IODev assigned to '$hash->{NAME}'";
  324. }
  325. }
  326. ###############################################################################
  327. sub I2C_PCA9532_Get($@) {
  328. my ($hash, @a) = @_;
  329. my $name =$a[0];
  330. my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
  331. $sendpackage{reg} = 0x10; #startadresse zum lesen (Adresse 0 mit Auto increment flag)
  332. $sendpackage{nbyte} = 10;
  333. return "$name: no IO device defined" unless ($hash->{IODev});
  334. my $phash = $hash->{IODev};
  335. my $pname = $phash->{NAME};
  336. CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
  337. }
  338. ###############################################################################
  339. sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen
  340. my ($hash, $clientmsg) = @_;
  341. my $name = $hash->{NAME};
  342. my $phash = $hash->{IODev};
  343. my $pname = $phash->{NAME};
  344. while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
  345. $hash->{$k} = $v if $k =~ /^$pname/ ;
  346. }
  347. #hier noch ueberpruefen, ob Register und Daten ok
  348. if ($clientmsg->{direction} && defined($clientmsg->{reg}) && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") {
  349. if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) { # =~ m/^[a-f0-9]{2}$/i) {
  350. #my @rec = @{$clientmsg->{received}}; #bei uebergabe im hash als array
  351. my @rec = split(" ",$clientmsg->{received}); #bei uebergabe im als skalar
  352. Log3 $hash, 3, "$name: wrong amount of registers transmitted from $pname" unless (@rec == $clientmsg->{nbyte});
  353. foreach (reverse 0..$#rec) { #reverse, damit Inputs (Register 0 und 1 als letztes geschrieben werden)
  354. I2C_PCA9532_UpdReadings($hash, $_ + $clientmsg->{reg} , $rec[$_]);
  355. }
  356. readingsSingleUpdate($hash,"state", "Ok", 1);
  357. } elsif ( $clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data}) ) { # =~ m/^[a-f0-9]{2}$/i) {#readings aktualisieren wenn uebertragung ok
  358. I2C_PCA9532_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
  359. readingsSingleUpdate($hash,"state", "Ok", 1);
  360. } else {
  361. readingsSingleUpdate($hash,"state", "transmission error", 1);
  362. Log3 $hash, 3, "$name: failure in message from $pname";
  363. Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
  364. (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
  365. (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
  366. (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
  367. (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
  368. }
  369. } else {
  370. readingsSingleUpdate($hash,"state", "transmission error", 1);
  371. Log3 $hash, 3, "$name: failure in message from $pname";
  372. Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
  373. (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
  374. (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
  375. (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
  376. (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
  377. #my $cmsg = undef;
  378. #foreach my $av (keys %{$clientmsg}) { $cmsg .= "|" . $av . ": " . $clientmsg->{$av}; }
  379. #Log3 $hash, 3, $cmsg;
  380. }
  381. #undef $clientmsg;
  382. }
  383. ###############################################################################
  384. sub I2C_PCA9532_UpdReadings($$$) {
  385. my ($hash, $reg, $inh) = @_;
  386. my $name = $hash->{NAME};
  387. #$inh = hex($inh);
  388. Log3 $hash, 5, "$name UpdReadings Register: " .sprintf("0x%.2X", $reg).", Inhalt: ".sprintf("0x%.2X", $inh);
  389. readingsBeginUpdate($hash);
  390. if ( $reg < 10 && $reg > 5) { #Wenn PortRegister
  391. my %rsetsP = reverse %setsP;
  392. my $LSreg = $reg - 6; #Nummer des entspechenden LS Registers
  393. my $n = $LSreg * 4; #Erster Port in LSx
  394. foreach (reverse 0..3) { #Ports aus Controlregister abarbeiten
  395. my $pval = 3 & ( $inh >> ($_ * 2) );
  396. my $port = $_ + $n;
  397. readingsBulkUpdate($hash, 'Port'.$port , $rsetsP{$pval})
  398. if (ReadingsVal($name, 'Port'.$port,"nix") ne $rsetsP{$pval}); #nur wenn Wert geaendert
  399. }
  400. } elsif ( $reg == 3) { #wenn PWM0 Register
  401. readingsBulkUpdate($hash, 'PWM0' , $inh);
  402. } elsif ( $reg == 5) { #wenn PWM1 Register
  403. readingsBulkUpdate($hash, 'PWM1' , $inh);
  404. } elsif ( $reg == 2) { #wenn Frequenz0 Register
  405. $hash->{Frequency_0} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz";
  406. } elsif ( $reg == 4) { #wenn Frequenz0 Register
  407. $hash->{Frequency_1} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz";
  408. } elsif ( $reg >= 0 && $reg < 2 ) { #Input Register
  409. my $j = 8 * $reg;
  410. my @outports = sort(split( ",",AttrVal($name, "OutputPorts", "")));
  411. for (my $i = 0; $i < 8; $i++) {
  412. Log3 $hash, 5, "Register " .sprintf("0x%.2X", $reg)." Forschleife i: $i";
  413. unless ( ($i + $j) ~~ @outports ) { #nur als Input definierte Ports aktualisieren
  414. my $sval = $inh & (1 << $i);
  415. $sval = $sval == 0 ? "0" :"1";
  416. readingsBulkUpdate($hash, 'Port'.($i + $j) , $sval) if (ReadingsVal($name,'Port'.($i + $j),2) ne $sval);
  417. }
  418. }
  419. }
  420. readingsEndUpdate($hash, 1);
  421. return;
  422. }
  423. ###############################################################################
  424. 1;
  425. =pod
  426. =item device
  427. =item summary controls PWM outputs from an via I2C connected PCA9532
  428. =item summary_DE steuern der PWM Ausg&aumlnge eines &uuml;ber I2C angeschlossenen PCA9532
  429. =begin html
  430. <a name="I2C_PCA9532"></a>
  431. <h3>I2C_PCA9532</h3>
  432. (en | <a href="commandref_DE.html#I2C_PCA9532">de</a>)
  433. <ul>
  434. <a name="I2C_PCA9532"></a>
  435. Provides an interface to the PCA9532 I2C 16 channel PWM IC.
  436. The PCA9532 has 2 independent PWM stages and every channel can be attached to on of these stages or directly turned on or off.
  437. The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  438. or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
  439. <b>attribute IODev must be set</b><br>
  440. <a name="I2C_PCA9532Define"></a><br>
  441. <b>Define</b>
  442. <ul>
  443. <code>define &lt;name&gt; I2C_PCA9532 &lt;I2C Address&gt;</code><br>
  444. where <code>&lt;I2C Address&gt;</code> is an 2 digit hexadecimal value<br>
  445. </ul>
  446. <a name="I2C_PCA9532Set"></a>
  447. <b>Set</b>
  448. <ul>
  449. <code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
  450. <ul>
  451. <li>if <code>&lt;port&gt;</code> is one of Port0 to Port15, then <code>&lt;value&gt;</code> will be one of:<br>
  452. <ul>
  453. <code>
  454. off<br>
  455. on<br>
  456. PWM0 (output is switched with PWM0 frequency and duty cycle)<br>
  457. PWM1 (output is switched with PWM1 frequency and duty cycle)<br>
  458. </code>
  459. </ul>
  460. </li>
  461. <li>
  462. 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.
  463. </li>
  464. </ul>
  465. <br>
  466. Examples:
  467. <ul>
  468. <code>set mod1 Port4 PWM1</code><br>
  469. <code>set mod1 PWM1 128</code><br>
  470. </ul><br>
  471. </ul>
  472. <a name="I2C_PCA9532Get"></a>
  473. <b>Get</b>
  474. <ul>
  475. <code>get &lt;name&gt;</code>
  476. <br><br>
  477. refreshes all readings
  478. </ul><br>
  479. <a name="I2C_PCA9532Attr"></a>
  480. <b>Attributes</b>
  481. <ul>
  482. <li>poll_interval<br>
  483. Set the polling interval in minutes to query the GPIO's level<br>
  484. Default: -, valid values: decimal number<br><br>
  485. </li>
  486. <li>OutputPorts<br>
  487. Comma separated list of Portnumers that are used as Outputs<br>
  488. Only ports in this list can be written<br>
  489. Default: no, valid values: 0 1 2 .. 15<br><br>
  490. </li>
  491. <li>OnStartup<br>
  492. Comma separated list of output ports/PWM registers and their desired state after start<br>
  493. Without this atribut all output ports will set to last state<br>
  494. Default: -, valid values: &lt;port&gt;=on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where &lt;port&gt; = 0 - 15<br><br>
  495. </li>
  496. <li>T0/T1<br>
  497. Sets PWM0/PWM1 to another Frequency. The Formula is: Fx = 152/(Tx + 1) The corresponding frequency value is shown under internals.<br>
  498. Default: 0 (152Hz), valid values: 0-255<br><br>
  499. </li>
  500. <li><a href="#IODev">IODev</a></li>
  501. <li><a href="#ignore">ignore</a></li>
  502. <li><a href="#do_not_notify">do_not_notify</a></li>
  503. <li><a href="#showtime">showtime</a></li>
  504. </ul>
  505. <br>
  506. </ul>
  507. =end html
  508. =begin html_DE
  509. <a name="I2C_PCA9532"></a>
  510. <h3>I2C_PCA9532</h3>
  511. (<a href="commandref.html#I2C_PCA9532">en</a> | de)
  512. <ul>
  513. <a name="I2C_PCA9532"></a>
  514. Erm&ouml;glicht die Verwendung eines PCA9532 I2C 16 Kanal PWM IC.
  515. Das PCA9532 hat 2 unabh&auml;ngige PWM Stufen. Jeder Kanal kanne einer der Stufen zugeordnet werden oder direkt auf off/on gesetzt werden.
  516. I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  517. oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
  518. <b>Das Attribut IODev muss definiert sein.</b><br>
  519. <a name="I2C_PCA9532Define"></a><br>
  520. <b>Define</b>
  521. <ul>
  522. <code>define &lt;name&gt; I2C_PCA9532 &lt;I2C Address&gt;</code><br>
  523. Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert<br>
  524. </ul>
  525. <a name="I2C_PCA9532Set"></a>
  526. <b>Set</b>
  527. <ul>
  528. <code>set &lt;name&gt; &lt;port&gt; &lt;value&gt;</code><br><br>
  529. <ul>
  530. <li>wenn als <code>&lt;port&gt;</code> Port0 bis Port15 verwendet wird, dann ist <code>&lt;value&gt;</code> einer dieser Werte:<br>
  531. <ul>
  532. <code>
  533. off<br>
  534. on<br>
  535. PWM0 (Port wird auf PWM0 Frequenz- und Pulsweiteneinstellung gesetzt)<br>
  536. PWM1 (Port wird auf PWM1 Frequenz- und Pulsweiteneinstellung gesetzt)<br>
  537. </code>
  538. </ul>
  539. </li>
  540. <li>
  541. 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.
  542. </li>
  543. </ul>
  544. <br>
  545. Beispiele:
  546. <ul>
  547. <code>set mod1 Port4 PWM1</code><br>
  548. <code>set mod1 PWM1 128</code><br>
  549. </ul><br>
  550. </ul>
  551. <a name="I2C_PCA9532Get"></a>
  552. <b>Get</b>
  553. <ul>
  554. <code>get &lt;name&gt;</code>
  555. <br><br>
  556. Aktualisierung aller Werte
  557. </ul><br>
  558. <a name="I2C_PCA9532Attr"></a>
  559. <b>Attribute</b>
  560. <ul>
  561. <li>poll_interval<br>
  562. Aktualisierungsintervall aller Werte in Minuten.<br>
  563. Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br>
  564. </li>
  565. <li>OutputPorts<br>
  566. Durch Komma getrennte Portnummern die als Outputs genutzt werden.<br>
  567. Nur Ports in dieser Liste k&ouml;nnen geschrieben werden.<br>
  568. Standard: no, g&uuml;ltige Werte: 0 1 2 .. 15<br><br>
  569. </li>
  570. <li>OnStartup<br>
  571. Durch Komma getrennte Output Ports/PWM Register und ihr gew&uuml;nschter Status nach dem Start.<br>
  572. Ohne dieses Attribut werden alle Ausg&auml;nge nach dem Start auf den letzten Status gesetzt.<br>
  573. Standard: -, g&uuml;ltige Werte: &lt;port&gt;=on|off|PWM0|PWM1|last oder PWM0|PWM1=0..255|last wobei &lt;port&gt; = 0 - 15<br><br>
  574. </li>
  575. <li>T0/T1<br>
  576. &Auml;nderung der Frequenzwerte von PWM0/PWM1 nach der Formel: Fx = 152/(Tx + 1). Der entsprechende Frequenzwert wird unter Internals angezeigt.<br>
  577. Standard: 0 (152Hz), g&uuml;ltige Werte: 0-255<br><br>
  578. </li>
  579. <li><a href="#IODev">IODev</a></li>
  580. <li><a href="#ignore">ignore</a></li>
  581. <li><a href="#do_not_notify">do_not_notify</a></li>
  582. <li><a href="#showtime">showtime</a></li>
  583. </ul>
  584. <br>
  585. </ul>
  586. =end html_DE
  587. =cut