2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-07 12:58:13 +00:00

00_NetzerI2C.pm: added BME280 as client

00_RPII2C.pm: added BME280 as client
10_FRM.pm: added BME280 as client
51_RPI_GPIO.pm: removed table from commandref
52_I2C_BME280.pm: initial release
52_I2C_PCA9685.pm: bugfixing for FRM


git-svn-id: https://svn.fhem.de/fhem/trunk@10638 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
klauswitt 2016-01-26 00:11:07 +00:00
parent 9501df9a60
commit 70a941bd59
12 changed files with 986 additions and 361 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- feature: new module 52_I2C_BME280.pm added (klausw)
- bugfix: 52_I2C_PCA9685: bugfix for interaction with FRM
- change: 49_SSCAM: changed DEF in order to remove credentials from string, - change: 49_SSCAM: changed DEF in order to remove credentials from string,
added "set credentials" command to save username/password, added "set credentials" command to save username/password,
added Attribute "session" to make login-session selectable, added Attribute "session" to make login-session selectable,

View File

@ -23,7 +23,8 @@ my @clients = qw(
I2C_LCD I2C_LCD
I2C_DS1307 I2C_DS1307
I2C_PC.* I2C_PC.*
I2C_MCP23.* I2C_MCP.*
I2C_BME280
I2C_BMP180 I2C_BMP180
I2C_SHT21 I2C_SHT21
I2C_TSL2561 I2C_TSL2561

View File

@ -14,6 +14,7 @@ I2C_LCD
I2C_DS1307 I2C_DS1307
I2C_PC.* I2C_PC.*
I2C_MCP.* I2C_MCP.*
I2C_BME280
I2C_BMP180 I2C_BMP180
I2C_SHT21 I2C_SHT21
I2C_TSL2561 I2C_TSL2561

View File

@ -51,6 +51,7 @@ my @clients = qw(
I2C_PC.* I2C_PC.*
I2C_MCP23.* I2C_MCP23.*
I2C_SHT21 I2C_SHT21
I2C_BME280
I2C_BMP180 I2C_BMP180
I2C_TSL2561 I2C_TSL2561
FRM_LCD FRM_LCD

View File

@ -684,56 +684,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<b>Define</b> <b>Define</b>
<ul> <ul>
<code>define <name> RPI_GPIO &lt;GPIO number&gt;</code><br><br> <code>define <name> RPI_GPIO &lt;GPIO number&gt;</code><br><br>
all usable <code>GPIO number</code> are in the following tables<br><br> all usable <code>GPIO number</code> can be found <a href="http://www.panu.it/raspberry/">here</a><br><br>
<table border="0" cellspacing="0" cellpadding="0">
<td>
PCB Revision 1 P1 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td> <td>Function</td></tr>
<tr><td>3,3V</td> <td>1</td> <td></td><td>2</td> <td>5V</td></tr>
<tr><td><b>GPIO 0 (SDA0)</b></td><td>3</td> <td></td><td>4</td> <td></td></tr>
<tr><td><b>GPIO 1 (SCL0)</b></td><td>5</td> <td></td><td>6</td> <td>GND</td></tr>
<tr><td>GPIO 4 (GPCLK0)</td> <td>7</td> <td></td><td>8</td> <td>GPIO 14 (TxD)</td></tr>
<tr><td></td> <td>9</td> <td></td><td>10</td> <td>GPIO 15 (RxD)</td></tr>
<tr><td>GPIO 17</td> <td>11</td> <td></td><td>12</td> <td>GPIO 18 (PCM_CLK)</td></tr>
<tr><td><b>GPIO 21</b></td> <td>13</td> <td></td><td>14</td> <td></td></tr>
<tr><td>GPIO 22</td> <td>15</td> <td></td><td>16</td> <td>GPIO 23</td></tr>
<tr><td></td> <td>17</td> <td></td><td>18</td> <td>GPIO 24</td></tr>
<tr><td>GPIO 10 (MOSI)</td> <td>19</td> <td></td><td>20</td> <td></td></tr>
<tr><td>GPIO 9 (MISO)</td> <td>21</td> <td></td><td>22</td> <td>GPIO 25</td></tr>
<tr><td>GPIO 11 (SCLK)</td> <td>23</td> <td></td><td>24</td> <td>GPIO 8 (CE0)</td></tr>
<tr><td></td> <td>25</td> <td></td><td>26</td> <td>GPIO 7 (CE1)</td></tr></table>
</td>
<td>
PCB Revision 2 P1 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td> <td>Function</td></tr>
<tr><td>3,3V</td> <td>1</td> <td></td><td>2</td> <td>5V</td></tr>
<tr><td><b>GPIO 2 (SDA1)</b></td><td>3</td> <td></td><td>4</td> <td></td></tr>
<tr><td><b>GPIO 3 (SCL1)</b></td><td>5</td> <td></td><td>6</td> <td>GND</td></tr>
<tr><td>GPIO 4 (GPCLK0)</td> <td>7</td> <td></td><td>8</td> <td>GPIO 14 (TxD)</td></tr>
<tr><td></td> <td>9</td> <td></td><td>10</td> <td>GPIO 15 (RxD)</td></tr>
<tr><td>GPIO 17</td> <td>11</td> <td></td><td>12</td> <td>GPIO 18 (PCM_CLK)</td></tr>
<tr><td><b>GPIO 27</b></td> <td>13</td> <td></td><td>14</td> <td></td></tr>
<tr><td>GPIO 22</td> <td>15</td> <td></td><td>16</td> <td>GPIO 23</td></tr>
<tr><td></td> <td>17</td> <td></td><td>18</td> <td>GPIO 24</td></tr>
<tr><td>GPIO 10 (MOSI)</td> <td>19</td> <td></td><td>20</td> <td></td></tr>
<tr><td>GPIO 9 (MISO)</td> <td>21</td> <td></td><td>22</td> <td>GPIO 25</td></tr>
<tr><td>GPIO 11 (SCLK)</td> <td>23</td> <td></td><td>24</td> <td>GPIO 8 (CE0)</td></tr>
<tr><td></td> <td>25</td> <td></td><td>26</td> <td>GPIO 7 (CE1)</td></tr></table>
</td>
<td>
PCB Revision 2 P5 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td><td>Function</td></tr>
<tr><td>5V</td> <td>1</td> <td></td><td>2</td> <td>3,3V</td></tr>
<tr><td>GPIO 28 (SDA0)</td><td>3</td> <td></td><td>4</td> <td>GPIO 29 (SCL0)</td></tr>
<tr><td>GPIO 30</td> <td>5</td> <td></td><td>6</td> <td>GPOI 31</td></tr>
<tr><td>GND</td> <td>7</td> <td></td><td>8</td> <td>GND</td></tr></table>
</td>
</table>
Examples: Examples:
<pre> <pre>
define Pin12 RPI_GPIO 18 define Pin12 RPI_GPIO 18
@ -877,56 +829,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<b>Define</b> <b>Define</b>
<ul> <ul>
<code>define &lt;name&gt; RPI_GPIO &lt;GPIO number&gt;</code><br><br> <code>define &lt;name&gt; RPI_GPIO &lt;GPIO number&gt;</code><br><br>
Alle verf&uuml;gbaren <code>GPIO number</code> sind in den folgenden Tabellen zu finden<br><br> Alle verf&uuml;gbaren <code>GPIO number</code> sind z.B. <a href="http://www.panu.it/raspberry/">hier</a> zu finden<br><br>
<table border="0" cellspacing="0" cellpadding="0">
<td>
PCB Revision 1 P1 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td> <td>Function</td></tr>
<tr><td>3,3V</td> <td>1</td> <td></td><td>2</td> <td>5V</td></tr>
<tr><td><b>GPIO 0 (SDA0)</b></td><td>3</td> <td></td><td>4</td> <td></td></tr>
<tr><td><b>GPIO 1 (SCL0)</b></td><td>5</td> <td></td><td>6</td> <td>GND</td></tr>
<tr><td>GPIO 4 (GPCLK0)</td> <td>7</td> <td></td><td>8</td> <td>GPIO 14 (TxD)</td></tr>
<tr><td></td> <td>9</td> <td></td><td>10</td> <td>GPIO 15 (RxD)</td></tr>
<tr><td>GPIO 17</td> <td>11</td> <td></td><td>12</td> <td>GPIO 18 (PCM_CLK)</td></tr>
<tr><td><b>GPIO 21</b></td> <td>13</td> <td></td><td>14</td> <td></td></tr>
<tr><td>GPIO 22</td> <td>15</td> <td></td><td>16</td> <td>GPIO 23</td></tr>
<tr><td></td> <td>17</td> <td></td><td>18</td> <td>GPIO 24</td></tr>
<tr><td>GPIO 10 (MOSI)</td> <td>19</td> <td></td><td>20</td> <td></td></tr>
<tr><td>GPIO 9 (MISO)</td> <td>21</td> <td></td><td>22</td> <td>GPIO 25</td></tr>
<tr><td>GPIO 11 (SCLK)</td> <td>23</td> <td></td><td>24</td> <td>GPIO 8 (CE0)</td></tr>
<tr><td></td> <td>25</td> <td></td><td>26</td> <td>GPIO 7 (CE1)</td></tr></table>
</td>
<td>
PCB Revision 2 P1 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td> <td>Function</td></tr>
<tr><td>3,3V</td> <td>1</td> <td></td><td>2</td> <td>5V</td></tr>
<tr><td><b>GPIO 2 (SDA1)</b></td><td>3</td> <td></td><td>4</td> <td></td></tr>
<tr><td><b>GPIO 3 (SCL1)</b></td><td>5</td> <td></td><td>6</td> <td>GND</td></tr>
<tr><td>GPIO 4 (GPCLK0)</td> <td>7</td> <td></td><td>8</td> <td>GPIO 14 (TxD)</td></tr>
<tr><td></td> <td>9</td> <td></td><td>10</td> <td>GPIO 15 (RxD)</td></tr>
<tr><td>GPIO 17</td> <td>11</td> <td></td><td>12</td> <td>GPIO 18 (PCM_CLK)</td></tr>
<tr><td><b>GPIO 27</b></td> <td>13</td> <td></td><td>14</td> <td></td></tr>
<tr><td>GPIO 22</td> <td>15</td> <td></td><td>16</td> <td>GPIO 23</td></tr>
<tr><td></td> <td>17</td> <td></td><td>18</td> <td>GPIO 24</td></tr>
<tr><td>GPIO 10 (MOSI)</td> <td>19</td> <td></td><td>20</td> <td></td></tr>
<tr><td>GPIO 9 (MISO)</td> <td>21</td> <td></td><td>22</td> <td>GPIO 25</td></tr>
<tr><td>GPIO 11 (SCLK)</td> <td>23</td> <td></td><td>24</td> <td>GPIO 8 (CE0)</td></tr>
<tr><td></td> <td>25</td> <td></td><td>26</td> <td>GPIO 7 (CE1)</td></tr></table>
</td>
<td>
PCB Revision 2 P5 pin header
<table border="2" cellspacing="0" cellpadding="4" rules="all" style="margin:1em 1em 1em 0; border:solid 1px #000000; border-collapse:collapse; font-size:80%; empty-cells:show;">
<tr><td>Function</td> <td>Pin</td><td></td><td>Pin</td><td>Function</td></tr>
<tr><td>5V</td> <td>1</td> <td></td><td>2</td> <td>3,3V</td></tr>
<tr><td>GPIO 28 (SDA0)</td><td>3</td> <td></td><td>4</td> <td>GPIO 29 (SCL0)</td></tr>
<tr><td>GPIO 30</td> <td>5</td> <td></td><td>6</td> <td>GPOI 31</td></tr>
<tr><td>GND</td> <td>7</td> <td></td><td>8</td> <td>GND</td></tr></table>
</td>
</table>
Beispiele: Beispiele:
<pre> <pre>
define Pin12 RPI_GPIO 18 define Pin12 RPI_GPIO 18

629
fhem/FHEM/52_I2C_BME280.pm Normal file
View File

@ -0,0 +1,629 @@
# $Id$
=head1
52_I2C_BME280.pm
=head1 SYNOPSIS
Modul for FHEM for reading a BME280 digital pressure/humidity sensor via I2C
=cut
package main;
use strict;
use warnings;
use constant {
BME280_I2C_ADDRESS => 0x76,
};
##################################################
# Forward declarations
#
sub I2C_BME280_I2CRec ($$);
sub I2C_BME280_GetReadings ($$);
sub I2C_BME280_GetTemp ($@);
sub I2C_BME280_GetPress ($@);
sub I2C_BME280_GetHum ($@);
sub I2C_BME280_calcTrueTemperature($$);
sub I2C_BME280_calcTrueHumidity($$);
sub I2C_BME280_calcTruePressure($$);
my %sets = (
'readValues' => 1,
);
sub I2C_BME280_Initialize($) {
my ($hash) = @_;
$hash->{DefFn} = 'I2C_BME280_Define';
$hash->{InitFn} = 'I2C_BME280_Init';
$hash->{AttrFn} = 'I2C_BME280_Attr';
$hash->{SetFn} = 'I2C_BME280_Set';
#$hash->{GetFn} = 'I2C_BME280_Get';
$hash->{UndefFn} = 'I2C_BME280_Undef';
$hash->{I2CRecFn} = 'I2C_BME280_I2CRec';
$hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 poll_interval:1,2,5,10,20,30 ' .
'oversampling_t:0,1,2,3,4,5 oversampling_p:0,1,2,3,4,5 oversampling_h:0,1,2,3,4,5 ' .
'roundPressureDecimal:0,1,2 roundTemperatureDecimal:0,1,2 roundHumidityDecimal:0,1,2 ' .
$readingFnAttributes;
$hash->{DbLog_splitFn} = "I2C_BME280_DbLog_splitFn";
}
sub I2C_BME280_Define($$) {
my ($hash, $def) = @_;
my @a = split('[ \t][ \t]*', $def);
$hash->{STATE} = 'defined';
my $name = $a[0];
my $msg = '';
if((@a < 2)) {
$msg = 'wrong syntax: define <name> I2C_BME280 [I2C-Address]';
}
if ($msg) {
Log3 ($hash, 1, $msg);
return $msg;
}
if ($main::init_done) {
eval { I2C_BME280_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_BME280_Catch($@) if $@;
}
}
sub I2C_BME280_Init($$) { # wird bei FHEM start Define oder wieder
my ( $hash, $args ) = @_;
my $name = $hash->{NAME};
if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0x.*$/ ? oct($address) : $address;
return "$name: I2C Address not valid" unless ($hash->{I2C_Address} < 128 && $hash->{I2C_Address} > 3);
} else {
$hash->{I2C_Address} = BME280_I2C_ADDRESS;
}
my $msg = '';
# create default attributes
#if (AttrVal($name, 'poll_interval', '?') eq '?') {
# $msg = CommandAttr(undef, $name . ' poll_interval 5');
# if ($msg) {
# Log3 ($hash, 1, $msg);
# return $msg;
# }
#}
eval {
AssignIoPort($hash, AttrVal($hash->{NAME},"IODev",undef));
I2C_BME280_i2cread($hash, 0xD0, 1); #get Id
$hash->{STATE} = 'getCalData';
I2C_BME280_i2cread($hash, 0x88, 26);
I2C_BME280_i2cread($hash, 0xE1, 8);
};
return I2C_BME280_Catch($@) if $@;
}
sub I2C_BME280_Catch($) { # Fehlermeldungen formattieren
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}
sub I2C_BME280_Attr (@) { # Wird beim Attribut anlegen/aendern aufgerufen
my ($command, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
if (defined $command && $command eq "set" && $attr eq "IODev") {
eval {
if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
main::AssignIoPort($hash,$val);
my @def = split (' ',$hash->{DEF});
I2C_LCD_Init($hash,\@def) if (defined ($hash->{IODev}));
}
};
$msg = I2C_BME280_Catch($@) if $@;
} elsif ($attr eq 'poll_interval') {
if (defined($val)) {
if ($val =~ m/^(0*[1-9][0-9]*)$/) {
RemoveInternalTimer($hash);
I2C_BME280_Poll($hash) if ($main::init_done);
#InternalTimer(gettimeofday() + 5, 'I2C_BME280_Poll', $hash, 0) if ($main::init_done);
} else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
}
} else {
RemoveInternalTimer($hash);
}
} elsif ($attr =~ m/^oversampling_.$/ && defined($val)) {
$msg = 'Wrong value: $val for $attr defined. value must be a one of 0,1,2,3,4,5' unless ($val =~ m/^(0*[0-5])$/);
} elsif ($attr =~ m/^round(Pressure|Temperature|Humidity)Decimal$/ && defined($val)) {
$msg = 'Wrong value: $val for $attr defined. value must be a one of 0,1,2' unless ($val =~ m/^(0*[0-2])$/);
}
return ($msg) ? $msg : undef;
}
sub I2C_BME280_Poll($) { # Messwerte regelmaessig anfordern
my ($hash) = @_;
my $name = $hash->{NAME};
I2C_BME280_Set($hash, ($name, 'readValues')); # Read values
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BME280_Poll', $hash, 0);
}
}
sub I2C_BME280_Set($@) { # Messwerte manuell anfordern
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) . ":noArg"
}
if ($cmd eq 'readValues') {
if (defined($hash->{calibrationData}{dig_H6})) { # query sensor
I2C_BME280_i2cwrite($hash, 0xF2, AttrVal($name, 'oversampling_h', 1) & 7);
my $data = ( AttrVal($name, 'oversampling_t', 1) & 7 ) << 5 | ( AttrVal($name, 'oversampling_p', 1) & 7 ) << 2 | 1; #Register 0xF4 “ctrl_meas” zusammenbasteln
I2C_BME280_i2cwrite($hash, 0xF4, $data);
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, 'I2C_BME280_UpdateReadings', $hash, 0); #nach 1s Werte auslesen
} else { #..but get calibration variables first
Log3 $hash, 5, "$name: in set but no calibrationData, requesting again";
I2C_BME280_i2cread($hash, 0x88, 26);
I2C_BME280_i2cread($hash, 0xE1, 16);
}
}
return undef
}
sub I2C_BME280_Get($@) { # Messwerte manuell anfordern
my ($hash, @a) = @_;
my $name = $a[0];
my $cmd = $a[1];
if (defined($cmd) && $cmd eq 'readValues') {
if (defined($hash->{calibrationData}{dig_H6})) { # query sensor
I2C_BME280_i2cwrite($hash, 0xF2, AttrVal($name, 'oversampling_h', 1) & 7);
my $data = ( AttrVal($name, 'oversampling_t', 1) & 7 ) << 5 | ( AttrVal($name, 'oversampling_p', 1) & 7 ) << 2 | 1; #Register 0xF4 “ctrl_meas” zusammenbasteln
I2C_BME280_i2cwrite($hash, 0xF4, $data);
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 1, 'I2C_BME280_UpdateReadings', $hash, 0); #nach 1s Werte auslesen
} else { #..but get calibration variables first
Log3 $hash, 5, "$name: in set but no calibrationData, requesting again";
I2C_BME280_i2cread($hash, 0x88, 26);
I2C_BME280_i2cread($hash, 0xE1, 16);
}
} else {
return 'Unknown argument ' . $cmd . ', choose one of readValues:noArg';
}
return undef
}
sub I2C_BME280_UpdateReadings($) { # Messwerte auslesen
my ($hash) = @_;
I2C_BME280_i2cread($hash, 0xF7, 8); # alle Werte auslesen
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); #poll_interval Timer wiederherstellen
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BME280_Poll', $hash, 0) if ($pollInterval > 0);
}
sub I2C_BME280_Undef($$) { # Device loeschen
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
return undef;
}
sub I2C_BME280_I2CRec ($$) { # wird vom IODev aus aufgerufen wenn I2C Daten vorliegen
my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME};
my $pname = undef;
my $phash = $hash->{IODev};
$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} && $clientmsg->{reg} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) {
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
Log3 $hash, 5, "$name Rx, Reg: $clientmsg->{reg}, Data: $clientmsg->{received}";
I2C_BME280_GetCal1 ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0x88 && $clientmsg->{nbyte} == 26;
I2C_BME280_GetCal2 ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xE1 && $clientmsg->{nbyte} == 8;
I2C_BME280_GetId ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xD0;
I2C_BME280_GetReadings ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xF7 && $clientmsg->{nbyte} == 8;
}
}
return undef
}
sub I2C_BME280_GetId ($$) { # empfangenes Id Byte auswerten
my ($hash, $rawdata) = @_;
if ($rawdata == hex("60")) {
$hash->{DeviceType} = "BME280";
} elsif ($rawdata == hex("58")) {
$hash->{DeviceType} = "BMP280";
} if ($rawdata == hex("56") || $rawdata == hex("57")) {
$hash->{DeviceType} = "BMP280s";
}
}
sub I2C_BME280_GetCal1 ($$) { # empfangene Cal Daten in Internals Speichern
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my $n = 0;
$hash->{calibrationData}{dig_T1} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++], 0); # unsigned
$hash->{calibrationData}{dig_T2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_T3} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P1} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++], 0); # unsigned
$hash->{calibrationData}{dig_P2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P3} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P4} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P5} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P6} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P7} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P8} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_P9} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$n++;
$hash->{calibrationData}{dig_H1} = $raw[$n++]; # unsigned
$hash->{STATE} = 'First calibration block received';
return
}
sub I2C_BME280_GetCal2 ($$) { # empfangene Cal Daten in Internals Speichern Teil 2
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my $n = 0;
$hash->{calibrationData}{dig_H2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
$hash->{calibrationData}{dig_H3} = $raw[$n++]; # unsigned
$hash->{calibrationData}{dig_H4} = ($raw[$n++] << 4) | $raw[$n] & 0xF; # signed word, kann aber nur positiv sein, da nur 12 bit
$hash->{calibrationData}{dig_H5} = (($raw[$n++] >> 4) & 0x0F) | ($raw[$n++] << 4); # signed word, kann aber nur positiv sein, da nur 12 bit
$hash->{calibrationData}{dig_H6} = I2C_BME280_GetCalVar($raw[$n++], undef); # signed 8bit #geht das? oder muss I2C_BME280_GetCalVar ($;$$) angepasst werden
$hash->{STATE} = 'Initialized';
I2C_BME280_Poll($hash) if defined(AttrVal($hash->{NAME}, 'poll_interval', undef)); # wenn poll_interval definiert -> timer starten
return
}
sub I2C_BME280_GetCalVar ($$;$) { # Variablen aus Bytes zusammenbauen (signed und unsigned)
my ($lsb, $msb, $returnSigned) = @_;
$returnSigned = (!defined($returnSigned) || $returnSigned == 1) ? 1 : 0;
my $retVal = undef;
if (defined $msb) { # 16 bit Variable
$retVal = $msb << 8 | $lsb;
# check if we need return signed or unsigned int
if ($returnSigned == 1) {
$retVal = $retVal >> 15 ? $retVal - 2**16 : $retVal;
}
} else { # 8 bit Variable
$retVal = $lsb >> 7 ? $lsb - 2 ** 8 : $lsb;
}
return $retVal;
}
sub I2C_BME280_GetReadings ($$) { # Empfangene Messwerte verarbeiten
my ($hash, $rawdata) = @_;
my @raw = split(" ",$rawdata);
my @pres = splice(@raw,0,3);
my @temp = splice(@raw,0,3);
I2C_BME280_GetTemp ($hash, @temp );
I2C_BME280_GetPress ($hash, @pres);
I2C_BME280_GetHum ($hash, @raw );
my $tem = ReadingsVal($hash->{NAME},"temperature", undef);
my $hum = ReadingsVal($hash->{NAME},"humidity", undef);
my $prs = ReadingsVal($hash->{NAME},"pressure", undef);
readingsSingleUpdate(
$hash,
'state',
((defined $tem ? "T: $tem " : "") . (defined $hum ? "H: $hum " : "") . (defined $prs ? ("P: $prs P-NN: " . ReadingsVal($hash->{NAME},"pressure-nn", 0)) : "")),
1
);
}
sub I2C_BME280_GetTemp ($@) { # Temperatur Messwerte verarbeiten
my ($hash, @raw) = @_;
if ( $raw[0] == 0x80 && $raw[1] == 0 && $raw[2] == 0 ) { # 0x80000 (MSB = 0x80) wird ausgegeben, wenn Temperaturmessung deaktiviert (oversampling_t = 0)
Log3 $hash, 4, "temperature reading deleted due to oversampling_t = 0";
delete ($hash->{READINGS}{temperature});
} else {
my $ut = $raw[0] << 12 | $raw[1] << 4 | $raw[2] >> 4 ;
my $temperature = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
I2C_BME280_calcTrueTemperature($hash, $ut)
);
readingsSingleUpdate($hash, 'temperature', $temperature, 1);
}
}
sub I2C_BME280_GetPress ($@) { # Luftdruck Messwerte verarbeiten
my ($hash, @raw) = @_;
if ( $raw[0] == 0x80 && $raw[1] == 0 && $raw[2] == 0 ) { # 0x80000 (MSB = 0x80) wird ausgegeben, wenn Luftdruckmessung deaktiviert (oversampling_p = 0)
Log3 $hash, 4, "pressure readings seleted due to oversampling_p = 0";
delete ($hash->{READINGS}{'pressure'});
delete ($hash->{READINGS}{'pressure-nn'});
} else {
my $up = $raw[0] << 12 | $raw[1] << 4 | $raw[2] >> 4 ;
my $pressure = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
I2C_BME280_calcTruePressure($hash, $up) / 100
);
my $altitude = AttrVal('global', 'altitude', 0);
# simple barometric height formula
my $pressureNN = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
$pressure + ($altitude / 8.5)
);
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'pressure', $pressure);
readingsBulkUpdate($hash, 'pressure-nn', $pressureNN);
readingsEndUpdate($hash, 1);
}
}
sub I2C_BME280_GetHum ($@) { # Luftfeuchte Messwerte verarbeiten
my ($hash, @raw) = @_;
if ( $raw[0] == 0x80 && $raw[1] == 0 ) { # 0x8000 (MSB = 0x80) wird ausgegeben, wenn Feuchtemessung deaktiviert (oversampling_h = 0)
Log3 $hash, 4, "humidity readings seleted due to oversampling_h = 0";
delete ($hash->{READINGS}{humidity})
} else {
my $uh = $raw[0] << 8 | $raw[1];
my $humidity = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundHumidityDecimal', 1) . 'f',
I2C_BME280_calcTrueHumidity($hash, $uh)
);
readingsSingleUpdate($hash, 'humidity', $humidity, 1);
}
}
sub I2C_BME280_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an)
my ($hash, $reg, $nbyte) = @_;
if (defined (my $iodev = $hash->{IODev})) {
Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} read $nbyte Byte from Register $reg";
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cread",
i2caddress => $hash->{I2C_Address},
reg => $reg,
nbyte => $nbyte
});
} else {
return "no IODev assigned to '$hash->{NAME}'";
}
}
sub I2C_BME280_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
my ($hash, $reg, @data) = @_;
if (defined (my $iodev = $hash->{IODev})) {
Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . join (' ',@data) . " to Register $reg";
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cwrite",
i2caddress => $hash->{I2C_Address},
reg => $reg,
data => join (' ',@data),
});
} else {
return "no IODev assigned to '$hash->{NAME}'";
}
}
sub I2C_BME280_calcTrueTemperature($$) { # Temperatur aus Rohwerten berechnen
my ($hash, $ut) = @_;
my $dig_T1 = $hash->{calibrationData}{dig_T1};
my $dig_T2 = $hash->{calibrationData}{dig_T2};
my $dig_T3 = $hash->{calibrationData}{dig_T3};
my $h1 = ( $ut / 16384.0 - $dig_T1 / 1024.0 ) * $dig_T2;
my $h2 = ( ( $ut / 131072.0 - $dig_T1 / 8192.0 ) * ( $ut / 131072.0 - $dig_T1 / 8192.0 ) ) * $dig_T3;
$hash->{calibrationData}{t_fine} = $h1 + $h2;
my $ct = $hash->{calibrationData}{t_fine} / 5120.0;
return $ct;
}
sub I2C_BME280_calcTruePressure($$) { # Luftdruck aus Rohwerten berechnen
my ($hash, $up) = @_;
my $t_fine = $hash->{calibrationData}{t_fine};
my $dig_P1 = $hash->{calibrationData}{dig_P1};
my $dig_P2 = $hash->{calibrationData}{dig_P2};
my $dig_P3 = $hash->{calibrationData}{dig_P3};
my $dig_P4 = $hash->{calibrationData}{dig_P4};
my $dig_P5 = $hash->{calibrationData}{dig_P5};
my $dig_P6 = $hash->{calibrationData}{dig_P6};
my $dig_P7 = $hash->{calibrationData}{dig_P7};
my $dig_P8 = $hash->{calibrationData}{dig_P8};
my $dig_P9 = $hash->{calibrationData}{dig_P9};
my $h1 = ($t_fine / 2) - 64000.0;
my $h2 = $h1 * $h1 * $dig_P6 / 32768;
$h2 = $h2 + $h1 * $dig_P5 * 2;
$h2 = $h2 / 4 + $dig_P4 * 65536;
#$h1 = $dig_P3 * $h1 * $h1 / 524288 + $dig_P2 * $h1 / 524288;
$h1 = ((($dig_P3 * ((($h1/4.0) * ($h1/4.0)) / 8192)) / 8) + (($dig_P2 * $h1) / 2.0)) / 262144;
#$h1 = ( 1 + $h1 / 32768) * $dig_P1;
$h1 = ((32768 + $h1) * $dig_P1) / 32768;
return 0 if ($h1 == 0);
my $p = ((1048576 - $up) - ($h2 / 4096)) * 3125;
if ($p < 0x80000000) {
$p = ($p * 2) / $h1;
} else {
$p = ($p / $h1) * 2 ;
}
#$p = ( $p - $h2 / 4096 ) * 6250 / $h1;
$h1 = ($dig_P9 * ((($p/8.0) * ($p/8.0)) / 8192.0)) / 4096;
$h2 = (($p/4.0) * $dig_P8) / 8192.0;
$p = $p + (($h1 + $h2 + $dig_P7) / 16);
return $p;
}
sub I2C_BME280_calcTrueHumidity($$) { # Luftfeuchte aus Rohwerten berechnen
my ($hash, $uh) = @_;
my $t_fine = $hash->{calibrationData}{t_fine};
my $dig_H1 = $hash->{calibrationData}{dig_H1};
my $dig_H2 = $hash->{calibrationData}{dig_H2};
my $dig_H3 = $hash->{calibrationData}{dig_H3};
my $dig_H4 = $hash->{calibrationData}{dig_H4};
my $dig_H5 = $hash->{calibrationData}{dig_H5};
my $dig_H6 = $hash->{calibrationData}{dig_H6};
my $t1 = $t_fine - 76800;
$t1 = ( $uh - ( $dig_H4 * 64 + $dig_H5 / 16384 * $t1 ) ) * ( $dig_H2 / 65536 * ( 1 + $dig_H6 / 67108864 * $t1 * ( 1 + $dig_H3 / 67108864 * $t1 ) ) );
$t1 = $t1 * ( 1 - $dig_H1 * $t1 / 524288);
if ($t1 > 100) {
$t1 = 100;
} elsif ($t1 < 0) {
$t1 = 0;
}
return $t1;
}
sub I2C_BME280_DbLog_splitFn($) { # Einheiten
my ($event) = @_;
Log3 undef, 5, "in DbLog_splitFn empfangen: $event";
my ($reading, $value, $unit) = "";
my @parts = split(/ /,$event);
$reading = shift @parts;
$reading =~ tr/://d;
$value = $parts[0];
$unit = "\xB0C" if(lc($reading) =~ m/temp/);
$unit = "hPa" if(lc($reading) =~ m/pres/);
$unit = "%" if(lc($reading) =~ m/humi/);
return ($reading, $value, $unit);
}
1;
=pod
=begin html
<a name="I2C_BME280"></a>
<h3>I2C_BME280</h3>
(en | <a href="commandref_DE.html#I2C_BME280">de</a>)
<ul>
<a name="I2C_BME280"></a>
Provides an interface to the digital pressure/humidity sensor BME280
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>
<b>Define</b>
<ul>
<code>define BME280 I2C_BME280 [&lt;I2C Address&gt;]</code><br><br>
without defined <code>&lt;I2C Address&gt;</code> 0x76 will be used as address<br>
<br>
Examples:
<pre>
define BME280 I2C_BME280 0x77
attr BME280 poll_interval 5
</pre>
</ul>
<a name="I2C_BME280set"></a>
<b>Set</b>
<ul>
<code>set BME280 &lt;readValues&gt;</code>
<br><br>
Reads current temperature, humidity and pressure values from the sensor.<br>
Normaly this execute automaticly at each poll intervall. You can execute
this manually if you want query the current values.
<br><br>
</ul>
<a name="I2C_BME280attr"></a>
<b>Attributes</b>
<ul>
<li>oversampling_t,oversampling_h,oversampling_p<br>
Controls the oversampling settings of the temperature,humidity or pressure measurement in the sensor.<br>
Default: 1, valid values: 0, 1, 2, 3, 4, 5<br>
0 switches the respective measurement off<br>
1 to 5 complies to oversampling value 2^value/2<br><br>
</li>
<li>poll_interval<br>
Set the polling interval in minutes to query the sensor for new measured
values.<br>
Default: 5, valid values: any whole number<br><br>
</li>
<li>roundTemperatureDecimal,roundHumidityDecimal,roundPressureDecimal<br>
Round temperature, humidity or pressure values to given decimal places.<br>
Default: 1, valid values: 0, 1, 2<br><br>
</li>
<li>altitude<br>
if set, this altitude is used for calculating the pressure related to sea level (nautic null) NN<br><br>
Note: this is a global attributes, e.g<br>
<code>attr global altitude 220</code>
</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_BME280"></a>
<h3>I2C_BME280</h3>
(<a href="commandref.html#I2C_BME280">en</a> | de)
<ul>
<a name="I2C_BME280"></a>
Erm&ouml;glicht die Verwendung eines digitalen (Luft)druck/feuchtesensors BME280 &uuml;ber den I2C Bus des Raspberry Pi.<br><br>
I2C-Botschaften werden &uuml;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>
<b>Define</b>
<ul>
<code>define BME280 &lt;BME280_name&gt; [&lt;I2C Addresse&gt;]</code><br><br>
Fehlt <code>&lt;I2C Address&gt;</code> wird 0x76 verwendet<br>
<br>
Beispiel:
<pre>
define BME280 I2C_BME280 0x77
attr BME280 poll_interval 5
</pre>
</ul>
<a name="I2C_BME280set"></a>
<b>Set</b>
<ul>
<code>set BME280 readValues</code>
<br><br>
<code>set &lt;name&gt; readValues</code><br>
Aktuelle Temperatur, Feuchte und Luftdruck Werte vom Sensor lesen.<br><br>
</ul>
<a name="I2C_BME280attr"></a>
<b>Attribute</b>
<ul>
<li>oversampling_t,oversampling_h,oversampling_p<br>
Steuert das jeweils das Oversampling der Temperatur-, Feuchte-, oder Druckmessung im Sensor.<br>
Standard: 1, g&uuml;ltige Werte: 0, 1, 2, 3, 4, 5<br>
0 deaktiviert die jeweilige Messung<br>
1 to 5 entspricht einem Oversampling von 2^zahl/2<br><br>
</li>
<li>poll_interval<br>
Definiert das Poll Intervall in Minuten f&uuml;r das Auslesen einer neuen Messung.<br>
Default: 5, g&uuml;ltige Werte: 1, 2, 5, 10, 20, 30<br><br>
</li>
<li>roundTemperatureDecimal, roundHumidityDecimal, roundPressureDecimal<br>
Rundet jeweils den Temperatur-, Feuchte-, oder Druckwert mit den angegebenen Nachkommastellen.<br>
Standard: 1, g&uuml;ltige Werte: 0, 1, 2<br><br>
</li>
<li>altitude<br>
Wenn dieser Wert definiert ist, wird diese Angabe zus&auml; f&uuml;r die Berechnung des
Luftdrucks bezogen auf Meeresh&ouml;he (Normalnull) NN herangezogen.<br>
Bemerkung: Dies ist ein globales Attribut.<br><br>
<code>attr global altitude 220</code>
</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

