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:
parent
9501df9a60
commit
70a941bd59
@ -1,5 +1,7 @@
|
||||
# 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.
|
||||
- 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,
|
||||
added "set credentials" command to save username/password,
|
||||
added Attribute "session" to make login-session selectable,
|
||||
|
@ -23,7 +23,8 @@ my @clients = qw(
|
||||
I2C_LCD
|
||||
I2C_DS1307
|
||||
I2C_PC.*
|
||||
I2C_MCP23.*
|
||||
I2C_MCP.*
|
||||
I2C_BME280
|
||||
I2C_BMP180
|
||||
I2C_SHT21
|
||||
I2C_TSL2561
|
||||
|
@ -14,6 +14,7 @@ I2C_LCD
|
||||
I2C_DS1307
|
||||
I2C_PC.*
|
||||
I2C_MCP.*
|
||||
I2C_BME280
|
||||
I2C_BMP180
|
||||
I2C_SHT21
|
||||
I2C_TSL2561
|
||||
|
@ -51,6 +51,7 @@ my @clients = qw(
|
||||
I2C_PC.*
|
||||
I2C_MCP23.*
|
||||
I2C_SHT21
|
||||
I2C_BME280
|
||||
I2C_BMP180
|
||||
I2C_TSL2561
|
||||
FRM_LCD
|
||||
|
@ -684,55 +684,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> RPI_GPIO <GPIO number></code><br><br>
|
||||
all usable <code>GPIO number</code> are in the following tables<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>
|
||||
all usable <code>GPIO number</code> can be found <a href="http://www.panu.it/raspberry/">here</a><br><br>
|
||||
|
||||
Examples:
|
||||
<pre>
|
||||
@ -877,55 +829,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> RPI_GPIO <GPIO number></code><br><br>
|
||||
Alle verfügbaren <code>GPIO number</code> sind in den folgenden Tabellen 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>
|
||||
Alle verfügbaren <code>GPIO number</code> sind z.B. <a href="http://www.panu.it/raspberry/">hier</a> zu finden<br><br>
|
||||
|
||||
Beispiele:
|
||||
<pre>
|
||||
|
629
fhem/FHEM/52_I2C_BME280.pm
Normal file
629
fhem/FHEM/52_I2C_BME280.pm
Normal 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 [<I2C Address>]</code><br><br>
|
||||
without defined <code><I2C Address></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 <readValues></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öglicht die Verwendung eines digitalen (Luft)druck/feuchtesensors BME280 über den I2C Bus des Raspberry Pi.<br><br>
|
||||
I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
|
||||
oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
|
||||
<b>Das Attribut IODev muss definiert sein.</b><br>
|
||||
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define BME280 <BME280_name> [<I2C Addresse>]</code><br><br>
|
||||
Fehlt <code><I2C Address></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 <name> 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ü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ür das Auslesen einer neuen Messung.<br>
|
||||
Default: 5, gü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ültige Werte: 0, 1, 2<br><br>
|
||||
</li>
|
||||
<li>altitude<br>
|
||||
Wenn dieser Wert definiert ist, wird diese Angabe zusä für die Berechnung des
|
||||
Luftdrucks bezogen auf Meereshö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
|
@ -1,5 +1,5 @@
|
||||
##############################################################################
|
||||
# $Id: 52_I2C_EEPROM.pm 7007 2014-11-17 18:03:42Z klauswitt $
|
||||
# $Id$
|
||||
##############################################################################
|
||||
# Modul for I2C EEPROM
|
||||
#
|
||||
|
@ -1,5 +1,5 @@
|
||||
##############################################################################
|
||||
# $Id: 52_I2C_MCP23008.pm 6209 2014-07-06 21:38:39Z klauswitt $
|
||||
# $Id$
|
||||
##############################################################################
|
||||
# Modul for I2C GPIO Extender MCP23008
|
||||
#
|
||||
|
@ -1,5 +1,5 @@
|
||||
##############################################
|
||||
# $Id: 52_MCP342x.pm 5865 2014-05-14 23:00:12Z klauswitt $
|
||||
# $Id$
|
||||
|
||||
package main;
|
||||
|
||||
|
@ -25,7 +25,6 @@ use strict;
|
||||
use warnings;
|
||||
use SetExtensions;
|
||||
#use POSIX;
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
my $setdim = ":slider,0,1,4095 ";
|
||||
|
||||
@ -34,23 +33,32 @@ my %setsP = (
|
||||
'on' => 1,
|
||||
);
|
||||
|
||||
my %confregs = (
|
||||
0 => 'modereg1',
|
||||
1 => 'modereg2',
|
||||
2 => 'SUBADR1',
|
||||
3 => 'SUBADR2',
|
||||
4 => 'SUBADR3',
|
||||
5 => 'ALLCALLADR',
|
||||
);
|
||||
|
||||
my %defaultreg = (
|
||||
'modereg1' => 32, #32-> Bit 5 -> Autoincrement
|
||||
'modereg2' => 0,
|
||||
'sub1' => 113,
|
||||
'sub2' => 114,
|
||||
'sub3' => 116,
|
||||
'allc' => 112,
|
||||
'presc' => 30,
|
||||
'SUBADR1' => 113,
|
||||
'SUBADR2' => 114,
|
||||
'SUBADR3' => 116,
|
||||
'ALLCALLADR' => 112,
|
||||
'PRESCALE' => 30,
|
||||
);
|
||||
|
||||
my %mr1 = (
|
||||
'EXTCLK' => 64,
|
||||
'SLEEP' => 16,
|
||||
'SUB1' => 8,
|
||||
'SUB2' => 4,
|
||||
'SUB3' => 2,
|
||||
'ALLCALL' => 1,
|
||||
'SUBADR1' => 8,
|
||||
'SUBADR2' => 4,
|
||||
'SUBADR3' => 2,
|
||||
'ALLCALLADR' => 1,
|
||||
);
|
||||
|
||||
my %mr2 = (
|
||||
@ -69,22 +77,14 @@ sub I2C_PCA9685_Initialize($) { #
|
||||
$hash->{AttrFn} = "I2C_PCA9685_Attr";
|
||||
$hash->{StateFn} = "I2C_PCA9685_State";
|
||||
$hash->{SetFn} = "I2C_PCA9685_Set";
|
||||
$hash->{GetFn} = "I2C_PCA9685_Get";
|
||||
#$hash->{GetFn} = "I2C_PCA9685_Get";
|
||||
$hash->{I2CRecFn} = "I2C_PCA9685_I2CRec";
|
||||
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ".
|
||||
"prescale:slider,0,1,255 OnStartup ".
|
||||
"subadr1 subadr2 subadr3 allcalladr ".
|
||||
"modreg1:multiple-strict,EXTCLK,SUB1,SUB2,SUB3,ALLCALL ".
|
||||
"modreg2:multiple-strict,INVRT,OCH,OUTDRV,OUTNE0,OUTNE1 ".
|
||||
"$readingFnAttributes dummy:0,1";
|
||||
}
|
||||
#############################################################################
|
||||
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;
|
||||
"SUBADR1 SUBADR2 SUBADR3 ALLCALLADR ".
|
||||
"modereg1:multiple-strict,EXTCLK,SUBADR1,SUBADR2,SUBADR3,ALLCALLADR ".
|
||||
"modereg2:multiple-strict,INVRT,OCH,OUTDRV,OUTNE0,OUTNE1 ".
|
||||
"$readingFnAttributes dummy:0,1 extClock";
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Define($$) {
|
||||
@ -98,45 +98,74 @@ sub I2C_PCA9685_Define($$) {
|
||||
return undef;
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Init($$) { #
|
||||
sub I2C_PCA9685_Init($$) { # wird ausgefuehrt bei Initialisierung und Connect/Reconnect des DEVio
|
||||
my ( $hash, $args ) = @_;
|
||||
#my @a = split("[ \t]+", $args);
|
||||
my $name = $hash->{NAME};
|
||||
if (defined $args && int(@$args) != 1) {
|
||||
if (defined $args && int(@$args) < 1) {
|
||||
return "Define: Wrong syntax. Usage:\n" .
|
||||
"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)) {
|
||||
$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 {
|
||||
return "$name I2C Address not valid";
|
||||
return "$name: no I2C Address defined" unless defined($hash->{I2C_Address});
|
||||
}
|
||||
if (defined (my $maxbuff = shift @$args)) {
|
||||
return "$name: I2C buffer size must be a number" if $maxbuff =~ m/^d+$/;
|
||||
$hash->{I2C_Buff} = $maxbuff;
|
||||
}
|
||||
my $msg = '';
|
||||
eval {
|
||||
Log3 $hash, 4, "$hash->{NAME}: Init1 Konfigurationsregister auslesen";
|
||||
AssignIoPort($hash);
|
||||
#Mode register wiederherstellen
|
||||
I2C_PCA9685_i2cread($hash, 1, 1); # Modreg2 schonmal lesen
|
||||
#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
|
||||
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"));
|
||||
$hash->{STATE} = 'Initializing';
|
||||
InternalTimer(gettimeofday() + 10, 'I2C_PCA9685_InitError', $hash, 0); # nach 10s Initialisierungsfehler ablegen
|
||||
};
|
||||
return I2C_BME280_Catch($@) if $@;
|
||||
}
|
||||
#############################################################################
|
||||
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_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);
|
||||
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", $defaultreg{'presc'})) if defined AttrVal($name, "prescale", undef);
|
||||
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;
|
||||
};
|
||||
return I2C_BME280_Catch($@) if $@;
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Catch($) { #
|
||||
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;
|
||||
if ($exception) {
|
||||
$exception =~ /^(.*)( at.*FHEM.*)$/;
|
||||
@ -147,7 +176,7 @@ sub I2C_PCA9685_Catch($) { #
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
|
||||
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) {
|
||||
substr($sname,0,4,"");
|
||||
$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";
|
||||
readingsSingleUpdate($hash,"Port". sprintf('%02d', $sname), $onstart{$sname}, 1);
|
||||
} 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)}{TIME} = $tim;
|
||||
}
|
||||
@ -164,12 +193,15 @@ sub I2C_PCA9685_State($$$$) { # reload readings at FHEM start
|
||||
return undef;
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Undefine($$) { #
|
||||
sub I2C_PCA9685_Undefine($$) { # wird beim loeschen des Device ausgefuehrt
|
||||
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
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Attr(@) { #
|
||||
sub I2C_PCA9685_Attr(@) { # wird beim setzen eines Attributes ausgefuehrt
|
||||
my ($command, $name, $attr, $val) = @_;
|
||||
my $hash = $defs{$name};
|
||||
my $msg = '';
|
||||
@ -180,11 +212,12 @@ sub I2C_PCA9685_Attr(@) { #
|
||||
I2C_PCA9685_Init($hash,\@def) if (defined ($hash->{IODev}));
|
||||
}
|
||||
} elsif ($attr && $attr =~ m/^prescale$/i) { # Frequenz
|
||||
return undef unless ($main::init_done);
|
||||
$val = 30 unless (defined($val)); #beim loeschen wieder auf Standard setzen
|
||||
$val = $defaultreg{'PRESCALE'} unless (defined($val)); #beim loeschen wieder auf Standard setzen
|
||||
return "wrong value: $val for \"set $name $attr\" use 0-255"
|
||||
unless(looks_like_number($val) && $val >= 0 && $val < 256);
|
||||
my $modereg1 = defined $hash->{confregs}{0} ? $hash->{confregs}{0} : $defaultreg{'modreg1'};
|
||||
unless($val =~ m/^(\d+)$/ && $val >= 0 && $val < 256);
|
||||
Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".$hash->{confregs}{PRESCALE}." neuer Wert: ".$val;
|
||||
if ($main::init_done && $val != $hash->{confregs}{PRESCALE}) {
|
||||
my $modereg1 = defined $hash->{confregs}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
|
||||
my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
|
||||
$msg = I2C_PCA9685_i2cwrite($hash, 0, $modereg1mod); #sleep Mode aktivieren
|
||||
$msg = I2C_PCA9685_i2cwrite($hash, 254 ,$val); #Frequenz aktualisieren
|
||||
@ -193,43 +226,48 @@ sub I2C_PCA9685_Attr(@) { #
|
||||
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
|
||||
return undef unless ($main::init_done);
|
||||
}
|
||||
} elsif ($attr && $attr =~ m/^(SUBADR[1-3])|ALLCALLADR$/i) { # weitere I2C Adressen
|
||||
$val = $defaultreg{$attr} unless defined($val);
|
||||
substr($attr,0,6,"");
|
||||
my $regaddr = ($attr =~ m/^l/i) ? 5 : $attr + 1;
|
||||
my $subadr = $val =~ /^0.*$/ ? oct($val) : $val;
|
||||
return "I2C Address not valid" if $subadr > 127;
|
||||
$msg = I2C_PCA9685_i2cwrite($hash, $regaddr ,$subadr << 1);
|
||||
} elsif ($attr && $attr =~ m/^modreg1$/i) { # Mode register 1
|
||||
return undef unless ($main::init_done);
|
||||
my $SUBADR = $val =~ /^0x.*$/ ? oct($val) : $val;
|
||||
return "I2C Address not valid" if $SUBADR > 127;
|
||||
Log3 $hash, 5, $hash->{NAME} . ": $confregs{$regaddr} alter Wert: ".$hash->{confregs}{$confregs{$regaddr}}." neuer Wert: ".($SUBADR << 1);
|
||||
$msg = I2C_PCA9685_i2cwrite($hash, $regaddr ,$SUBADR << 1) if $main::init_done && ($SUBADR << 1) != $hash->{confregs}{$confregs{$regaddr}};
|
||||
} elsif ($attr && $attr =~ m/^modereg1$/i) { # Mode register 1
|
||||
my @inp = split(/,/, $val) if defined($val);
|
||||
my $data = 32; # Auto increment soll immer gesetzt sein
|
||||
foreach (@inp) {
|
||||
return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr1{ $a } <=> $mr1{ $b } } keys %setsP) )
|
||||
unless(exists($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)
|
||||
my $modereg1 = defined $hash->{confregs}{0} ? $hash->{confregs}{0} : $defaultreg{'modreg1'};
|
||||
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}{$confregs{0}} ? $hash->{confregs}{$confregs{0}} : $defaultreg{'modereg1'};
|
||||
my $modereg1mod = ( $modereg1 & 0x7F ) | $mr1{ "SLEEP" };
|
||||
Log3 $hash, 5, "$hash->{NAME}: sleep Mode aktivieren (Vorbereitung fuer EXTCLK)";
|
||||
$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);
|
||||
}
|
||||
Log3 $hash, 5, $hash->{NAME} . ": $attr alter Wert: ".$hash->{confregs}{$confregs{0}}." neuer Wert: ".$data;
|
||||
if ( $main::init_done && $data != $hash->{confregs}{$confregs{0}} ) {
|
||||
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/^modreg2$/i) { #Mode register 2
|
||||
return undef unless ($main::init_done);
|
||||
}
|
||||
} elsif ($attr && $attr =~ m/^modereg2$/i) { #Mode register 2
|
||||
my @inp = split(/,/, $val) if defined($val);
|
||||
my $data = 0; # Auto increment soll immer gesetzt sein
|
||||
my $data = 0;
|
||||
foreach (@inp) {
|
||||
return "wrong value: $_ for \"attr $name $attr\" use comma separated list of " . join(',', (sort { $mr2{ $a } <=> $mr2{ $b } } keys %setsP) )
|
||||
unless(exists($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") {
|
||||
if (defined $val) {
|
||||
foreach (split (/,/,$val)) {
|
||||
@ -242,6 +280,11 @@ sub I2C_PCA9685_Attr(@) { #
|
||||
);
|
||||
}
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
@ -253,37 +296,77 @@ sub I2C_PCA9685_Set($@) { #
|
||||
my $dimcount = AttrVal($name, "dimcount", "4095");
|
||||
my $msg;
|
||||
my $str = join(' ',@rest);
|
||||
if ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))/i) { # (mehrere) Ports ( regex unfertig)
|
||||
#Log3 undef, 1, "$name: empfangen: $str";
|
||||
if (index($str, ',') == -1) { # Nur ein Port
|
||||
#Log3 undef, 5, "$name: empfangen: $str";
|
||||
if ($str && $str =~ m/^(P(ort|)((0|)[0-9]|1[0-5]))/i && index($str, ',') == -1) { # Nur ein Port
|
||||
my ($port, $dim, $delay) = split(' ', $str);
|
||||
#Log3 undef, 1, "$name: ein Wert: $port, $dim, $delay";
|
||||
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay);
|
||||
} 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]
|
||||
$port =~ tr/(P|p)(ort|)//d;
|
||||
#Log3 undef, 5, "$name: ein Port: $port, $dim, $delay";
|
||||
($msg, my $data, my $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
|
||||
$msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
|
||||
# } 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]
|
||||
} 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]
|
||||
Log3 undef, 5, "mehrere ports und ein wert";
|
||||
$str =~ tr/(P|p)(ort|)//d;
|
||||
my @einzel = split(',', $str);
|
||||
my @port;
|
||||
my (undef, $dim, $delay) = split(' ', $einzel[$#einzel]);
|
||||
foreach (reverse @einzel) {
|
||||
my ($port) = split(' ', $_);
|
||||
#Log3 undef, 1, "$name: mehrere Ports gleich: $port, $dim" . (defined $delay ? ", $delay" : "" );
|
||||
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay);
|
||||
last if defined($msg);
|
||||
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 ($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);
|
||||
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);
|
||||
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|) \d{1,4}( \d{1,4})?)( ){0,3}$/i) { # Alle Ports gleichzeitig
|
||||
}
|
||||
} 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);
|
||||
$port = 61; # Portnummer auf 61 für All setzen (All Startreg ist 250)
|
||||
#Log3 undef, 1, "$name: alle Ports: $port, $dim" . (defined $delay ? ", $delay" : "" );
|
||||
$msg = I2C_PCA9685_SetPort($hash, $port, $dim, $delay);
|
||||
Log3 undef, 5, "$name: alle Ports: $port, $dim" . (defined $delay ? ", $delay" : "" );
|
||||
my ($msg, $data, $reg) = I2C_PCA9685_CalcRegs($hash, $port, $dim, $delay); # Registerinhalte berechnen
|
||||
$msg = I2C_PCA9685_i2cwrite($hash,$reg, $data) unless($msg); # Rausschicken
|
||||
} else {
|
||||
my $list = undef;
|
||||
foreach (0..15) {
|
||||
@ -292,97 +375,68 @@ sub I2C_PCA9685_Set($@) { #
|
||||
$list .= "all:slider,0,$dimstep,$dimcount";
|
||||
$msg = "Unknown argument $str, choose one of " . $list;
|
||||
}
|
||||
|
||||
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
|
||||
return (defined($msg) ? $msg : undef);
|
||||
}
|
||||
#############################################################################
|
||||
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 $data;
|
||||
if ($val eq "on") {
|
||||
my $msg = undef;
|
||||
if (defined($dim) && $dim eq "on") {
|
||||
$data = "0 16 0 0";
|
||||
} elsif ($val eq "off") {
|
||||
} elsif (defined($dim) && $dim eq "off") {
|
||||
$data = "0 0 0 16";
|
||||
} else {
|
||||
} elsif (defined($dim) && $dim =~ m/^\d+$/ && $dim >= 0 && $dim <= $dimcount) {
|
||||
my $delaytime = 0;
|
||||
if ($dimcount < 4095) { #DimmWert anpassen bei anderem Faktor
|
||||
$val = int($val * 4095 / $dimcount);
|
||||
$dim = int($dim * 4095 / $dimcount);
|
||||
}
|
||||
if (defined $del) { #Delaytime angegeben?
|
||||
return "wrong delay value: $del for \"set $hash->{NAME} Port$port $val $del\" use value between 0 and $dimcount"
|
||||
unless ($del >= 0 && $del <= $dimcount);
|
||||
$msg = "wrong delay value: \"$del\" for \"$name Port$port $dim\" use value between 0 and $dimcount"
|
||||
unless ($del =~ m/^\d+$/ && $del >= 0 && $del <= $dimcount);
|
||||
if ($dimcount < 4095) { #DelayWert anpassen bei anderem Faktor
|
||||
$del = int($del * 4095 / $dimcount);
|
||||
}
|
||||
$delaytime = $del
|
||||
} 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
|
||||
my $LEDx_OFF = $delaytime + $dim - (( $dim + $delaytime < 4096 ) ? 0 : 4096);
|
||||
if ($LEDx_OFF == $delaytime) { #beide Register dürfen nicht gleichen Inhalt haben, das entpricht "aus"
|
||||
$data = "0 0 0 16";
|
||||
} else {
|
||||
my @LEDx = unpack("C*", pack("S", $delaytime));
|
||||
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 " x 4, @LEDx;
|
||||
$data = sprintf "%01d %01d %01d %01d", @LEDx;
|
||||
}
|
||||
}
|
||||
my $reg = 6 + 4 * $port; # Nummer des entspechenden LEDx_ON_L Registers (LED0_ON_L = 0x06) jede LED hat 4 Register
|
||||
return $data, $reg;
|
||||
} 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 if defined $port; # Nummer des entspechenden LEDx_ON_L Registers (LED0_ON_L = 0x06) jede LED hat 4 Register
|
||||
return $msg, $data, $reg;
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_Get($@) { # Portwerte bei laden der Datailseite aktualisieren
|
||||
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);
|
||||
}
|
||||
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)
|
||||
@ -404,21 +458,36 @@ sub I2C_PCA9685_i2cread($$$) { # Lesebefehl an Hardware absetzen (ant
|
||||
}
|
||||
#############################################################################
|
||||
sub I2C_PCA9685_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
|
||||
my ($hash, $reg, @data) = @_;
|
||||
my ($hash, $reg, $data) = @_;
|
||||
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, {
|
||||
direction => "i2cwrite",
|
||||
i2caddress => $hash->{I2C_Address},
|
||||
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 {
|
||||
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 {
|
||||
return "no IODev assigned to '$hash->{NAME}'";
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
#############################################################################
|
||||
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}) ) {
|
||||
I2C_PCA9685_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{received});
|
||||
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});
|
||||
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
|
||||
my ($hash, $reg, $inh) = @_;
|
||||
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 $dimstep = AttrVal($name, "dimstep", "1");
|
||||
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);
|
||||
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
|
||||
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++) {
|
||||
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 $port = sprintf ('%02d', ($reg + $i - 6) / 4);
|
||||
($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_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;
|
||||
}
|
||||
} elsif ($reg == 254) { #wenn Frequenz Register
|
||||
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";
|
||||
I2C_PCA9685_Init2($hash) if defined($init);
|
||||
} elsif ( $reg >= 0 && $reg < 6 ) { #Konfigurations Register
|
||||
$hash->{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__ ");
|
||||
$hash->{confregs}{$confregs{$reg}} = $inh;
|
||||
}
|
||||
readingsEndUpdate($hash, 1);
|
||||
return;
|
||||
@ -546,16 +611,19 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
<a name="I2C_PCA9685Define"></a><br>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> I2C_PCA9685 <I2C Address></code><br>
|
||||
<code>define <name> I2C_PCA9685 <I2C Address> [<I2C Buffer Size>]</code><br>
|
||||
where <code><I2C Address></code> can be written as decimal value or 0xnn<br>
|
||||
<code><I2C Buffer Size></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>
|
||||
|
||||
<a name="I2C_PCA9685Set"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<code>set <name> <port> <value> [<delay>]</code><br><br>
|
||||
<code>set <name> <port> <dimvalue> [<delay>]</code><br><br>
|
||||
<li>where <code><port></code> is one of Port0 to Port15<br>
|
||||
and <code><value></code> one of<br>
|
||||
and <code><dimvalue></code> one of<br>
|
||||
<ul>
|
||||
<code>
|
||||
off<br>
|
||||
@ -563,10 +631,13 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
0..4095<br>
|
||||
</code>
|
||||
</ul>
|
||||
<code><delay></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><delay></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>
|
||||
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.
|
||||
</li><br>
|
||||
|
||||
@ -574,7 +645,7 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
Examples:
|
||||
<ul>
|
||||
<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 on, P14 434 765</code><br>
|
||||
</ul><br>
|
||||
@ -592,10 +663,10 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
<a name="I2C_PCA9685Attr"></a>
|
||||
<b>Attributes</b>
|
||||
<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
|
||||
Respective flag in modreg1 must be set as well<br>
|
||||
Default: subadr1=113,subadr2=114,subadr3=116,allcalladr=112, valid values: valid I2C Address <br><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>
|
||||
</li>
|
||||
<li>OnStartup<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: <port>=on|off|0..4095|last where <port> = 0 - 15<br><br>
|
||||
</li>
|
||||
<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>
|
||||
Default: 30 (200Hz), valid values: 0-255<br><br>
|
||||
Sets PWM Frequency. The Formula is: Fx = 25MHz/(4096 * (prescale + 1)).
|
||||
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>modreg1<br>
|
||||
<li>modereg1<br>
|
||||
Comma separated list of:
|
||||
<ul>
|
||||
<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>SUB1<br>
|
||||
If set the PCA9685 responds to I2C-bus subaddress 1.
|
||||
<li>SUBADR1<br>
|
||||
If set the PCA9685 responds to I2C-bus SUBADR 1.
|
||||
</li>
|
||||
<li>SUB2<br>
|
||||
If set the PCA9685 responds to I2C-bus subaddress 2.
|
||||
<li>SUBADR2<br>
|
||||
If set the PCA9685 responds to I2C-bus SUBADR 2.
|
||||
</li>
|
||||
<li>SUB3<br>
|
||||
If set the PCA9685 responds to I2C-bus subaddress 3.
|
||||
<li>SUBADR3<br>
|
||||
If set the PCA9685 responds to I2C-bus SUBADR 3.
|
||||
</li>
|
||||
<li>ALLCALL<br>
|
||||
If set the PCA9685 responds to I2C-bus allcall address.
|
||||
<li>ALLCALLADR<br>
|
||||
If set the PCA9685 responds to I2C-bus ALLCALLADR address.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>modreg2<br>
|
||||
<li>modereg2<br>
|
||||
Comma separated list of:
|
||||
<ul>
|
||||
<li>INVRT<br>
|
||||
@ -678,16 +752,18 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
<a name="I2C_PCA9685Define"></a><br>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> I2C_PCA9685 <I2C Address></code><br>
|
||||
<code>define <name> I2C_PCA9685 <I2C Address> [<I2C Buffer Size>]</code><br>
|
||||
Der Wert <code><I2C Address></code> ist ein zweistelliger Hex-Wert im Format 0xnn oder eine Dezimalzahl<br>
|
||||
<code><I2C Buffer Size></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ßen Paketlängen umgehen, daher ist diese Option dort inaktiv.<br>
|
||||
</ul>
|
||||
|
||||
<a name="I2C_PCA9685Set"></a>
|
||||
<b>Set</b>
|
||||
<ul>
|
||||
<code>set <name> <port> <value> [<delay>]</code><br><br>
|
||||
<code>set <name> <port> <dimvalue> [<delay>]</code><br><br>
|
||||
<li>Als <code><port></code> kann Port00 bis Port15 verwendet werden<br>
|
||||
<code><value></code> kann folgende Werte annehmen:<br>
|
||||
<code><dimvalue></code> kann folgende Werte annehmen:<br>
|
||||
<ul>
|
||||
<code>
|
||||
off<br>
|
||||
@ -695,11 +771,15 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
0..4095<br>
|
||||
</code>
|
||||
</ul>
|
||||
<code><delay></code> gibt den Wert innerhalb der Zählschleife an, an dem der Ausgang eingeschaltet wird. Damit lassen sich die 16 Ausgänge zu unterschiedlichen Zeiten einschalten um Stromspitzen zu minimieren.
|
||||
<code><delay></code> gibt den Wert innerhalb der Zählschleife an, an dem der Ausgang eingeschaltet wird.
|
||||
Damit lassen sich die 16 Ausgänge zu unterschiedlichen Zeiten einschalten um Stromspitzen zu minimieren.
|
||||
Dieser Wert hat keinerlei Einfluss auf die Pulsbreite. Stardartwert ist 0, mögliche Werte sind 0..4095<br>
|
||||
</li>
|
||||
<li>
|
||||
Um mehrer Ports mit einem Befehl zu ändern kö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är letzteres darf nur der letzte Befehl dimvalue (und delay) enthalten.
|
||||
Aufeinanerfolgene Ports werden mit einem Befehl geschrieben. So können beispielsweise multicolor LED's ohne flackern geschaltet werden.<br>
|
||||
Anstelle von Port kann auch einfach ein P verwendet werden.
|
||||
</li>
|
||||
|
||||
@ -707,8 +787,8 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
Examples:
|
||||
<ul>
|
||||
<code>set mod1 Port04 543</code><br>
|
||||
<code>set mod1 Port14 434 765</code><br>
|
||||
<code>set mod1 Port1, Port14 434 765</code><br>
|
||||
<code>set mod1 Port4 434 765</code><br>
|
||||
<code>set mod1 Port1, Port2, Port14 434 765</code><br>
|
||||
<code>set mod1 Port1 on, P14 434 765</code><br>
|
||||
</ul><br>
|
||||
</ul>
|
||||
@ -725,49 +805,52 @@ sub I2C_PCA9685_UpdReadings($$$) { # vom IODev gesendete Werte in Read
|
||||
<a name="I2C_PCA9685Attr"></a>
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<li>subadr1,subadr2,subadr3,allcalladr<br>
|
||||
Alternative slave Adressen, if you want to control more than one PCA9685 with one define
|
||||
Zusätzlich zu diesen Registern müssen die Passenden Bits in modreg1 gesetzt werden.<br>
|
||||
Standard: subadr1=113,subadr2=114,subadr3=116,allcalladr=112, gültige Werte: I2C Adresse <br><br>
|
||||
<li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
|
||||
Alternative slave Adressen, zum kontrollieren mehrerer PCA9685 mit einem define
|
||||
Zusätzlich zu diesen Registern müssen die Passenden Bits in modereg1 gesetzt werden.<br>
|
||||
Standard: SUBADR1=113,SUBADR2=114,SUBADR3=116,ALLCALLADR=112, gültige Werte: I2C Adresse <br><br>
|
||||
</li>
|
||||
<li>OnStartup<br>
|
||||
Comma separated list of output ports/PWM registers and their desired state after start<br>
|
||||
Without this atribut all output ports will set to last state<br>
|
||||
Kommagetrennte Liste der Ports mit den gewünschten Startwerten.<br>
|
||||
Nicht gelistete Ports werden auf en letzte state wiederhergestellt.<br>
|
||||
Standard: last, gültige Werte: <port>=on|off|0..4095|last wobei <port> = 0 - 15<br><br>
|
||||
</li>
|
||||
<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>
|
||||
Standard: 30 (200Hz), gültige Werte: 0-255<br><br>
|
||||
PWM Frequenz setzen. Formel: Fx = 25MHz/(4096 * (prescale + 1)).
|
||||
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ür 25MHz clock), gültige Werte: 0-255<br><br>
|
||||
</li>
|
||||
<li>modreg1<br>
|
||||
<li>modereg1<br>
|
||||
Durch Komma getrennte Liste von:
|
||||
<ul>
|
||||
<li>EXTCLK<br>
|
||||
Anstelle des internen 25MHz Oszillators wird ein extern Angeschlossener verwendet.
|
||||
Die Frequenz des externen Oszillators kann über das Attribut extclock angegeben werden.
|
||||
</li>
|
||||
<li>SUB1<br>
|
||||
<li>SUBADR1<br>
|
||||
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 1.
|
||||
</li>
|
||||
<li>SUB2<br>
|
||||
<li>SUBADR2<br>
|
||||
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 2.
|
||||
</li>
|
||||
<li>SUB3<br>
|
||||
<li>SUBADR3<br>
|
||||
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Subadresse 3.
|
||||
</li>
|
||||
<li>ALLCALL<br>
|
||||
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus Allcall Adresse.
|
||||
<li>ALLCALLADR<br>
|
||||
Wenn gesetzt, antwortet der PCA9685 auf I2C-bus ALLCALLADR Adresse.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>modreg2<br>
|
||||
<li>modereg2<br>
|
||||
Durch Komma getrennte Liste von:
|
||||
<ul>
|
||||
<li>INVRT<br>
|
||||
Wenn gesetzt, werden die Ausgänge invertiert.<br>
|
||||
</li>
|
||||
<li>OCH<br>
|
||||
If set the outputs changes on ACK (after every byte sent).<br>
|
||||
Otherwise the output changes on STOP command (bus write action finished)<br>
|
||||
Wenn gesetzt, werden die Ports nach jedem ACK gesetzt (also nach jedem gesendeten Byte).<br>
|
||||
Andernfalls werden sie nach einem STOP Kommando gesetzt (Bus Schreibaktion fertig, also nach einem Datenpaket)<br>
|
||||
</li>
|
||||
<li>OUTDRV<br>
|
||||
Wenn gesetzt, werden die Ausgänge als totem pole konfiguriert.<br>
|
||||
|
@ -109,16 +109,19 @@ sub I2C_SHT21_Attr (@) {# hier noch Werteueberpruefung einfuegen
|
||||
my $hash = $defs{$name};
|
||||
my $msg = '';
|
||||
if ($command && $command eq "set" && $attr && $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_SHT21_Init($hash,\@def) if (defined ($hash->{IODev}));
|
||||
}
|
||||
};
|
||||
return I2C_SHT21_Catch($@) if $@;
|
||||
}
|
||||
if ($attr eq 'poll_interval') {
|
||||
if ($val > 0) {
|
||||
RemoveInternalTimer($hash);
|
||||
InternalTimer(1, 'I2C_SHT21_Poll', $hash, 0);
|
||||
InternalTimer(gettimeofday() + 5, 'I2C_SHT21_Poll', $hash, 0);
|
||||
} else {
|
||||
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
|
||||
}
|
||||
|
@ -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_EEPROM.pm klausw 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_MCP23017 klausw http://forum.fhem.de Sonstige Systeme
|
||||
FHEM/52_I2C_MCP342x klausw http://forum.fhem.de Sonstige Systeme
|
||||
|
Loading…
x
Reference in New Issue
Block a user