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.
# 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,

View File

@ -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

View File

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

View File

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

View File

@ -684,56 +684,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<b>Define</b>
<ul>
<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:
<pre>
define Pin12 RPI_GPIO 18
@ -877,56 +829,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<b>Define</b>
<ul>
<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>
<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&uuml;gbaren <code>GPIO number</code> sind z.B. <a href="http://www.panu.it/raspberry/">hier</a> zu finden<br><br>
Beispiele:
<pre>
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
#

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
#

View File

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

View File

@ -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});
}
AssignIoPort($hash);
#Mode register wiederherstellen
I2C_PCA9685_i2cread($hash, 1, 1); # Modreg2 schonmal lesen
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) );
if (defined (my $maxbuff = shift @$args)) {
return "$name: I2C buffer size must be a number" if $maxbuff =~ m/^d+$/;
$hash->{I2C_Buff} = $maxbuff;
}
$hash->{STATE} = 'Initialized';
return;
my $msg = '';
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;
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,69 +212,80 @@ 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'};
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
$msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1); #sleep Mode wieder aus
foreach (0..15) { #Portzustände wiederherstellen
my $port = "Port".sprintf ('%02d', $_);
I2C_PCA9685_Set($hash, $name, $port, ReadingsVal($name,$port ,0) );
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
$msg = I2C_PCA9685_i2cwrite($hash, 0 ,$modereg1); #sleep Mode wieder aus
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
return undef unless ($main::init_done);
substr($attr,0,6,"");
} 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);
}
$msg = I2C_PCA9685_i2cwrite($hash, 0 , $data);
} elsif ($attr && $attr =~ m/^modreg2$/i) { #Mode register 2
return undef unless ($main::init_done);
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/^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)) {
my @pair = split (/=/,$_);
$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 &&
(($pair[0] =~ m/(^[0-9]|1[0-5])$/i &&
( $pair[1] eq "last" || exists($setsP{$pair[1]}) ||
( $pair[1] =~ m/^\d+$/ && $pair[1] < 4095 ) ) ) )
);
if (defined $val) {
foreach (split (/,/,$val)) {
my @pair = split (/=/,$_);
$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 &&
(($pair[0] =~ m/(^[0-9]|1[0-5])$/i &&
( $pair[1] eq "last" || exists($setsP{$pair[1]}) ||
( $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;
}
#############################################################################
@ -253,38 +296,78 @@ 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
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]
my @einzel = split(',', $str);
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);
}
} 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);
}
#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);
$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]);
for my $i (0..$#einzel){
($port[$i]) = split(' ', $einzel[$i]);
}
} 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);
$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);
} else {
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) {
$list .= "Port" . sprintf ('%02d', $_) . ":slider,0,$dimstep,$dimcount ";
@ -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);
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;
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 %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
return $data, $reg;
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;
I2C_PCA9685_i2cread($hash, 0x6, 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 &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>
<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>
<a name="I2C_PCA9685Set"></a>
<b>Set</b>
<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>
and <code>&lt;value&gt;</code> one of<br>
and <code>&lt;dimvalue&gt;</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>&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>
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: &lt;port&gt;=on|off|0..4095|last where &lt;port&gt; = 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 &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>
<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>
<a name="I2C_PCA9685Set"></a>
<b>Set</b>
<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>
<code>&lt;value&gt;</code> kann folgende Werte annehmen:<br>
<code>&lt;dimvalue&gt;</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>&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>
</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.
</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&auml;tzlich zu diesen Registern m&uuml;ssen die Passenden Bits in modreg1 gesetzt werden.<br>
Standard: subadr1=113,subadr2=114,subadr3=116,allcalladr=112, g&uuml;ltige Werte: I2C Adresse <br><br>
<li>SUBADR1,SUBADR2,SUBADR3,ALLCALLADR<br>
Alternative slave Adressen, zum kontrollieren mehrerer PCA9685 mit einem define
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>
</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&uuml;nschten Startwerten.<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>
</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&uuml;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&uuml;r 25MHz clock), g&uuml;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 &uuml;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&auml;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&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 $msg = '';
if ($command && $command eq "set" && $attr && $attr eq "IODev") {
if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
main::AssignIoPort($hash,$val);
my @def = split (' ',$hash->{DEF});
I2C_SHT21_Init($hash,\@def) if (defined ($hash->{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';
}

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_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