View File

@ -1,5 +1,5 @@
############################################################################## ##############################################################################
# $Id: 52_I2C_EEPROM.pm 7007 2014-11-17 18:03:42Z klauswitt $ # $Id$
############################################################################## ##############################################################################
# Modul for I2C EEPROM # Modul for I2C EEPROM
# #

View File

@ -1,5 +1,5 @@
############################################################################## ##############################################################################
# $Id: 52_I2C_MCP23008.pm 6209 2014-07-06 21:38:39Z klauswitt $ # $Id$
############################################################################## ##############################################################################
# Modul for I2C GPIO Extender MCP23008 # Modul for I2C GPIO Extender MCP23008
# #

View File

@ -1,5 +1,5 @@
############################################## ##############################################
# $Id: 52_MCP342x.pm 5865 2014-05-14 23:00:12Z klauswitt $ # $Id$
package main; package main;

View File

@ -25,7 +25,6 @@ use strict;
use warnings; use warnings;
use SetExtensions; use SetExtensions;
#use POSIX; #use POSIX;
use Scalar::Util qw(looks_like_number);
my $setdim = ":slider,0,1,4095 "; my $setdim = ":slider,0,1,4095 ";
@ -34,23 +33,32 @@ my %setsP = (
'on' => 1, 'on' => 1,
); );
my %confregs = (
0 => 'modereg1',
1 => 'modereg2',
2 => 'SUBADR1',
3 => 'SUBADR2',
4 => 'SUBADR3',
5 => 'ALLCALLADR',
);
my %defaultreg = ( my %defaultreg = (
'modereg1' => 32, #32-> Bit 5 -> Autoincrement 'modereg1' => 32, #32-> Bit 5 -> Autoincrement
'modereg2' => 0, 'modereg2' => 0,
'sub1' => 113, 'SUBADR1' => 113,
'sub2' => 114, 'SUBADR2' => 114,
'sub3' => 116, 'SUBADR3' => 116,
'allc' => 112, 'ALLCALLADR' => 112,
'presc' => 30, 'PRESCALE' => 30,
); );
my %mr1 = ( my %mr1 = (
'EXTCLK' => 64, 'EXTCLK' => 64,
'SLEEP' => 16, 'SLEEP' => 16,
'SUB1' => 8, 'SUBADR1' => 8,
'SUB2' => 4, 'SUBADR2' => 4,
'SUB3' => 2, 'SUBADR3' => 2,
'ALLCALL' => 1, 'ALLCALLADR' => 1,
); );
my %mr2 = ( my %mr2 = (
@ -69,22 +77,14 @@ sub I2C_PCA9685_Initialize($) { #
$hash->{AttrFn} = "I2C_PCA9685_Attr"; $hash->{AttrFn} = "I2C_PCA9685_Attr";
$hash->{StateFn} = "I2C_PCA9685_State"; $hash->{StateFn} = "I2C_PCA9685_State";
$hash->{SetFn} = "I2C_PCA9685_Set"; $hash->{SetFn} = "I2C_PCA9685_Set";
$hash->{GetFn} = "I2C_PCA9685_Get"; #$hash->{GetFn} = "I2C_PCA9685_Get";
$hash->{I2CRecFn} = "I2C_PCA9685_I2CRec"; $hash->{I2CRecFn} = "I2C_PCA9685_I2CRec";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ". $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
"prescale:slider,0,1,255 OnStartup ". "prescale:slider,0,1,255 OnStartup ".
"subadr1 subadr2 subadr3 allcalladr ". "SUBADR1 SUBADR2 SUBADR3 ALLCALLADR ".
"modreg1:multiple-strict,EXTCLK,SUB1,SUB2,SUB3,ALLCALL ". "modereg1:multiple-strict,EXTCLK,SUBADR1,SUBADR2,SUBADR3,ALLCALLADR ".
"modreg2:multiple-strict,INVRT,OCH,OUTDRV,OUTNE0,OUTNE1 ". "modereg2:multiple-strict,INVRT,OCH,OUTDRV,OUTNE0,OUTNE1 ".
"$readingFnAttributes dummy:0,1"; "$readingFnAttributes dummy:0,1 extClock";
}
#############################################################################
sub I2C_PCA9685_SetState($$$$) { #-------wozu?
my ($hash, $tim, $vt, $val) = @_;
$val = $1 if($val =~ m/^(.*) \d+$/);
#return "Undefined value $val" if(!defined($it_c2b{$val}));
return undef;
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Define($$) { sub I2C_PCA9685_Define($$) {
@ -98,45 +98,74 @@ sub I2C_PCA9685_Define($$) {
return undef; return undef;
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Init($$) { # sub I2C_PCA9685_Init($$) { # wird ausgefuehrt bei Initialisierung und Connect/Reconnect des DEVio
my ( $hash, $args ) = @_; my ( $hash, $args ) = @_;
#my @a = split("[ \t]+", $args); #my @a = split("[ \t]+", $args);
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if (defined $args && int(@$args) != 1) { if (defined $args && int(@$args) < 1) {
return "Define: Wrong syntax. Usage:\n" . return "Define: Wrong syntax. Usage:\n" .
"define <name> I2C_PCA9685 <i2caddress>"; "define <name> I2C_PCA9685 <i2caddress>";
} }
#return "$name I2C Address not valid" unless ($a[0] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi);
my $msg = undef;
if (defined (my $address = shift @$args)) { if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; $hash->{I2C_Address} = $address =~ /^0x.*$/ ? oct($address) : $address;
return "$name: I2C Address not valid" unless ($hash->{I2C_Address} < 128 && $hash->{I2C_Address} > 3);
} else { } else {
return "$name I2C Address not valid"; return "$name: no I2C Address defined" unless defined($hash->{I2C_Address});
} }
AssignIoPort($hash); if (defined (my $maxbuff = shift @$args)) {
#Mode register wiederherstellen return "$name: I2C buffer size must be a number" if $maxbuff =~ m/^d+$/;
I2C_PCA9685_i2cread($hash, 1, 1); # Modreg2 schonmal lesen $hash->{I2C_Buff} = $maxbuff;
I2C_PCA9685_i2cread($hash, 254, 1); # Frequenz fuer Internal
select(undef, undef, undef, 0.1);
I2C_PCA9685_Attr(undef, $name, "modreg1", AttrVal($name, "modreg1", ""));
I2C_PCA9685_Attr(undef, $name, "modreg2", AttrVal($name, "modreg2", "OUTDRV"));
#alternative I2C Adressen wiederherstellen
I2C_PCA9685_i2cwrite($hash,AttrVal($name, $defaultreg{'sub1'}, "subadr1") << 1, 2) if defined AttrVal($name, "subadr1", undef);
I2C_PCA9685_i2cwrite($hash,AttrVal($name, $defaultreg{'sub2'}, "subadr2") << 1, 3) if defined AttrVal($name, "subadr2", undef);
I2C_PCA9685_i2cwrite($hash,AttrVal($name, $defaultreg{'sub3'}, "subadr3") << 1, 4) if defined AttrVal($name, "subadr3", undef);
I2C_PCA9685_i2cwrite($hash,AttrVal($name, $defaultreg{'allc'}, "allcalladr") << 1, 5) if defined AttrVal($name, "allcalladr", undef);
#PWM Frequenz wiederherstellen
I2C_PCA9685_Attr(undef, $name, "prescale", AttrVal($name, "prescale", $defaultreg{'presc'})) if defined AttrVal($name, "prescale", undef);
#Portzustände wiederherstellen
foreach (0..15) {
my $port = "Port".sprintf ('%02d', $_);
I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
} }
$hash->{STATE} = 'Initialized'; my $msg = '';
return; eval {
Log3 $hash, 4, "$hash->{NAME}: Init1 Konfigurationsregister auslesen";
AssignIoPort($hash);
#Config Register lesen (einzeln, da Blockweises lesen noch aktiviert werden muss)
I2C_PCA9685_i2cread($hash, 0, 1); # Modereg1
I2C_PCA9685_i2cread($hash, 1, 1); # Modereg2
I2C_PCA9685_i2cread($hash, 2, 1); # Subadr1
I2C_PCA9685_i2cread($hash, 3, 1); # Subadr2
I2C_PCA9685_i2cread($hash, 4, 1); # Subadr3
I2C_PCA9685_i2cread($hash, 5, 1); # Allcalladr
I2C_PCA9685_i2cread($hash, 254, 1); # Frequenz fuer Internal
$hash->{STATE} = 'Initializing';
InternalTimer(gettimeofday() + 10, 'I2C_PCA9685_InitError', $hash, 0); # nach 10s Initialisierungsfehler ablegen
};
return I2C_BME280_Catch($@) if $@;
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Catch($) { # sub I2C_PCA9685_Init2($) { # wird audgefuehrt wenn Frequenzregisterinhalt empfangen wird und entsprechendes Internal noch leer ist
my ( $hash ) = @_;
my $name = $hash->{NAME};
eval {
Log3 $hash, 4, "$hash->{NAME}: Init2 Konfigurationsregister beschreiben";
RemoveInternalTimer($hash); # Timer fuer Initialisierungsfehler stoppen
# Mode register wiederherstellen
I2C_PCA9685_Attr(undef, $name, "modereg1", AttrVal($name, "modereg1", undef));
I2C_PCA9685_Attr(undef, $name, "modereg2", AttrVal($name, "modereg2", undef));
# alternative I2C Adressen wiederherstellen
I2C_PCA9685_Attr(undef, $name, "SUBADR1", AttrVal($name, "SUBADR1", undef));
I2C_PCA9685_Attr(undef, $name, "SUBADR2", AttrVal($name, "SUBADR2", undef));
I2C_PCA9685_Attr(undef, $name, "SUBADR3", AttrVal($name, "SUBADR3", undef));
I2C_PCA9685_Attr(undef, $name, "ALLCALLADR", AttrVal($name, "ALLCALLADR", undef));
# PWM Frequenz wiederherstellen
I2C_PCA9685_Attr(undef, $name, "prescale", AttrVal($name, "prescale", undef));
#Portzustände wiederherstellen
foreach (0..15) {
my $port = "Port".sprintf ('%02d', $_);
I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
}
$hash->{STATE} = 'Initialized';
};
return I2C_BME280_Catch($@) if $@;
}
#############################################################################
sub I2C_PCA9685_InitError($) { # wird audgefuehrt wenn 10s nach Init immer noch keine Registerwerte empfangen wurden
my ( $hash ) = @_;
$hash->{STATE} = 'Error during Initialisation';
}
#############################################################################
sub I2C_PCA9685_Catch($) { # Fehlermeldung von eval formattieren
my $exception = shift; my $exception = shift;
if ($exception) { if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/; $exception =~ /^(.*)( at.*FHEM.*)$/;
@ -147,7 +176,7 @@ sub I2C_PCA9685_Catch($) { #
############################################################################# #############################################################################
sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
my ($hash, $tim, $sname, $sval) = @_; my ($hash, $tim, $sname, $sval) = @_;
Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; Log3 $hash, 5, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim";
if ($sname =~ m/^Port(((0|)[0-9])|(1[0-5]))$/i) { if ($sname =~ m/^Port(((0|)[0-9])|(1[0-5]))$/i) {
substr($sname,0,4,""); substr($sname,0,4,"");
$sname = sprintf('%d', $sname); $sname = sprintf('%d', $sname);
@ -156,7 +185,7 @@ sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
Log3 $hash, 5, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf $onstart{$sname} gesetzt werden"; Log3 $hash, 5, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf $onstart{$sname} gesetzt werden";
readingsSingleUpdate($hash,"Port". sprintf('%02d', $sname), $onstart{$sname}, 1); readingsSingleUpdate($hash,"Port". sprintf('%02d', $sname), $onstart{$sname}, 1);
} else { } else {
Log3 $hash, 5, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf Altzustand: $sval gesetzt werden"; Log3 $hash, 4, "$hash->{NAME}: Port" . sprintf('%02d', $sname) . " soll auf Altzustand: $sval gesetzt werden";
$hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{VAL} = $sval; $hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{VAL} = $sval;
$hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{TIME} = $tim; $hash->{READINGS}{'Port'. sprintf('%02d', $sname)}{TIME} = $tim;
} }
@ -164,12 +193,15 @@ sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
return undef; return undef;
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Undefine($$) { # sub I2C_PCA9685_Undefine($$) { # wird beim loeschen des Device ausgefuehrt
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
my ($msg, $data, $reg) = I2C_PCA9685_CalcRegs($hash, 61, 'off', undef); # Registerinhalte berechnen alle Ports aus
$msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
RemoveInternalTimer($hash);
return undef return undef
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Attr(@) { # sub I2C_PCA9685_Attr(@) { # wird beim setzen eines Attributes ausgefuehrt
my ($command, $name, $attr, $val) = @_; my ($command, $name, $attr, $val) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
my $msg = ''; my $msg = '';
@ -180,69 +212,80 @@ sub I2C_PCA9685_Attr(@) { #
I2C_PCA9685_Init($hash,\@def) if (defined ($hash->{IODev})); I2C_PCA9685_Init($hash,\@def) if (defined ($hash->{IODev}));
} }
} elsif ($attr && $attr =~ m/^prescale$/i) { # Frequenz } elsif ($attr && $attr =~ m/^prescale$/i) { # Frequenz
return undef unless ($main::init_done); $val = $defaultreg{'PRESCALE'} unless (defined($val)); #beim loeschen wieder auf Standard setzen
$val = 30 unless (defined($val)); #beim loeschen wieder auf Standard setzen
return "wrong value: $val for \"set $name $attr\" use 0-255" return "wrong value: $val for \"set $name $attr\" use 0-255"
unless(looks_like_number($val) && $val >= 0 && $val < 256); unless($val =~ m/^(\d+)$/ && $val >= 0 && $val < 256);
my $modereg1 = defined $hash->{confregs}{0} ? $hash->{confregs}{0} : $defaultreg{'modreg1'}; Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".$hash->{confregs}{PRESCALE}." neuer Wert: ".$val;
my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" }; if ($main::init_done && $val != $hash->{confregs}{PRESCALE}) {
$msg = I2C_PCA9685_i2cwrite($hash, 0, $modereg1mod); #sleep Mode aktivieren my $modereg1 = defined $hash->{confregs}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
$msg = I2C_PCA9685_i2cwrite($hash, 254 ,$val); #Frequenz aktualisieren my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
$msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1); #sleep Mode wieder aus $msg = I2C_PCA9685_i2cwrite($hash, 0, $modereg1mod); #sleep Mode aktivieren
foreach (0..15) { #Portzustände wiederherstellen $msg = I2C_PCA9685_i2cwrite($hash, 254 ,$val); #Frequenz aktualisieren
my $port = "Port".sprintf ('%02d', $_); $msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1); #sleep Mode wieder aus
I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) ); foreach (0..15) { #Portzustände wiederherstellen
my $port = "Port".sprintf ('%02d', $_);
I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
}
} }
} elsif ($attr && $attr =~ m/^(subadr[1-3])|allcalladr$/i) { # weitere I2C Adressen } elsif ($attr && $attr =~ m/^(SUBADR[1-3])|ALLCALLADR$/i) { # weitere I2C Adressen
return undef unless ($main::init_done); $val = $defaultreg{$attr} unless defined($val);
substr($attr,0,6,""); substr($attr,0,6,"");
my $regaddr = ($attr =~ m/^l/i) ? 5 : $attr + 1; my $regaddr = ($attr =~ m/^l/i) ? 5 : $attr + 1;
my $subadr = $val =~ /^0.*$/ ? oct($val) : $val; my $SUBADR = $val =~ /^0x.*$/ ? oct($val) : $val;
return "I2C Address not valid" if $subadr > 127; return "I2C Address not valid" if $SUBADR > 127;
$msg = I2C_PCA9685_i2cwrite($hash, $regaddr ,$subadr << 1); Log3 $hash, 5, $hash->{NAME} . ": $confregs{$regaddr} alter Wert: ".$hash->{confregs}{$confregs{$regaddr}}." neuer Wert: ".($SUBADR << 1);
} elsif ($attr && $attr =~ m/^modreg1$/i) { # Mode register 1 $msg = I2C_PCA9685_i2cwrite($hash, $regaddr ,$SUBADR << 1) if $main::init_done && ($SUBADR << 1) != $hash->{confregs}{$confregs{$regaddr}};
return undef unless ($main::init_done); } elsif ($attr && $attr =~ m/^modereg1$/i) { # Mode register 1
my @inp = split(/,/, $val) if defined($val); my @inp = split(/,/, $val) if defined($val);
my $data = 32; # Auto increment soll immer gesetzt sein my $data = 32; # Auto increment soll immer gesetzt sein
foreach (@inp) { foreach (@inp) {
return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr1{ $a } <=> $mr1{ $b } } keys %setsP) ) return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr1{ $a } <=> $mr1{ $b } } keys %setsP) )
unless(exists($mr1{$_})); unless(exists($mr1{$_}));
$data |= $mr1{$_}; $data |= $mr1{$_};
if ($_ eq "EXTCLK") { #wenn externer Oszillator genutzt werden soll, zuerst den sleep mode aktivieren (wenn er gelöscht wird dann noch reset machen) if ($main::init_done && $_ eq "EXTCLK" && ($hash->{confregs}{$confregs{0}} & $mr1{"EXTCLK"}) == 0) { #wenn externer Oszillator genutzt werden soll, zuerst den sleep mode aktivieren
my $modereg1 = defined $hash->{confregs}{0} ? $hash->{confregs}{0} : $defaultreg{'modreg1'}; my $modereg1 = defined $hash->{confregs}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" }; my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
Log3 $hash, 5, "$hash->{NAME}: sleep Mode aktivieren (Vorbereitung fuer EXTCLK)"; Log3 $hash, 5, "$hash->{NAME}: sleep Mode aktivieren (Vorbereitung fuer EXTCLK)";
$msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1mod); #sleep Mode aktivieren $msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1mod); #sleep Mode aktivieren
$data += $mr1{"SLEEP"}; # $data += $mr1{"SLEEP"}; #???????? muss hier nicht deaktiviert werden?????
} }
} }
if ( defined $hash->{confregs}{0} && ($hash->{confregs}{0} & $mr1{"EXTCLK"}) == $mr1{"EXTCLK"} && ($data & $mr1{"EXTCLK"}) == 0 ) { #reset wenn EXTCLK abgeschaltet wird if ($main::init_done && defined $hash->{confregs}{$confregs{0}} && ($hash->{confregs}{$confregs{0}} & $mr1{"EXTCLK"}) == $mr1{"EXTCLK"} && ($data & $mr1{"EXTCLK"}) == 0 ) { #reset wenn EXTCLK abgeschaltet wird
$msg = I2C_PCA9685_i2cwrite($hash, 0 , $data | 0x80); $msg = I2C_PCA9685_i2cwrite($hash, 0 , $data | 0x80);
} }
$msg = I2C_PCA9685_i2cwrite($hash, 0 , $data); Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".$hash->{confregs}{$confregs{0}}." neuer Wert: ".$data;
} elsif ($attr && $attr =~ m/^modreg2$/i) { #Mode register 2 if ( $main::init_done && $data != $hash->{confregs}{$confregs{0}} ) {
return undef unless ($main::init_done); I2C_PCA9685_UpdReadings($hash, 0, $data); #schonmal in den Internals ablegen lassen (damit wärend Initialisierung mit korrekten daten gearbeitet wird... bei Frequenz z.B.)
$msg = I2C_PCA9685_i2cwrite($hash, 0 , $data);
}
} elsif ($attr && $attr =~ m/^modereg2$/i) { #Mode register 2
my @inp = split(/,/, $val) if defined($val); my @inp = split(/,/, $val) if defined($val);
my $data = 0; # Auto increment soll immer gesetzt sein my $data = 0;
foreach (@inp) { foreach (@inp) {
return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr2{ $a } <=> $mr2{ $b } } keys %setsP) ) return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr2{ $a } <=> $mr2{ $b } } keys %setsP) )
unless(exists($mr2{$_})); unless(exists($mr2{$_}));
$data += $mr2{$_}; $data += $mr2{$_};
} }
$msg = I2C_PCA9685_i2cwrite($hash, 1, $data) if ($hash->{confregs}{1} != $data); Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".(defined($hash->{confregs}{$confregs{1}})?$hash->{confregs}{$confregs{1}}:"")." neuer Wert: ".$data;
$msg = I2C_PCA9685_i2cwrite($hash, 1, $data) if $main::init_done && $data != $hash->{confregs}{$confregs{1}};
} elsif ($attr && $attr eq "OnStartup") { } elsif ($attr && $attr eq "OnStartup") {
if (defined $val) { if (defined $val) {
foreach (split (/,/,$val)) { foreach (split (/,/,$val)) {
my @pair = split (/=/,$_); my @pair = split (/=/,$_);
$msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated <port>=on|off|0..4095|last where <port> = 0 - 15 " $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated <port>=on|off|0..4095|last where <port> = 0 - 15 "
unless ( scalar(@pair) == 2 && unless ( scalar(@pair) == 2 &&
(($pair[0] =~ m/(^[0-9]|1[0-5])$/i && (($pair[0] =~ m/(^[0-9]|1[0-5])$/i &&
( $pair[1] eq "last" || exists($setsP{$pair[1]}) || ( $pair[1] eq "last" || exists($setsP{$pair[1]}) ||
( $pair[1] =~ m/^\d+$/ && $pair[1] < 4095 ) ) ) ) ( $pair[1] =~ m/^\d+$/ && $pair[1] < 4095 ) ) ) )
); );
}
} }
} elsif ($attr && $attr eq "extClock") {
$val = defined($val) ? $val : 25;
return "wrong value: $val for \"set $name $attr\" use point number"
unless($val =~ m/^[1-9][0-9]*\.?[0-9]*$/);
I2C_PCA9685_i2cread($hash, 254, 1); # Frequenz fuer Internal neu auslesen und berechnen
} }
}
return ($msg) ? $msg : undef; return ($msg) ? $msg : undef;
} }
############################################################################# #############################################################################
@ -253,38 +296,78 @@ sub I2C_PCA9685_Set($@) { #
my $dimcount = AttrVal($name, "dimcount", "4095"); my $dimcount = AttrVal($name, "dimcount", "4095");
my $msg; my $msg;
my $str = join(' ',@rest); my $str = join(' ',@rest);
if ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))/i) { # (mehrere) Ports ( regex unfertig) #Log3 undef, 5, "$name: empfangen: $str";
#Log3 undef, 1, "$name: empfangen: $str"; if ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))/i && index($str, ',') == -1) { # Nur ein Port
if (index($str, ',') == -1) { # Nur ein Port my ($port, $dim, $delay) = split(' ', $str);
my ($port, $dim, $delay) = split(' ', $str); $port =~ tr/(P|p)(ort|)//d;
#Log3 undef, 1, "$name: ein Wert: $port, $dim, $delay"; #Log3 undef, 5, "$name: ein Port: $port, $dim, $delay";
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay); ($msg, my $data, my $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
} elsif ($str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))(( ){0,3},( ){0,3}(P(ort|)((0|)[0-9]|1[0-5])){1,})( ){1,3}\d*(( ){1,3}\d*)?( ){0,3}$/i ) { # Format P[ort]x,P[ort]y[,P..] Dimwert[ Delay] $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
my @einzel = split(',', $str); # } elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( *, *(P(ort|)((0|)[0-9]|1[0-5]))){1,} +\d+( +\d*)?/i ) { # Format P[ort]x,P[ort]y[,P..] Dimwert[ Delay]
my (undef, $dim, $delay) = split(' ', $einzel[$#einzel]); } elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( *, *(P(ort|)((0|)[0-9]|1[0-5]))){1,}/i ) { # Format P[ort]x,P[ort]y[,P..] Dimwert[ Delay]
foreach (reverse @einzel) { Log3 undef, 5, "mehrere ports und ein wert";
my ($port) = split(' ', $_); $str =~ tr/(P|p)(ort|)//d;
#Log3 undef, 1, "$name: mehrere Ports gleich: $port, $dim" . (defined $delay ? ", $delay" : "" ); my @einzel = split(',', $str);
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay); my @port;
last if defined($msg); my (undef, $dim, $delay) = split(' ', $einzel[$#einzel]);
} for my $i (0..$#einzel){
($port[$i]) = split(' ', $einzel[$i]);
} elsif ($str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?(( ){0,3},( ){0,3}(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?){1,}( ){0,3}$/i ) { # Mehrere Ports auf versch. Werte setzen
my @einzel = split(',', $str);
foreach (@einzel) {
my ($port, $dim, $delay) = split(' ', $_);
#Log3 undef, 1, "$name: mehrere Ports: $port, $dim" . (defined $delay ? ", $delay" : "" );
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay);
last if defined($msg);
}
} }
} elsif ($str =~ m/(a(ll|) \d{1,4}( \d{1,4})?)( ){0,3}$/i) { # Alle Ports gleichzeitig my ($data, $reg) = undef;
my $j = 1;
for my $i (0..$#einzel){
($msg, my $tdata, my $treg) = I2C_PCA9685_CalcRegs($hash, $port[$i], $dim, ( defined($delay) ? $delay : undef ) ); # Registerinhalte berechnen
return $msg if defined($msg);
Log3 $hash, 5, "$name: Port: $port[$i], Reg: $treg, Inhalt: $tdata, Rohwerte: $einzel[$i], Dimwert: $dim, Delay: ". ( defined($delay) ? ( $delay = "" ? "leer" : $delay ) : "leer" );
if ( defined($data) && defined($reg) ) { # bereits Werte für Ports vorhanden
$j += 1;
$data .= " " . $tdata;
} else {
$data = $tdata;
$reg = $treg;
}
unless ( $j < (int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4)) && $i < $#einzel && ($port[$i] + 1) == $port[$i+1]){ #wenn der naechste Port nicht der direkt Nachfolgende ist oder mehr als 8 Ports (32Bytes)
$msg = I2C_PCA9685_i2cwrite($hash,$reg, $data); # Rausschicken
($data, $reg) = undef;
$j = 1;
}
}
} elsif ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?(( ){0,3},( ){0,3}(P(ort|)((0|)[0-9]|1[0-5]))( ){1,3}\d*(( ){1,3}\d*)?){1,}( ){0,3}$/i ) { # Mehrere Ports auf versch. Werte setzen
Log3 undef, 5, "mehrere ports und unterschiedliche Werte";
$str =~ tr/(P|p)(ort|)//d;
my @einzel = split(',', $str);
my (@port, @dim, @delay);
#@einzel = sort { $a <=> $b } @einzel;
for my $i (0..$#einzel){
($port[$i], $dim[$i], $delay[$i]) = split(' ', $einzel[$i]);
}
my ($data, $reg) = undef;
my $j = 1;
for my $i (0..$#einzel){
($msg, my $tdata, my $treg) = I2C_PCA9685_CalcRegs($hash, $port[$i], $dim[$i], ( defined($delay[$i]) ? $delay[$i] : undef ) ); # Registerinhalte berechnen
return $msg if defined($msg);
Log3 $hash, 5, "$name: Port: $port[$i], Reg: $treg, Inhalt: $tdata, Rohwerte: $einzel[$i], Dimwert: $dim[$i], Delay: ". ( defined($delay[$i]) ? ( $delay[$i] =~ m/ */ ? "leer" : $delay[$i] ) : "leer" );
if ( defined($data) && defined($reg) ) { # bereits Werte für Ports vorhanden
$j += 1;
$data .= " " . $tdata;
} else {
$data = $tdata;
$reg = $treg;
}
unless ( $j < int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) && $i < $#einzel && ($port[$i] + 1) == $port[$i+1]){ #wenn der naechste Port nicht der direkt Nachfolgende ist oder mehr als 8 Ports (32Bytes)
$msg = I2C_PCA9685_i2cwrite($hash,$reg, $data); # Rausschicken
($data, $reg) = undef;
$j = 1;
}
}
} elsif ($str =~ m/(a(ll|)( ){0,3}((\d{1,4})|on|off)(( ){0,3}\d{1,4})?)( ){0,3}$/i) { # Alle Ports gleichzeitig
my ($port, $dim, $delay) = split(' ', $str); my ($port, $dim, $delay) = split(' ', $str);
$port = 61; # Portnummer auf 61 für All setzen (All Startreg ist 250) $port = 61; # Portnummer auf 61 für All setzen (All Startreg ist 250)
#Log3 undef, 1, "$name: alle Ports: $port, $dim" . (defined $delay ? ", $delay" : "" ); Log3 undef, 5, "$name: alle Ports: $port, $dim" . (defined $delay ? ", $delay" : "" );
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay); my ($msg, $data, $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
} else { $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
} else {
my $list = undef; my $list = undef;
foreach (0..15) { foreach (0..15) {
$list .= "Port" . sprintf ('%02d', $_) . ":slider,0,$dimstep,$dimcount "; $list .= "Port" . sprintf ('%02d', $_) . ":slider,0,$dimstep,$dimcount ";
@ -292,97 +375,68 @@ sub I2C_PCA9685_Set($@) { #
$list .= "all:slider,0,$dimstep,$dimcount"; $list .= "all:slider,0,$dimstep,$dimcount";
$msg = "Unknown argument $str, choose one of " . $list; $msg = "Unknown argument $str, choose one of " . $list;
} }
return (defined($msg) ? $msg : undef);
return (defined($msg) ? $msg."--" : undef);
}
#my $string = 'AA55FF0102040810204080';
#my @hex = ($string =~ /(..)/g);
#my @dec = map { hex($_) } @hex;
#my @bytes = map { pack('C', $_) } @dec;
#or
#my @bytes = map { pack('C', hex($_)) } ($string =~ /(..)/g);
#or
#my $bytes = pack "H*", $hex;
#----------------------
#$int = 2001;
#$bint = pack("N", $int);
#@octets = unpack("C4", $bint);
#sprintf "%02X " x 4 . "\n", @octets;
# prints: 00 00 07 D1
#############################################################################
sub I2C_PCA9685_SetPort($$$$) { #
my ($hash, $port, $dim, $delay) = @_;
my $name = $hash->{NAME};
my $dimcount = AttrVal($name, "dimcount", "4095");
$port =~ tr/P(ort|)//d; #Nummer aus Port extrahieren
return "wrong dimvalue: $dim for \"set $name $port\" use one of: " .
join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) .
" 0..$dimcount"
unless(exists($setsP{$dim}) || ($dim >= 0 && $dim <= $dimcount));
return "wrong delayvalue: $delay for \"set $name $port $dim\" use one of: " .
join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) .
" 0..$dimcount"
unless( not defined($delay) && ( exists($setsP{$delay}) || ($delay >= 0 && $delay <= $dimcount) ));
my ($data, $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
my $msg = I2C_PCA9685_i2cwrite($hash,$reg, $data); # Rausschicken
return defined $msg ? $msg : undef
} }
############################################################################# #############################################################################
sub I2C_PCA9685_CalcRegs($$$$) { # Registerinhalte berechnen sub I2C_PCA9685_CalcRegs($$$$) { # Registerinhalte berechnen
my ($hash, $port, $val, $del) = @_; my ($hash, $port, $dim, $del) = @_;
my $name = $hash->{NAME};
#$port =~ tr/P(ort|)//d; #Nummer aus Port extrahieren
my $dimcount = AttrVal($hash->{NAME}, "dimcount", "4095"); my $dimcount = AttrVal($hash->{NAME}, "dimcount", "4095");
my $data; my $data;
if ($val eq "on") { my $msg = undef;
if (defined($dim) && $dim eq "on") {
$data = "0 16 0 0"; $data = "0 16 0 0";
} elsif ($val eq "off") { } elsif (defined($dim) && $dim eq "off") {
$data = "0 0 0 16"; $data = "0 0 0 16";
} else { } elsif (defined($dim) && $dim =~ m/^\d+$/ && $dim >= 0 && $dim <= $dimcount) {
my $delaytime = 0; my $delaytime = 0;
if ($dimcount < 4095) { #DimmWert anpassen bei anderem Faktor if ($dimcount < 4095) { #DimmWert anpassen bei anderem Faktor
$val = int($val * 4095 / $dimcount); $dim = int($dim * 4095 / $dimcount);
} }
if (defined $del) { #Delaytime angegeben? if (defined $del) { #Delaytime angegeben?
return "wrong delay value: $del for \"set $hash->{NAME} Port$port $val $del\" use value between 0 and $dimcount" $msg = "wrong delay value: \"$del\" for \"$name Port$port $dim\" use value between 0 and $dimcount"
unless ($del >= 0 && $del <= $dimcount); unless ($del =~ m/^\d+$/ && $del >= 0 && $del <= $dimcount);
if ($dimcount < 4095) { #DelayWert anpassen bei anderem Faktor if ($dimcount < 4095) { #DelayWert anpassen bei anderem Faktor
$del = int($del * 4095 / $dimcount); $del = int($del * 4095 / $dimcount);
} }
$delaytime = $del $delaytime = $del
} else { #...wenn nicht aus Reading holen (für all kommt immer 0 raus) } else { #...wenn nicht aus Reading holen (für all kommt immer 0 raus)
$delaytime = ReadingsVal($hash->{NAME},'Port_d'.sprintf ('%02d', $port),"0"); $delaytime = ReadingsVal($name,'Port_d'.sprintf ('%02d', $port),"0");
} }
my $LEDx_OFF = $delaytime + $val - (( $val + $delaytime < 4096 ) ? 0 : 4096); unless($msg) { # nur berechnen wenn es keine Fehlermeldung gibt
if ($LEDx_OFF == $delaytime) { #beide Register dürfen nicht gleichen Inhalt haben, das entpricht "aus" my $LEDx_OFF = $delaytime + $dim - (( $dim + $delaytime < 4096 ) ? 0 : 4096);
$data = "0 0 0 16"; if ($LEDx_OFF == $delaytime) { #beide Register dürfen nicht gleichen Inhalt haben, das entpricht "aus"
} else { $data = "0 0 0 16";
my @LEDx = unpack("C*", pack("S", $delaytime)); } else {
push @LEDx, unpack("C*", pack("S", $LEDx_OFF)); #Array $LEDx[0] = LEDx_ON_L, $LEDx[1] = LEDx_ON_H, $LEDx[2] = LEDx_OFF_L, $LEDx[3] = LEDx_OFF_H my @LEDx = unpack("C*", pack("S", $delaytime));
$data = sprintf "%01d " x 4, @LEDx; push @LEDx, unpack("C*", pack("S", $LEDx_OFF)); #Array $LEDx[0] = LEDx_ON_L, $LEDx[1] = LEDx_ON_H, $LEDx[2] = LEDx_OFF_L, $LEDx[3] = LEDx_OFF_H
# $data = sprintf "%01d " x 4, @LEDx;
$data = sprintf "%01d %01d %01d %01d", @LEDx;
} }
}
} else {
$msg = "wrong dimvalue: \"".(defined($dim)?$dim:"...")."\" for \"$name Port$port\" use one of: " .
join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " 0..$dimcount";
} }
my $reg = 6 + 4 * $port; # Nummer des entspechenden LEDx_ON_L Registers (LED0_ON_L = 0x06) jede LED hat 4 Register my $reg = 6 + 4 * $port if defined $port; # Nummer des entspechenden LEDx_ON_L Registers (LED0_ON_L = 0x06) jede LED hat 4 Register
return $data, $reg; return $msg, $data, $reg;
} }
############################################################################# #############################################################################
sub I2C_PCA9685_Get($@) { # Portwerte bei laden der Datailseite aktualisieren sub I2C_PCA9685_Get($@) { # Portwerte bei laden der Datailseite aktualisieren
my ($hash, @a) = @_; my ($hash, @a) = @_;
unless ($hash->{IODev}->{TYPE} eq 'RPII2C') { #fuer FRM, etc. Register zurücklesen (bei RPII2C kommt bei erfolgreicher Uebertragung die Botschaft zurueck)
my $reg = int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) * 4; # Anzahl moegliche 4er Registergruppen pro Lesevorgang
my $n = int(64 / $reg); # Anzahl Lesevorgänge (abgerundet)
foreach (0 .. ($n-1)) {
I2C_PCA9685_i2cread($hash, 6 + $_ * $reg, $reg);
}
I2C_PCA9685_i2cread($hash, 6 + $n * $reg, $reg - ($reg * ($n+1) - 64)) if (($n+1) * $reg) > 64;
} else {
I2C_PCA9685_i2cread($hash, 0x6, 64); I2C_PCA9685_i2cread($hash, 0x6, 64);
}
return; return;
#my $name =$a[0];
#my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
#$sendpackage{reg} = 0x6; #startadresse zum lesen
#$sendpackage{nbyte} = 64;
#return "$name: no IO device defined" unless ($hash->{IODev});
#my $phash = $hash->{IODev};
#my $pname = $phash->{NAME};
#CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
} }
############################################################################# #############################################################################
sub I2C_PCA9685_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an) sub I2C_PCA9685_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an)
@ -404,21 +458,36 @@ sub I2C_PCA9685_i2cread($$$) { # Lesebefehl an Hardware absetzen (ant
} }
############################################################################# #############################################################################
sub I2C_PCA9685_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen sub I2C_PCA9685_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
my ($hash, $reg, @data) = @_; my ($hash, $reg, $data) = @_;
if (defined (my $iodev = $hash->{IODev})) { if (defined (my $iodev = $hash->{IODev})) {
Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . join (' ',@data) . " to Register $reg"; Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . $data . " to Register $reg";
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, { CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cwrite", direction => "i2cwrite",
i2caddress => $hash->{I2C_Address}, i2caddress => $hash->{I2C_Address},
reg => $reg, reg => $reg,
data => join (' ',@data), data => $data,
}); });
unless ($hash->{IODev}->{TYPE} eq 'RPII2C') { #fuer FRM, etc. Register zurücklesen (bei RPII2C kommt bei erfolgreicher Uebertragung die Botschaft zurueck)
my $nbyte = () = $data =~ / /gi;
unless ($reg == 250) {
I2C_PCA9685_i2cread($hash, $reg, $nbyte + 1);
} else {
#I2C_PCA9685_UpdReadings($hash, $reg, $data);
my $reg = int( (defined($hash->{I2C_Buff})?$hash->{I2C_Buff}:30) / 4) * 4; # Anzahl moegliche 4er Registergruppen pro Lesevorgang
my $n = int(64 / $reg); # Anzahl Lesevorgänge (abgerundet)
foreach (0 .. ($n-1)) {
I2C_PCA9685_i2cread($hash, 6 + $_ * $reg, $reg);
}
I2C_PCA9685_i2cread($hash, 6 + $n * $reg, $reg - ($reg * ($n+1) - 64)) if (($n+1) * $reg) > 64;
}
}
} else { } else {
if (AttrVal($hash->{NAME}, "dummy", 0) == 1) { if (AttrVal($hash->{NAME}, "dummy", 0) == 1) {
I2C_PCA9685_UpdReadings($hash, $reg, @data); # Zeile zum testen (Werte werden direkt zu I2CRec umgeleitet) I2C_PCA9685_UpdReadings($hash, $reg, $data); # Zeile zum testen (Werte werden direkt zu I2CRec umgeleitet)
} else { } else {
return "no IODev assigned to '$hash->{NAME}'"; return "no IODev assigned to '$hash->{NAME}'";
} } }
}
} }
############################################################################# #############################################################################
sub I2C_PCA9685_I2CRec($@) { # vom IODev aufgerufen sub I2C_PCA9685_I2CRec($@) { # vom IODev aufgerufen
@ -433,7 +502,7 @@ sub I2C_PCA9685_I2CRec($@) { # vom IODev aufgerufen
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) { if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{received}); I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{received});
readingsSingleUpdate($hash,"state", "Ok", 1); readingsSingleUpdate($hash,"state", "Ok", 1);
} elsif ( $clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data}) ) { #readings aktualisieren wenn uebertragung ok } elsif ( $clientmsg->{direction} eq "i2cwrite" && defined($clientmsg->{data}) ) { #readings aktualisieren wenn uebertragung ok (bei FRM kommt nix zurueck)
I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data}); I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data});
readingsSingleUpdate($hash,"state", "Ok", 1); readingsSingleUpdate($hash,"state", "Ok", 1);
@ -480,7 +549,7 @@ sub I2C_PCA9685_CalcVal($@) { # Readings aus Registerwerten berechnen
sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Readings/Internals schreiben sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Readings/Internals schreiben
my ($hash, $reg, $inh) = @_; my ($hash, $reg, $inh) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
#Log3 $hash, 1, "$name UpdReadings Start Register: " .sprintf("0x%.2X", $reg).", Inhalt: $inh"; Log3 $hash, 5, "$name Received from Register $reg: $inh"; #sprintf("0x%.2X", $reg)
my @reginh = split(" ", $inh); my @reginh = split(" ", $inh);
my $dimstep = AttrVal($name, "dimstep", "1"); my $dimstep = AttrVal($name, "dimstep", "1");
my $dimcount = AttrVal($name, "dimcount", "4095"); my $dimcount = AttrVal($name, "dimcount", "4095");
@ -498,31 +567,27 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @reginh); ($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @reginh);
readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert
readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert
Log3 $hash, 5, "$name: lese einen Port - Reg: $reg ; Inh: @reginh"; Log3 $hash, 5, "$name: lese einen Port - Reg: $reg, Inh: @reginh";
} elsif ( $reg < 70 && $reg > 5 && @reginh > 4 ) { #Wenn alle Ports abgefragt werden } elsif ( $reg < 70 && $reg > 5 && @reginh > 4 ) { #Wenn mehrere Ports abgefragt werden
for (my $i = 0; $i < @reginh; $i++) { for (my $i = 0; $i < @reginh; $i++) {
next unless ( ($reg + $i - 2) / 4 =~ m/^\d+$/ ); next unless ( ($reg + $i - 2) / 4 =~ m/^\d+$/ && defined($reginh[$i + 3]) );
my @regpart = ( $reginh[$i], $reginh[$i + 1], $reginh[$i + 2], $reginh[$i + 3] ); my @regpart = ( $reginh[$i], $reginh[$i + 1], $reginh[$i + 2], $reginh[$i + 3] );
my $port = sprintf ('%02d', ($reg + $i - 6) / 4); my $port = sprintf ('%02d', ($reg + $i - 6) / 4);
($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @regpart); ($dimval, $delay) = I2C_PCA9685_CalcVal($dimcount, @regpart);
readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert readingsBulkUpdate($hash, 'Port'.$port , $dimval) if (ReadingsVal($name, 'Port'.$port, "failure") ne $dimval); #nur wenn Wert geaendert
readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert readingsBulkUpdate($hash, 'Port_d'.$port , $delay) if (defined $delay && ReadingsVal($name, 'Port_d'.$port, "failure") ne $delay); #nur wenn Wert geaendert
Log3 $hash, 5, "$name: lese mehrere Ports - Reg: $reg ; i: $i; Inh: @regpart"; Log3 $hash, 5, "$name: lese mehrere Ports - Reg: $reg, i: $i; Inh: @regpart";
$i += 3; $i += 3;
} }
} elsif ($reg == 254) { #wenn Frequenz Register } elsif ($reg == 254) { #wenn Frequenz Register
my $clock = AttrVal($name, "extClock", 25); my $clock = AttrVal($name, "extClock", 25);
my $init = 1 unless defined($hash->{Frequency});
$hash->{confregs}{PRESCALE} = $inh;
$hash->{Frequency} = sprintf( "%.1f", $clock * 1000000 / (4096 * ($inh + 1)) ) . " Hz"; $hash->{Frequency} = sprintf( "%.1f", $clock * 1000000 / (4096 * ($inh + 1)) ) . " Hz";
I2C_PCA9685_Init2($hash) if defined($init);
} elsif ( $reg >= 0 && $reg < 6 ) { #Konfigurations Register } elsif ( $reg >= 0 && $reg < 6 ) { #Konfigurations Register
$hash->{confregs}{$reg} = $inh; $hash->{confregs}{$confregs{$reg}} = $inh;
#folgendes evtl noch weg
#$hash->{CONF} = (defined $hash->{confregs}{0} ? sprintf('0x%.2X ', $hash->{confregs}{0}) : "0x__ ") .
# (defined $hash->{confregs}{1} ? sprintf('0x%.2X ', $hash->{confregs}{1}) : "0x__ ") .
# (defined $hash->{confregs}{2} ? sprintf('0x%.2X ', $hash->{confregs}{2}) : "0x__ ") .
# (defined $hash->{confregs}{3} ? sprintf('0x%.2X ', $hash->{confregs}{3}) : "0x__ ") .
# (defined $hash->{confregs}{4} ? sprintf('0x%.2X ', $hash->{confregs}{4}) : "0x__ ") .
# (defined $hash->{confregs}{5} ? sprintf('0x%.2X ', $hash->{confregs}{5}) : "0x__ ");
} }
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
return; return;
@ -546,16 +611,19 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
<a name="I2C_PCA9685Define"></a><br> <a name="I2C_PCA9685Define"></a><br>
<b>Define</b> <b>Define</b>
<ul> <ul>
<code>define &lt;name&gt; I2C_PCA9685 &lt;I2C Address&gt;</code><br> <code>define &lt;name&gt; I2C_PCA9685 &lt;I2C Address&gt; [&lt;I2C Buffer Size&gt;]</code><br>
where <code>&lt;I2C Address&gt;</code> can be written as decimal value or 0xnn<br> where <code>&lt;I2C Address&gt;</code> can be written as decimal value or 0xnn<br>
<code>&lt;I2C Buffer Size&gt;</code> sets the maximum size of the I2C-Packet.
Without this option the packet size is 30 Bytes (32 incl. Address and Register number).
For RPII2C this option has no influence, cause it can deal with arbitrary packet sizes.<br>
</ul> </ul>
<a name="I2C_PCA9685Set"></a> <a name="I2C_PCA9685Set"></a>
<b>Set</b> <b>Set</b>
<ul> <ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt; [&lt;delay&gt;]</code><br><br> <code>set &lt;name&gt; &lt;port&gt; &lt;dimvalue&gt; [&lt;delay&gt;]</code><br><br>
<li>where <code>&lt;port&gt;</code> is one of Port0 to Port15<br> <li>where <code>&lt;port&gt;</code> is one of Port0 to Port15<br>
and <code>&lt;value&gt;</code> one of<br> and <code>&lt;dimvalue&gt;</code> one of<br>
<ul> <ul>
<code> <code>
off<br> off<br>
@ -563,10 +631,13 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
0..4095<br> 0..4095<br>
</code> </code>
</ul> </ul>
<code>&lt;delay&gt;</code> defines the switch on time inside the PWM counting loop. It does not have an influence to the duty cycle. Default value is 0 and, possible values are 0..4095<br> <code>&lt;delay&gt;</code> defines the switch on time inside the PWM counting loop. It does not have an influence to the duty cycle.
Default value is 0 and, possible values are 0..4095<br>
</li><br> </li><br>
<li> <li>
It is also possible to change more than one port at the same time with comma separated values.<br> It is also possible to change more than one port at the same time. Just separate them by comma.
If only the last of the comma separated ports has dimvalue (and delay), all ports will set to the same values.
Sequently ports will set at once (useful for multi color LED's).<br>
Also P instead of Port is Possible. Also P instead of Port is Possible.
</li><br> </li><br>
@ -574,7 +645,7 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
Examples: Examples:
<ul> <ul>
<code>set mod1 Port04 543</code><br> <code>set mod1 Port04 543</code><br>
<code>set mod1 Port14 434 765</code><br> <code>set mod1 Port4 434 765</code><br>
<code>set mod1 Port1, Port14 434 765</code><br> <code>set mod1 Port1, Port14 434 765</code><br>
<code>set mod1 Port1 on, P14 434 765</code><br> <code>set mod1 Port1 on, P14 434 765</code><br>
</ul><br> </ul><br>
@ -592,10 +663,10 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
<a name="I2C_PCA9685Attr"></a> <a name="I2C_PCA9685Attr"></a>
<b>Attributes</b> <b>Attributes</b>
<ul> <ul>
<li>subadr1,subadr2,subadr3,allcalladr<br> <li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
Alternative slave addresses, if you want to control more than one PCA9685 with one define Alternative slave addresses, if you want to control more than one PCA9685 with one define
Respective flag in modreg1 must be set as well<br> Respective flag in modereg1 must be set as well<br>
Default: subadr1=113,subadr2=114,subadr3=116,allcalladr=112, valid values: valid I2C Address <br><br> Default: SUBADR1=113,SUBADR2=114,SUBADR3=116,ALLCALLADR=112, valid values: valid I2C Address <br><br>
</li> </li>
<li>OnStartup<br> <li>OnStartup<br>
Comma separated list of output ports/PWM registers and their desired state after start<br> Comma separated list of output ports/PWM registers and their desired state after start<br>
@ -603,30 +674,33 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
Default: -, valid values: &lt;port&gt;=on|off|0..4095|last where &lt;port&gt; = 0 - 15<br><br> Default: -, valid values: &lt;port&gt;=on|off|0..4095|last where &lt;port&gt; = 0 - 15<br><br>
</li> </li>
<li>prescale<br> <li>prescale<br>
Sets PWM Frequency. The Formula is: Fx = 25MHz/(4096 * (prescale + 1)) The corresponding frequency value is shown under internals (valid for the internal 25MHz clock).<br> Sets PWM Frequency. The Formula is: Fx = 25MHz/(4096 * (prescale + 1)).
Default: 30 (200Hz), valid values: 0-255<br><br> The corresponding frequency value is shown under internals.
If provided, attribute extClock will be used for frequency calculation. Otherwise 25MHz<br>
Default: 30 (200Hz for 25MHz clock), valid values: 0-255<br><br>
</li> </li>
<li>modreg1<br> <li>modereg1<br>
Comma separated list of: Comma separated list of:
<ul> <ul>
<li>EXTCLK<br> <li>EXTCLK<br>
If set the an external connected clock will be used instead of the internal 25MHz oscillator If set the an external connected clock will be used instead of the internal 25MHz oscillator.
Use the attribute extClock to provide the external oscillater value.
</li> </li>
<li>SUB1<br> <li>SUBADR1<br>
If set the PCA9685 responds to I2C-bus subaddress 1. If set the PCA9685 responds to I2C-bus SUBADR 1.
</li> </li>
<li>SUB2<br> <li>SUBADR2<br>
If set the PCA9685 responds to I2C-bus subaddress 2. If set the PCA9685 responds to I2C-bus SUBADR 2.
</li> </li>
<li>SUB3<br> <li>SUBADR3<br>
If set the PCA9685 responds to I2C-bus subaddress 3. If set the PCA9685 responds to I2C-bus SUBADR 3.
</li> </li>
<li>ALLCALL<br> <li>ALLCALLADR<br>
If set the PCA9685 responds to I2C-bus allcall address. If set the PCA9685 responds to I2C-bus ALLCALLADR address.
</li> </li>
</ul> </ul>
</li> </li>
<li>modreg2<br> <li>modereg2<br>
Comma separated list of: Comma separated list of:
<ul> <ul>
<li>INVRT<br> <li>INVRT<br>
@ -678,16 +752,18 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
<a name="I2C_PCA9685Define"></a><br> <a name="I2C_PCA9685Define"></a><br>
<b>Define</b> <b>Define</b>
<ul> <ul>
<code>define &lt;name&gt; I2C_PCA9685 &lt;I2C Address&gt;</code><br> <code>define &lt;name&gt; I2C_PCA9685 &lt;I2C Address&gt; [&lt;I2C Buffer Size&gt;]</code><br>
Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert im Format 0xnn oder eine Dezimalzahl<br> Der Wert <code>&lt;I2C Address&gt;</code> ist ein zweistelliger Hex-Wert im Format 0xnn oder eine Dezimalzahl<br>
<code>&lt;I2C Buffer Size&gt;</code> gibt die maximale Anzahl von Datenbytes pro I2C Datenpaket an. Nicht angegeben, wird der Wert 30 verwendet
( entspricht 32 Bytes incl. Adresse und Registernummer). RPII2C kann mit beliebig gro&szlig;en Paketl&auml;ngen umgehen, daher ist diese Option dort inaktiv.<br>
</ul> </ul>
<a name="I2C_PCA9685Set"></a> <a name="I2C_PCA9685Set"></a>
<b>Set</b> <b>Set</b>
<ul> <ul>
<code>set &lt;name&gt; &lt;port&gt; &lt;value&gt; [&lt;delay&gt;]</code><br><br> <code>set &lt;name&gt; &lt;port&gt; &lt;dimvalue&gt; [&lt;delay&gt;]</code><br><br>
<li>Als <code>&lt;port&gt;</code> kann Port00 bis Port15 verwendet werden<br> <li>Als <code>&lt;port&gt;</code> kann Port00 bis Port15 verwendet werden<br>
<code>&lt;value&gt;</code> kann folgende Werte annehmen:<br> <code>&lt;dimvalue&gt;</code> kann folgende Werte annehmen:<br>
<ul> <ul>
<code> <code>
off<br> off<br>
@ -695,11 +771,15 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
0..4095<br> 0..4095<br>
</code> </code>
</ul> </ul>
<code>&lt;delay&gt;</code> gibt den Wert innerhalb der Z&auml;hlschleife an, an dem der Ausgang eingeschaltet wird. Damit lassen sich die 16 Ausg&auml;nge zu unterschiedlichen Zeiten einschalten um Stromspitzen zu minimieren. <code>&lt;delay&gt;</code> gibt den Wert innerhalb der Z&auml;hlschleife an, an dem der Ausgang eingeschaltet wird.
Damit lassen sich die 16 Ausg&auml;nge zu unterschiedlichen Zeiten einschalten um Stromspitzen zu minimieren.
Dieser Wert hat keinerlei Einfluss auf die Pulsbreite. Stardartwert ist 0, m&ouml;gliche Werte sind 0..4095<br> Dieser Wert hat keinerlei Einfluss auf die Pulsbreite. Stardartwert ist 0, m&ouml;gliche Werte sind 0..4095<br>
</li> </li>
<li> <li>
Um mehrer Ports mit einem Befehl zu &auml;ndern k&ouml;nnen mehrere Befehle per Komma getrennt eingegeben werden. Um mehrer Ports mit einem Befehl zu &auml;ndern k&ouml;nnen mehrere Befehle per Komma getrennt eingegeben werden.
Dabei kann jeder Port auf einen separaten, oder alle Ports auf den selben Wert gesettz werden.
F&auml;r letzteres darf nur der letzte Befehl dimvalue (und delay) enthalten.
Aufeinanerfolgene Ports werden mit einem Befehl geschrieben. So k&ouml;nnen beispielsweise multicolor LED's ohne flackern geschaltet werden.<br>
Anstelle von Port kann auch einfach ein P verwendet werden. Anstelle von Port kann auch einfach ein P verwendet werden.
</li> </li>
@ -707,8 +787,8 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
Examples: Examples:
<ul> <ul>
<code>set mod1 Port04 543</code><br> <code>set mod1 Port04 543</code><br>
<code>set mod1 Port14 434 765</code><br> <code>set mod1 Port4 434 765</code><br>
<code>set mod1 Port1, Port14 434 765</code><br> <code>set mod1 Port1, Port2, Port14 434 765</code><br>
<code>set mod1 Port1 on, P14 434 765</code><br> <code>set mod1 Port1 on, P14 434 765</code><br>
</ul><br> </ul><br>
</ul> </ul>
@ -725,49 +805,52 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
<a name="I2C_PCA9685Attr"></a> <a name="I2C_PCA9685Attr"></a>
<b>Attribute</b> <b>Attribute</b>
<ul> <ul>
<li>subadr1,subadr2,subadr3,allcalladr<br> <li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
Alternative slave Adressen, if you want to control more than one PCA9685 with one define Alternative slave Adressen, zum kontrollieren mehrerer PCA9685 mit einem define
Zus&auml;tzlich zu diesen Registern m&uuml;ssen die Passenden Bits in modreg1 gesetzt werden.<br> Zus&auml;tzlich zu diesen Registern m&uuml;ssen die Passenden Bits in modereg1 gesetzt werden.<br>
Standard: subadr1=113,subadr2=114,subadr3=116,allcalladr=112, g&uuml;ltige Werte: I2C Adresse <br><br> Standard: SUBADR1=113,SUBADR2=114,SUBADR3=116,ALLCALLADR=112, g&uuml;ltige Werte: I2C Adresse <br><br>
</li> </li>
<li>OnStartup<br> <li>OnStartup<br>
Comma separated list of output ports/PWM registers and their desired state after start<br> Kommagetrennte Liste der Ports mit den gew&uuml;nschten Startwerten.<br>
Without this atribut all output ports will set to last state<br> Nicht gelistete Ports werden auf en letzte state wiederhergestellt.<br>
Standard: last, g&uuml;ltige Werte: &lt;port&gt;=on|off|0..4095|last wobei &lt;port&gt; = 0 - 15<br><br> Standard: last, g&uuml;ltige Werte: &lt;port&gt;=on|off|0..4095|last wobei &lt;port&gt; = 0 - 15<br><br>
</li> </li>
<li>prescale<br> <li>prescale<br>
Sets PWM Frequency. The Formula is: Fx = 25MHz/(4096 * (prescale + 1)) The corresponding frequency value is shown under internals (valid for the internal 25MHz clock).<br> PWM Frequenz setzen. Formel: Fx = 25MHz/(4096 * (prescale + 1)).
Standard: 30 (200Hz), g&uuml;ltige Werte: 0-255<br><br> Die eingestellte Frequenz wird in den Internals angezeigt.
Wenn das Attribut extclock angegeben ist, wird dieses zur Frequenzberechnung verwendet. Andernfalls 25MHz.<br>
Standard: 30 (200Hz f&uuml;r 25MHz clock), g&uuml;ltige Werte: 0-255<br><br>
</li> </li>
<li>modreg1<br> <li>modereg1<br>
Durch Komma getrennte Liste von: Durch Komma getrennte Liste von:
<ul> <ul>
<li>EXTCLK<br> <li>EXTCLK<br>
Anstelle des internen 25MHz Oszillators wird ein extern Angeschlossener verwendet. Anstelle des internen 25MHz Oszillators wird ein extern Angeschlossener verwendet.
Die Frequenz des externen Oszillators kann &uuml;ber das Attribut extclock angegeben werden.
</li> </li>
<li>SUB1<br> <li>SUBADR1<br>
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 1. Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 1.
</li> </li>
<li>SUB2<br> <li>SUBADR2<br>
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 2. Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 2.
</li> </li>
<li>SUB3<br> <li>SUBADR3<br>
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 3. Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 3.
</li> </li>
<li>ALLCALL<br> <li>ALLCALLADR<br>
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Allcall Adresse. Wenn gesetzt, antwortet der PCA9685 auf I2C-bus ALLCALLADR Adresse.
</li> </li>
</ul> </ul>
</li> </li>
<li>modreg2<br> <li>modereg2<br>
Durch Komma getrennte Liste von: Durch Komma getrennte Liste von:
<ul> <ul>
<li>INVRT<br> <li>INVRT<br>
Wenn gesetzt, werden die Ausg&auml;nge invertiert.<br> Wenn gesetzt, werden die Ausg&auml;nge invertiert.<br>
</li> </li>
<li>OCH<br> <li>OCH<br>
If set the outputs changes on ACK (after every byte sent).<br> Wenn gesetzt, werden die Ports nach jedem ACK gesetzt (also nach jedem gesendeten Byte).<br>
Otherwise the output changes on STOP command (bus write action finished)<br> Andernfalls werden sie nach einem STOP Kommando gesetzt (Bus Schreibaktion fertig, also nach einem Datenpaket)<br>
</li> </li>
<li>OUTDRV<br> <li>OUTDRV<br>
Wenn gesetzt, werden die Ausg&auml;nge als totem pole konfiguriert.<br> Wenn gesetzt, werden die Ausg&auml;nge als totem pole konfiguriert.<br>

View File

@ -109,16 +109,19 @@ sub I2C_SHT21_Attr (@) {# hier noch Werteueberpruefung einfuegen
my $hash = $defs{$name}; my $hash = $defs{$name};
my $msg = ''; my $msg = '';
if ($command && $command eq "set" && $attr && $attr eq "IODev") { if ($command && $command eq "set" && $attr && $attr eq "IODev") {
if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) { eval {
main::AssignIoPort($hash,$val); if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
my @def = split (' ',$hash->{DEF}); main::AssignIoPort($hash,$val);
I2C_SHT21_Init($hash,\@def) if (defined ($hash->{IODev})); my @def = split (' ',$hash->{DEF});
} I2C_SHT21_Init($hash,\@def) if (defined ($hash->{IODev}));
}
};
return I2C_SHT21_Catch($@) if $@;
} }
if ($attr eq 'poll_interval') { if ($attr eq 'poll_interval') {
if ($val > 0) { if ($val > 0) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer(1, 'I2C_SHT21_Poll', $hash, 0); InternalTimer(gettimeofday() + 5, 'I2C_SHT21_Poll', $hash, 0);
} else { } else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
} }

View File

@ -185,6 +185,7 @@ FHEM/51_RPI_GPIO.pm klausw http://forum.fhem.de Einplatin
FHEM/52_I2C_DS1307 ntruchsess http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_DS1307 ntruchsess http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_EEPROM.pm klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_EEPROM.pm klausw http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_LCD ntruchsess http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_LCD ntruchsess http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_BME280 klausw http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_MCP23008 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_MCP23008 klausw http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_MCP23017 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_MCP23017 klausw http://forum.fhem.de Sonstige Systeme
FHEM/52_I2C_MCP342x klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_MCP342x klausw http://forum.fhem.de Sonstige Systeme