I2C_TSL2561 [devicename] address';
+ } elsif ((@a == 3)) {
+ $address = lc($a[2]);
+ } else {
+ $address = lc($a[3]);
+ if ($libcheck_hasHiPi) {
+ $hash->{HiPi_used} = 1;
+ } else {
+ $msg = '$name error: HiPi library not installed';
+ }
+ }
+ if ($msg) {
+ Log3 ($hash, 1, $msg);
+ return $msg;
+ }
+
+ $address = $validAdresses{$address};
+ if (defined($address) && (!$hash->{HiPi_used} || !$address eq TSL2561_ADDR_AUTO)) {
+ if ($address eq TSL2561_ADDR_AUTO) {
+ # start with lowest address in auto mode
+ $hash->{autoAddress} = 1;
+ $address = TSL2561_ADDR_LOW;
+ } else {
+ $hash->{autoAddress} = 0;
+ }
+ $hash->{I2C_Address} = hex($address);
+ } else {
+ $msg = "Wrong address, must be one of " . TSL2561_ADDR_LOW . ", " . TSL2561_ADDR_FLOAT . " or " . TSL2561_ADDR_HIGH . " (and " . TSL2561_ADDR_AUTO . " for IODev)";
+ Log3 ($hash, 1, $msg);
+ return $msg;
+ }
+
+ # create default attributes
+ if (AttrVal($name, 'poll_interval', '?') eq '?') {
+ $msg = CommandAttr(undef, $name . ' poll_interval 5');
+ if ($msg) {
+ Log (1, $msg);
+ return $msg;
+ }
+ }
+ if (AttrVal($name, 'floatArithmetics', '?') eq '?') {
+ $msg = CommandAttr(undef, $name . ' floatArithmetics 1');
+ if ($msg) {
+ Log (1, $msg);
+ return $msg;
+ }
+ }
+
+ # preset some internal readings
+ if (!defined($hash->{tsl2561IntegrationTime})) {
+ my $attrVal = AttrVal($name, 'integrationTime', 13);
+ $hash->{tsl2561IntegrationTime} = $attrVal == 402? TSL2561_INTEGRATIONTIME_402MS : $attrVal == 101? TSL2561_INTEGRATIONTIME_101MS : TSL2561_INTEGRATIONTIME_13MS;
+ }
+ if (!defined($hash->{tsl2561Gain})) {
+ my $attrVal = AttrVal($name, 'gain', 1);
+ $hash->{tsl2561Gain} = $attrVal == 16? TSL2561_GAIN_16X : TSL2561_GAIN_1X;
+ }
+ if (!defined($hash->{acquiState})) {
+ $hash->{acquiState} = ACQUI_STATE_DISABLED;
+ }
+ if (!defined($hash->{calcState})) {
+ $hash->{calcState} = CALC_STATE_IDLE;
+ }
- readingsSingleUpdate($hash, 'state', 'Defined', 1);
-
- eval {
- I2C_TSL2561_Init($hash, [ @a[ 2 .. scalar(@a) - 1 ] ] );
- };
- Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;;
+ readingsSingleUpdate($hash, 'state', STATE_DEFINED, 1);
+
+ eval {
+ I2C_TSL2561_Init($hash, [ @a[ 2 .. scalar(@a) - 1 ] ] );
+ };
+ Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;;
- Log3 $name, 5, "I2C_TSL2561_Define end";
- return undef;
+ Log3 $name, 5, "I2C_TSL2561_Define end";
+ return undef;
}
-
+
sub I2C_TSL2561_Init($$) {
- my ($hash, $args) = @_;
- my $name = $hash->{NAME};
-
- if ($hash->{HiPi_used}) {
- # check for existing i2c device
- my $i2cModulesLoaded = 0;
- my $dev = shift @$args;
- $i2cModulesLoaded = 1 if -e $dev;
- if ($i2cModulesLoaded) {
- if (-r $dev && -w $dev) {
- $hash->{devTSL2561} = HiPi::Device::I2C->new(
- devicename => $dev,
- address => $hash->{I2C_Address},
- busmode => 'i2c',
- );
- Log3 $name, 3, "I2C_TSL2561_Define device created";
- } else {
- my @groups = split '\s', $(;
- return "$name :Error! $dev isn't readable/writable by user " . getpwuid( $< ) . " or group(s) " .
- getgrgid($_) . " " foreach(@groups);
- }
- } else {
- return $name . ': Error! I2C device not found: ' . $dev . '. Please check that these kernelmodules are loaded: i2c_bcm2708, i2c_dev';
- }
- } else {
- AssignIoPort($hash);
- }
-
- readingsSingleUpdate($hash, 'state', 'Initialized', 1);
-
- return undef;
+ my ($hash, $args) = @_;
+ my $name = $hash->{NAME};
+
+ if ($hash->{HiPi_used}) {
+ # check for existing i2c device
+ my $i2cModulesLoaded = 0;
+ my $dev = shift @$args;
+ $i2cModulesLoaded = 1 if -e $dev;
+ if ($i2cModulesLoaded) {
+ if (-r $dev && -w $dev) {
+ $hash->{devTSL2561} = HiPi::Device::I2C->new(
+ devicename => $dev,
+ address => $hash->{I2C_Address},
+ busmode => 'i2c',
+ );
+ Log3 $name, 3, "I2C_TSL2561_Define device created";
+ } else {
+ my @groups = split '\s', $(;
+ return "$name :Error! $dev isn't readable/writable by user " . getpwuid( $< ) . " or group(s) " .
+ getgrgid($_) . " " foreach(@groups);
+ }
+ } else {
+ return $name . ': Error! I2C device not found: ' . $dev . '. Please check that these kernelmodules are loaded: i2c_bcm2708, i2c_dev';
+ }
+ } else {
+ AssignIoPort($hash);
+ }
+
+ readingsSingleUpdate($hash, 'state', STATE_INITIALIZED, 1);
+
+ return undef;
}
sub I2C_TSL2561_Catch($) {
- my $exception = shift;
- if ($exception) {
- $exception =~ /^(.*)( at.*FHEM.*)$/;
- return $1;
- }
- return undef;
+ my $exception = shift;
+ if ($exception) {
+ $exception =~ /^(.*)( at.*FHEM.*)$/;
+ return $1;
+ }
+ return undef;
}
=head2 I2C_TSL2561_Attr
- Title: I2C_TSL2561_Attr
- Function: Implements AttrFn function.
- Returns: string|undef
- Args: named arguments:
- -argument1 => array
+ Title: I2C_TSL2561_Attr
+ Function: Implements AttrFn function.
+ Returns: string|undef
+ Args: named arguments:
+ -argument1 => array
=cut
sub I2C_TSL2561_Attr (@) {
- my (undef, $name, $attr, $val) = @_;
- my $hash = $defs{$name};
- my $msg = '';
+ my ($cmd, $name, $attr, $val) = @_;
+ my $hash = $defs{$name};
+ my $msg = '';
- Log3 $name, 5, "I2C_TSL2561_Attr: attr " . $attr . " val " . defined($val)? $val : "undef";
- if ($attr eq 'poll_interval') {
- my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
-
- if ($val > 0) {
- RemoveInternalTimer($hash);
- InternalTimer(1, 'I2C_TSL2561_Poll', $hash, 0);
- } elsif (defined($val)) {
- $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
- }
- } elsif ($attr eq 'gain') {
- my $gain = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
-
- Log3 $name, 5, "attr gain is" . $gain;
- if ($gain == 1) {
- I2C_TSL2561_SetGain($hash, TSL2561_GAIN_1X);
- } elsif ($gain == 16) {
- I2C_TSL2561_SetGain($hash, TSL2561_GAIN_16X);
- } elsif (defined($val)) {
- $msg = 'Wrong gain defined. must be 1 or 16';
- }
- } elsif ($attr eq 'integrationTime') {
- my $time = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
-
- if ($time == 13) {
- I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_13MS);
- } elsif ($time == 101) {
- I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_101MS);
- } elsif ($time == 402) {
- I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_402MS);
- } elsif (defined($val)) {
- $msg = 'Wrong integrationTime defined. must be 13 or 101 or 402';
- }
- } elsif ($attr eq 'autoGain') {
- my $autoGain = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
-
- $hash->{tsl2561AutoGain} = $autoGain;
- if (!$autoGain) {
- I2C_TSL2561_Attr($hash, $name, 'gain', AttrVal($name, 'gain', 1));
- }
- } elsif ($attr eq 'floatArithmetics') {
- my $floatArithmetics = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
- }
+ Log3 $name, 5, "I2C_TSL2561_Attr: start cmd=$cmd attr=$attr";
+ if ($attr eq 'poll_interval') {
+ my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+
+ if ($val > 0) {
+ RemoveInternalTimer($hash);
+ InternalTimer(gettimeofday() + 1, 'I2C_TSL2561_Poll', $hash, 0);
+ } elsif (defined($val)) {
+ $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
+ }
+ } elsif ($attr eq 'gain') {
+ my $gain = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+
+ Log3 $name, 5, "I2C_TSL2561_Attr: attr gain is " . $gain;
+ if ($gain == 1) {
+ I2C_TSL2561_SetGain($hash, TSL2561_GAIN_1X);
+ } elsif ($gain == 16) {
+ I2C_TSL2561_SetGain($hash, TSL2561_GAIN_16X);
+ } elsif (defined($val)) {
+ $msg = 'Wrong gain defined. must be 1 or 16';
+ }
+ } elsif ($attr eq 'integrationTime') {
+ my $time = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+
+ if ($time == 13) {
+ I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_13MS);
+ } elsif ($time == 101) {
+ I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_101MS);
+ } elsif ($time == 402) {
+ I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_402MS);
+ } elsif (defined($val)) {
+ $msg = 'Wrong integrationTime defined. must be 13 or 101 or 402';
+ }
+ } elsif ($attr eq 'autoGain') {
+ my $autoGain = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+
+ if (!$autoGain) {
+ I2C_TSL2561_Attr($hash, $name, 'gain', AttrVal($name, 'gain', 1));
+ }
+ } elsif ($attr eq 'autoIntegrationTime') {
+ my $autoIntegrationTime = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
- return ($msg) ? $msg : undef;
+ if (!$autoIntegrationTime) {
+ I2C_TSL2561_Attr($hash, $name, 'integrationTime', AttrVal($name, 'integrationTime', 13));
+ }
+ } elsif ($attr eq 'normalizeRawValues') {
+ my $normalizeRawValues = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+ } elsif ($attr eq 'floatArithmetics') {
+ my $floatArithmetics = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+ } elsif ($attr eq "disable") {
+ my $disable = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
+ }
+
+ return ($msg) ? $msg : undef;
}
=head2 I2C_TSL2561_Poll
- Title: I2C_TSL2561_Poll
- Function: Start polling the sensor at interval defined in attribute
- Returns: -
- Args: named arguments:
- -argument1 => hash
+ Title: I2C_TSL2561_Poll
+ Function: Start polling the sensor at interval defined in attribute
+ Returns: -
+ Args: named arguments:
+ -argument1 => hash
=cut
sub I2C_TSL2561_Poll($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- # Read new values
- I2C_TSL2561_Get($hash);
-
- # Schedule next polling
- my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
- Log3 $name, 5, "I2C_TSL2561_Poll: $pollInterval min";
- if ($pollInterval > 0) {
- InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_TSL2561_Poll', $hash, 0);
- }
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ Log3 $name, 5, "I2C_TSL2561_Poll: start";
+
+ my $pollDelay = 60*AttrVal($hash->{NAME}, 'poll_interval', 0); # seconds polling
+ if (!AttrVal($hash->{NAME}, "disable", 0)) {
+ # Read new values
+ my $state = ReadingsVal($name, 'state', '');
+ if ($state eq STATE_I2C_ERROR) {
+ # try to turn off the device to check I2C communication (hotplug and error recovery)
+ if (I2C_TSL2561_Disable($hash)) {
+ $state = STATE_INITIALIZED;
+ readingsSingleUpdate($hash, 'state', $state, 1);
+ } elsif ($hash->{autoAddress}) {
+ # auto address mode, scan bus for device
+ if ($hash->{I2C_Address} == hex(TSL2561_ADDR_LOW)) {
+ $hash->{I2C_Address} = hex(TSL2561_ADDR_FLOAT);
+ } elsif ($hash->{I2C_Address} == hex(TSL2561_ADDR_FLOAT)) {
+ $hash->{I2C_Address} = hex(TSL2561_ADDR_HIGH);
+ } else {
+ $hash->{I2C_Address} = hex(TSL2561_ADDR_LOW);
+ }
+ $pollDelay = 10; # seconds retry delay
+ }
+ $hash->{tsl2561Package} = undef;
+ }
+ if ($state ne STATE_I2C_ERROR) {
+ # Request new samples from TSL2561 and calculate luminosity
+ my $lux = I2C_TSL2561_GetLuminosity($hash);
+ if ($hash->{calcState} == CALC_STATE_DATA_REQUESTED) {
+ $pollDelay = I2C_TSL2561_GetIntegrationTime($hash) + 0.001; # seconds integration time
+ } else {
+ if ($hash->{calcState} == CALC_STATE_IDLE) {
+ my $chScale = 1;
+ if (AttrVal($hash->{NAME}, "normalizeRawValues", 0)) {
+ $chScale = I2C_TSL2561_GetChannelScale($hash);
+ }
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate($hash, "gain", I2C_TSL2561_GetGain($hash));
+ readingsBulkUpdate($hash, "integrationTime", I2C_TSL2561_GetIntegrationTime($hash));
+ readingsBulkUpdate($hash, "broadband", ceil($chScale*$hash->{broadband}));
+ readingsBulkUpdate($hash, "ir", ceil($chScale*$hash->{ir}));
+ if (defined($lux)) {
+ readingsBulkUpdate($hash, "luminosity", $lux);
+ }
+ if ($state eq STATE_INITIALIZED && $hash->{saturated}) {
+ readingsBulkUpdate($hash, 'state', STATE_SATURATED, 1);
+ } elsif ($state eq STATE_SATURATED && !$hash->{saturated}) {
+ readingsBulkUpdate($hash, 'state', STATE_INITIALIZED, 1);
+ }
+ readingsEndUpdate($hash, 1);
+ }
+ }
+ }
+ } else {
+ readingsSingleUpdate($hash, 'state', STATE_DISABLED, 1);
+ }
+
+ # Schedule next polling
+ Log3 $name, 5, "I2C_TSL2561_Poll: $pollDelay s";
+ if ($pollDelay > 0) {
+ InternalTimer(gettimeofday() + $pollDelay, 'I2C_TSL2561_Poll', $hash, 0);
+ }
}
-=head2 I2C_TSL2561_Get
- Title: I2C_TSL2561_Get
- Function: Implements GetFn function.
- Returns: string|undef
- Args: named arguments:
- -argument1 => hash: $hash hash of device
- -argument2 => array: @a argument array
+sub I2C_TSL2561_Set($) {
+ my ( $hash, @args ) = @_;
+ my $name = $hash->{NAME};
-=cut
+ my $cmd = $args[1];
-sub I2C_TSL2561_Get($) {
- my ( $hash ) = @_;
- my $name = $hash->{NAME};
-
- Log3 $name, 5, "I2C_TSL2561_Get start";
-
- my $state = ReadingsVal($name, 'state', '');
- if ($state eq 'Error') {
- # try to turn off the device to check I2C communication (hotplug and error recovery)
- if (I2C_TSL2561_Disable($hash)) {
- $state = 'Initialized';
- readingsSingleUpdate($hash, 'state', $state, 1);
- }
- }
- if ($state ne 'Error') {
- # read from TSL2561 and calculate luminosity
- my $lux = I2C_TSL2561_CalculateLux($hash);
- $state = ReadingsVal($name, 'state', '');
- if ($state eq 'Initialized') {
- my $chScale = I2C_TSL2561_GetChannelScale($hash);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "broadband", ceil($chScale*$hash->{broadband}));
- readingsBulkUpdate($hash, "ir", ceil($chScale*$hash->{ir}));
- readingsBulkUpdate($hash, "luminosity", $lux);
- readingsEndUpdate($hash, 1);
- }
- }
-
- #readingsSingleUpdate($hash,"failures",ReadingsVal($hash->{NAME},"failures",0)+1,1);
+ if(!defined($sets{$cmd})) {
+ return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
+ }
+
+ RemoveInternalTimer($hash);
+ I2C_TSL2561_Poll($hash);
+ return undef;
}
-=head2 I2C_TSL2561_Set
- Title: I2C_TSL2561_Set
- Function: Implements SetFn function.
- Returns: string|undef
- Args: named arguments:
- -argument1 => hash: $hash hash of device
- -argument2 => array: @a argument array
-
-=cut
-
-sub I2C_TSL2561_Set($@) {
- my ($hash, @a) = @_;
-
- my $name =$a[0];
- my $cmd = $a[1];
- my $val = $a[2];
-
- if(!defined($sets{$cmd})) {
- return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
- }
-
- if ($cmd eq 'readValues') {
-
- }
-}
-
-=head2 I2C_TSL2561_Undef
- Title: I2C_TSL2561_Undef
- Function: Implements UndefFn function.
- Returns: undef
- Args: named arguments:
- -argument1 => hash: $hash hash of device
- -argument2 => array: @a argument array
-
-=cut
-
sub I2C_TSL2561_Undef($$) {
- my ($hash, $arg) = @_;
-
- RemoveInternalTimer($hash);
- if ($hash->{HiPi_used}) {
- $hash->{devTSL2561}->close()
- }
-
- return undef;
+ my ($hash, $arg) = @_;
+
+ RemoveInternalTimer($hash);
+ if ($hash->{HiPi_used}) {
+ $hash->{devTSL2561}->close()
+ }
+
+ return undef;
}
+#
+# process received control register
+#
sub I2C_TSL2561_I2CRcvControl($$) {
- my ($hash, $control) = @_;
- my $name = $hash->{NAME};
-
- my $enabled = $control & 0x3;
- if ($enabled == TSL2561_CONTROL_POWERON) {
- Log3 $name, 5, "I2C_TSL2561_Enable: is enabled";
- $hash->{sensorEnabled} = 1;
- } else {
- Log3 $name, 5, "I2C_TSL2561_Enable: is not enabled";
- readingsSingleUpdate($hash, 'state', 'Error', 1);
- $hash->{sensorEnabled} = 0;
- }
-
+ my ($hash, $control) = @_;
+ my $name = $hash->{NAME};
+
+ my $enabled = $control & 0x3;
+ if ($enabled == TSL2561_CONTROL_POWERON) {
+ Log3 $name, 5, "I2C_TSL2561_I2CRcvControl: is enabled";
+ $hash->{sensorEnabled} = 1;
+ $hash->{acquiState} = ACQUI_STATE_ENABLED;
+ $hash->{acquiStarted} = [gettimeofday];
+ } else {
+ Log3 $name, 5, "I2C_TSL2561_I2CRcvControl: is disabled";
+ $hash->{sensorEnabled} = 0;
+ $hash->{acquiState} = ACQUI_STATE_DISABLED;
+ }
+
}
+#
+# process received ID register
+#
sub I2C_TSL2561_I2CRcvID($$) {
- my ($hash, $sensorId) = @_;
- my $name = $hash->{NAME};
-
- if ( !($sensorId & 0b00010000) ) {
- return $name . ': Error! I2C failure: Please check your i2c bus and the connected device address: ' . $hash->{I2C_Address};
- }
-
- my $package = '';
- $hash->{tsl2561Package} = $sensorId >> 4;
- if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
- $package = 'CS';
- } else {
- $package = 'T/FN/CL';
- }
- $hash->{sensorType} = 'TSL2561 Package ' . $package . ' Rev. ' . ( $sensorId & 0x0f );
+ my ($hash, $sensorId) = @_;
+ my $name = $hash->{NAME};
+
+ if ( !($sensorId & 0b00010000) ) {
+ return $name . ': Error! I2C failure: Please check your i2c bus and the connected device address: ' . $hash->{I2C_Address};
+ }
+
+ my $package = '';
+ $hash->{tsl2561Package} = $sensorId >> 4;
+ if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
+ $package = 'CS';
+ } else {
+ $package = 'T/FN/CL';
+ }
+ $hash->{sensorType} = 'TSL2561 Package ' . $package . ' Rev. ' . ( $sensorId & 0x0f );
- Log3 $name, 5, 'sensorId ' . $hash->{sensorType};
+ Log3 $name, 5, 'I2C_TSL2561_I2CRcvID: sensorId ' . $hash->{sensorType};
}
+#
+# process received timing register
+#
sub I2C_TSL2561_I2CRcvTiming ($$) {
- my ($hash, $timing) = @_;
-
- $hash->{tsl2561IntegrationTime} = $timing & 0x03;
- $hash->{tsl2561Gain} = $timing & 0x10;
-
- my $name = $hash->{NAME};
- Log3 $name, 5, "I2C_TSL2561_I2CRcvTiming: $timing, $hash->{tsl2561IntegrationTime}, $hash->{tsl2561Gain}";
+ my ($hash, $timing) = @_;
+ my $name = $hash->{NAME};
+
+ $hash->{tsl2561IntegrationTime} = $timing & 0x03;
+ $hash->{tsl2561Gain} = $timing & 0x10;
+
+ Log3 $name, 5, "I2C_TSL2561_I2CRcvTiming: $timing, $hash->{tsl2561IntegrationTime}, $hash->{tsl2561Gain}";
}
+#
+# process received ADC channel 0 register
+#
sub I2C_TSL2561_I2CRcvChan0 ($$) {
- my ($hash, $broadband) = @_;
-
- my $name = $hash->{NAME};
- Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan0 ' . $broadband;
-
- $hash->{broadband} = $broadband;
+ my ($hash, $broadband) = @_;
+ my $name = $hash->{NAME};
+
+ Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan0 ' . $broadband;
+
+ $hash->{broadband} = $broadband;
+ $hash->{acquiState} = ACQUI_STATE_DATA_CH0_RECEIVED;
}
+#
+# process received ADC channel 1 register
+#
sub I2C_TSL2561_I2CRcvChan1 ($$) {
- my ($hash, $ir) = @_;
-
- my $name = $hash->{NAME};
- Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan1 ' . $ir;
-
- $hash->{ir} = $ir;
+ my ($hash, $ir) = @_;
+ my $name = $hash->{NAME};
+
+ Log3 $name, 5, 'I2C_TSL2561_I2CRcvChan1 ' . $ir;
+
+ $hash->{ir} = $ir;
+ $hash->{acquiState} = ACQUI_STATE_DATA_CH1_RECEIVED;
}
+#
+# preprocess received data from I2C bus
+#
sub I2C_TSL2561_I2CRec ($$) {
- my ($hash, $clientmsg) = @_;
- my $name = $hash->{NAME};
-
- my $pname = undef;
- unless ($hash->{HiPi_used}) { #nicht nutzen wenn HiPi Bibliothek in Benutzung
- my $phash = $hash->{IODev};
- $pname = $phash->{NAME};
- while (my ( $k, $v ) = each %$clientmsg) { #erzeugen von Internals für alle Keys in $clientmsg die mit dem physical Namen beginnen
- $hash->{$k} = $v if $k =~ /^$pname/;
- }
- }
-
- if ($clientmsg->{direction} && $clientmsg->{reg} &&
- (($pname && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok")
- || $hash->{HiPi_used})) {
- if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received})) {
- my $register = $clientmsg->{reg} & 0xF;
- Log3 $hash, 5, "$name RX register $register, $clientmsg->{nbyte} byte: $clientmsg->{received}";
- my $byte = undef;
- my $word = undef;
- my @raw = split(" ", $clientmsg->{received});
- if ($clientmsg->{nbyte} == 1) {
- $byte = $raw[0];
- } elsif ($clientmsg->{nbyte} == 2) {
- $word = $raw[1] << 8 | $raw[0];
- }
- if ($register == TSL2561_REGISTER_CONTROL) {
- I2C_TSL2561_I2CRcvControl($hash, $byte);
- } elsif ($register == TSL2561_REGISTER_ID) {
- I2C_TSL2561_I2CRcvID($hash, $byte);
- } elsif ($register == TSL2561_REGISTER_TIMING) {
- I2C_TSL2561_I2CRcvTiming($hash, $byte);
- } elsif ($register == TSL2561_REGISTER_CHAN0_LOW) {
- I2C_TSL2561_I2CRcvChan0($hash, $word);
- } elsif ($register == TSL2561_REGISTER_CHAN1_LOW) {
- I2C_TSL2561_I2CRcvChan1($hash, $word);
- } else {
- Log3 $name, 3, "I2C_TSL2561_I2CRec unsupported register $register";
- }
- }
- }
+ my ($hash, $clientmsg) = @_;
+ my $name = $hash->{NAME};
+
+ my $pname = undef;
+ unless ($hash->{HiPi_used}) { #nicht nutzen wenn HiPi Bibliothek in Benutzung
+ my $phash = $hash->{IODev};
+ $pname = $phash->{NAME};
+ while (my ( $k, $v ) = each %$clientmsg) { #erzeugen von Internals für alle Keys in $clientmsg die mit dem physical Namen beginnen
+ $hash->{$k} = $v if $k =~ /^$pname/;
+ }
+ }
+
+ if ($clientmsg->{direction} && $clientmsg->{reg} &&
+ (($pname && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok")
+ || $hash->{HiPi_used})) {
+ if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received})) {
+ my $register = $clientmsg->{reg} & 0xF;
+ Log3 $hash, 5, "$name RX register $register, $clientmsg->{nbyte} byte: $clientmsg->{received}";
+ my $byte = undef;
+ my $word = undef;
+ my @raw = split(" ", $clientmsg->{received});
+ if ($clientmsg->{nbyte} == 1) {
+ $byte = $raw[0];
+ } elsif ($clientmsg->{nbyte} == 2) {
+ $word = $raw[1] << 8 | $raw[0];
+ }
+ if ($register == TSL2561_REGISTER_CONTROL) {
+ I2C_TSL2561_I2CRcvControl($hash, $byte);
+ } elsif ($register == TSL2561_REGISTER_ID) {
+ I2C_TSL2561_I2CRcvID($hash, $byte);
+ } elsif ($register == TSL2561_REGISTER_TIMING) {
+ I2C_TSL2561_I2CRcvTiming($hash, $byte);
+ } elsif ($register == TSL2561_REGISTER_CHAN0_LOW) {
+ I2C_TSL2561_I2CRcvChan0($hash, $word);
+ } elsif ($register == TSL2561_REGISTER_CHAN1_LOW) {
+ I2C_TSL2561_I2CRcvChan1($hash, $word);
+ } else {
+ Log3 $name, 3, "I2C_TSL2561_I2CRec unsupported register $register";
+ }
+ }
+ }
}
=head2 I2C_TSL2561_Enable
- Title: I2C_TSL2561_Enable
- Function: Enables the device
- Returns: 1 if sensor was enabled, 0 if enabling sensor failed
- Args: named arguments:
- -argument1 => hash: $hash hash of device
+ Title: I2C_TSL2561_Enable
+ Function: Enables the device
+ Returns: 1 if sensor was enabled, 0 if enabling sensor failed
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_Enable($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- Log3 $name, 5, 'I2C_TSL2561_Enable: start ';
-
- # Detect TLS2561 package type and init integration time and gain
- if (!defined($hash->{tsl2561Package})) {
- # Get TLS2561 package type
- if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID, 1)) {
- # Preset integration time and gain
- I2C_TSL2561_SetGain($hash, $hash->{tsl2561Gain});
- }
- }
-
- # Enable TLS2561
- $hash->{sensorEnabled} = 0;
- if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON)) {
- I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, 1);
- }
-
- Log3 $name, 5, 'I2C_TSL2561_Enable: end ';
-
- return $hash->{sensorEnabled};
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ Log3 $name, 5, 'I2C_TSL2561_Enable: start ';
+
+ # Detect TLS2561 package type and init integration time and gain
+ my $initialized = 1;
+ if (!defined($hash->{tsl2561Package})) {
+ # Get TLS2561 package type
+ $initialized = 0;
+ if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID, 1)) {
+ # Preset integration time and gain
+ if (I2C_TSL2561_SetGain($hash, $hash->{tsl2561Gain})) {
+ $initialized = 1;
+ }
+ }
+ }
+
+ # Enable TLS2561
+ $hash->{sensorEnabled} = 0;
+ if ($initialized) {
+ if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON)) {
+ I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, 1);
+ }
+ if (!$hash->{sensorEnabled}) {
+ # Enable failed (no sensor at address or wrong I2C device)
+ $hash->{tsl2561Package} = undef;
+ }
+ }
+
+ Log3 $name, 5, 'I2C_TSL2561_Enable: end ';
+
+ return $hash->{sensorEnabled};
}
=head2 I2C_TSL2561_Disable
- Title: I2C_TSL2561_Disable
- Function: Enables the device
- Returns: 1 if write was successful, 0 if write failed
- Args: named arguments:
- -argument1 => hash: $hash hash of device
+ Title: I2C_TSL2561_Disable
+ Function: Enables the device
+ Returns: 1 if write was successful, 0 if write failed
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_Disable($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
- Log3 $name, 5, 'I2C_TSL2561_Disable: start ';
- my $success = I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF);
- $hash->{sensorEnabled} = 0;
- Log3 $name, 5, 'I2C_TSL2561_Disable: end ';
-
- return $success;
+ Log3 $name, 5, 'I2C_TSL2561_Disable: start ';
+ my $success = I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWEROFF);
+ $hash->{sensorEnabled} = 0;
+ Log3 $name, 5, 'I2C_TSL2561_Disable: end ';
+
+ return $success;
}
=head2 I2C_TSL2561_GetData
- Title: I2C_TSL2561_GetData
- Function: Private function to read luminosity on both channels
- Returns: -
- Args: named arguments:
- -argument1 => hash: $hash hash of device
+ Title: I2C_TSL2561_GetData
+ Function: Private function to read luminosity on both channels
+ Returns: -
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_GetData($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- # Enable the device by setting the control bit to 0x03
- if (I2C_TSL2561_Enable($hash)) {
-
- # Wait x ms for ADC to complete
- if ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
- usleep(14000); # 14ms
- } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
- usleep(102000); # 102ms
- } else {
- usleep(403000); # 403ms
- }
-
- # Reads a two byte value from channel 0 (visible + infrared)
- if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW, 2)) {
-
- # Reads a two byte value from channel 1 (infrared)
- I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW, 2);
- }
- }
-
- # Turn the device off to save power
- I2C_TSL2561_Disable($hash);
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
+
+ # Data acquisition state machine with asynchronous wait
+ my $success = 1;
+ my $operations = 0;
+ while (1) {
+ $operations++;
+ if ($hash->{acquiState} == ACQUI_STATE_ERROR) {
+ $hash->{calcState} = CALC_STATE_ERROR;
+ readingsSingleUpdate($hash, 'state', STATE_I2C_ERROR, 1);
+ # Turn the device off to save power
+ I2C_TSL2561_Disable($hash);
+ $hash->{acquiState} = ACQUI_STATE_DISABLED;
+ $success = 0;
+ last; # Abort, Start again at next slow poll
+ } elsif ($operations > 10) {
+ # Too many consecutive operations, abort
+ $hash->{acquiState} = ACQUI_STATE_ERROR;
+ Log3 $name, 5, "I2C_TSL2561_GetData: state machine stuck, aborting";
+ } elsif ($hash->{acquiState} == ACQUI_STATE_DISABLED) {
+ # Enable the device by setting the control bit to 0x03
+ if (!I2C_TSL2561_Enable($hash)) {
+ $hash->{acquiState} = ACQUI_STATE_ERROR;
+ }
+ } elsif ($hash->{acquiState} == ACQUI_STATE_ENABLED) {
+ # Wait x ms for ADC to complete
+ $hash->{calcState} = CALC_STATE_DATA_REQUESTED;
+ my $now = [gettimeofday];
+ if (tv_interval($hash->{acquiStarted}, $now) >= I2C_TSL2561_GetIntegrationTime($hash)) {
+ $hash->{acquiState} = ACQUI_STATE_DATA_AVAILABLE;
+ } else {
+ last; # Wait, check again after next fast poll
+ }
+ } elsif ($hash->{acquiState} == ACQUI_STATE_DATA_AVAILABLE) {
+ # Reads a two byte value from channel 0 (visible + infrared)
+ if (!I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW, 2)) {
+ $hash->{acquiState} = ACQUI_STATE_ERROR;
+ }
+ } elsif ($hash->{acquiState} == ACQUI_STATE_DATA_CH0_RECEIVED) {
+ # Reads a two byte value from channel 1 (infrared)
+ if (!I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW, 2)) {
+ $hash->{acquiState} = ACQUI_STATE_ERROR;
+ }
+ } elsif ($hash->{acquiState} == ACQUI_STATE_DATA_CH1_RECEIVED) {
+ $hash->{calcState} = CALC_STATE_DATA_RECEIVED;
+ # Done, turn the device off to save power
+ I2C_TSL2561_Disable($hash);
+ $hash->{acquiState} = ACQUI_STATE_DISABLED;
+ last; # Done, start again at next slow poll
+ } else {
+ # Undefined state
+ $hash->{acquiState} = ACQUI_STATE_ERROR;
+ }
+ }
+
+ return $success;
}
=head2 I2C_TSL2561_SetIntegrationTime
- Title: I2C_TSL2561_SetIntegrationTime
- Function: Sets the integration time for the TSL2561
- Returns: -
- Args: named arguments:
- -argument1 => hash: $hash hash of device
- -argument1 => number: $time constant for integration time setting
+ Title: I2C_TSL2561_SetIntegrationTime
+ Function: Sets the integration time for the TSL2561
+ Returns: -
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
+ -argument1 => number: $time constant for integration time setting
=cut
sub I2C_TSL2561_SetIntegrationTime($$) {
- my ($hash, $time) = @_;
- my $name = $hash->{NAME};
-
- # Enable the device by setting the control bit to 0x03
- if (I2C_TSL2561_Enable($hash)) {
+ my ($hash, $time) = @_;
+ my $name = $hash->{NAME};
+
+ # Enable the device by setting the control bit to 0x03
+ my $success = 0;
+ if (!AttrVal($hash->{NAME}, "disable", 0) && defined($hash->{tsl2561Package})) {
+ my $state = ReadingsVal($name, 'state', '');
+ if ($state ne STATE_I2C_ERROR) {
+ # Update the timing register
+ Log3 $name, 5, "I2C_TSL2561_SetIntegrationTime: time " . $time ;
+ Log3 $name, 5, "I2C_TSL2561_SetIntegrationTime: gain " . $hash->{tsl2561Gain};
+ if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $time | $hash->{tsl2561Gain})) {
+ if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1)) {
+ $success = 1;
+ }
+ }
+ }
+ }
+
+ return $success;
+}
- # Update the timing register
- Log3 $name, 5, "I2C_TSL2561_SetIntegrationTime: time " . $time ;
- Log3 $name, 5, "I2C_TSL2561_SetIntegrationTime: gain " . $hash->{tsl2561Gain};
- if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $time | $hash->{tsl2561Gain})) {
- I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1);
- }
- }
-
- # Turn the device off to save power
- I2C_TSL2561_Disable($hash);
+#
+# decode TSL2561 integration time into decimal value
+# @param device hash
+# @return integration time in seconds that was last reported by the TSL2561
+#
+sub I2C_TSL2561_GetIntegrationTime($) {
+ my ($hash) = @_;
+ my $tsl2561IntegrationTime = $hash->{tsl2561IntegrationTime};
+
+ my $integrationTime = 0.402; # 402 ms
+ if ($tsl2561IntegrationTime == TSL2561_INTEGRATIONTIME_13MS) {
+ $integrationTime = 0.0137; # 13.7 ms
+ } elsif ($tsl2561IntegrationTime == TSL2561_INTEGRATIONTIME_101MS) {
+ $integrationTime = 0.101; # 101 ms
+ }
+
+ return $integrationTime;
}
=head2 I2C_TSL2561_SetGain
- Title: I2C_TSL2561_SetGain
- Function: Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
- Returns: -
- Args: named arguments:
- -argument1 => hash: $hash hash of device
- -argument1 => number: $gain constant for gain
+ Title: I2C_TSL2561_SetGain
+ Function: Adjusts the gain on the TSL2561 (adjusts the sensitivity to light)
+ Returns: -
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
+ -argument1 => number: $gain constant for gain
=cut
sub I2C_TSL2561_SetGain($$) {
- my ($hash, $gain) = @_;
- my $name = $hash->{NAME};
+ my ($hash, $gain) = @_;
+ my $name = $hash->{NAME};
- # Enable the device by setting the control bit to 0x03
- if (I2C_TSL2561_Enable($hash)) {
- # Update the timing register
- Log3 $name, 5, "I2C_TSL2561_SetGain: gain " . $gain ;
- Log3 $name, 5, "I2C_TSL2561_SetGain: time " . $hash->{tsl2561IntegrationTime};
- if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $gain | $hash->{tsl2561IntegrationTime})) {
- I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1);
- }
- }
-
- # Turn the device off to save power
- I2C_TSL2561_Disable($hash);
+ # Enable the device by setting the control bit to 0x03
+ my $success = 0;
+ if (!AttrVal($hash->{NAME}, "disable", 0) && defined($hash->{tsl2561Package})) {
+ my $state = ReadingsVal($name, 'state', '');
+ if ($state ne STATE_I2C_ERROR) {
+ # Update the timing register
+ Log3 $name, 5, "I2C_TSL2561_SetGain: gain " . $gain ;
+ Log3 $name, 5, "I2C_TSL2561_SetGain: time " . $hash->{tsl2561IntegrationTime};
+ if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $gain | $hash->{tsl2561IntegrationTime})) {
+ if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1)) {
+ $success = 1;
+ }
+ }
+ }
+ }
+
+ return $success;
+}
+
+#
+# decode TSL2561 gain into decimal value
+# @param device hash
+# @return decimal gain factor that was last reported by the TSL2561
+#
+sub I2C_TSL2561_GetGain($) {
+ my ($hash) = @_;
+ my $tsl2561Gain = $hash->{tsl2561Gain};
+
+ my $gain = 1;
+ if (defined($tsl2561Gain) && $tsl2561Gain) {
+ $gain = 16;
+ }
+
+ return $gain;
}
=head2 I2C_TSL2561_GetLuminosity
- Title: I2C_TSL2561_GetLuminosity
- Function: Gets the broadband (mixed lighting) and IR only values from the TSL2561, adjusting gain if auto-gain is enabled
- Returns: luminosity
- Args: named arguments:
- -argument1 => hash: $hash hash of device
+ Title: I2C_TSL2561_GetLuminosity
+ Function: Gets the broadband (mixed lighting) and IR only values from the TSL2561, adjusting gain if auto-gain is enabled and calculate luminosity
+ Returns: luminosity
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_GetLuminosity($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- # If Auto gain disabled get a single reading and continue
- if (!$hash->{tsl2561AutoGain}) {
- I2C_TSL2561_GetData($hash);
- return;
- }
-
- # Read data until we find a valid range
- my $agcCheck = 0;
- my $hi = 0;
- my $lo = 0;
- my $it = 0;
- my $lux = 0;
- my $valid = 0;
- while (!$valid) {
- $it = $hash->{tsl2561IntegrationTime};
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
- # Get the hi/low threshold for the current integration time
- if ($it==TSL2561_INTEGRATIONTIME_13MS) {
- $hi = TSL2561_AGC_THI_13MS;
- $lo = TSL2561_AGC_TLO_13MS;
- } elsif ( $it==TSL2561_INTEGRATIONTIME_101MS) {
- $hi = TSL2561_AGC_THI_101MS;
- $lo = TSL2561_AGC_TLO_101MS;
- } else {
- $hi = TSL2561_AGC_THI_402MS;
- $lo = TSL2561_AGC_TLO_402MS;
- }
+# Log3 $name, 5, "I2C_TSL2561_GetLuminosity: start";
+
+ # Luminosity calculation state machine
+ my $lux = undef;
+ my $operations = 0;
+ while(1) {
+ $operations++;
+ Log3 $name, 5, "I2C_TSL2561_GetLuminosity: calc state $hash->{calcState} acqui state $hash->{acquiState}";
+ if ($hash->{calcState} == CALC_STATE_ERROR) {
+ $hash->{calcState} = CALC_STATE_IDLE;
+ Log3 $name, 5, "I2C_TSL2561_GetLuminosity: error, aborting";
+ last; # Abort, start again at next slow poll
+ } elsif ($operations > 10) {
+ # Too many consecutive operations, abort
+ $hash->{calcState} = CALC_STATE_ERROR;
+ Log3 $name, 5, "I2C_TSL2561_GetLuminosity: state machine stuck, aborting";
+ } elsif ($hash->{calcState} == CALC_STATE_IDLE) {
+ # Request data
+ Log3 $name, 5, "I2C_TSL2561_GetLuminosity: starting new measurement";
+ if (!I2C_TSL2561_GetData($hash)) {
+ $hash->{calcState} = CALC_STATE_ERROR;
+ }
+ } elsif ($hash->{calcState} == CALC_STATE_DATA_REQUESTED) {
+ # Wait for data
+ if (I2C_TSL2561_GetData($hash)) {
+ if ($hash->{acquiState} == ACQUI_STATE_ENABLED) {
+ last; # Wait for data to arrive, check again at next fast poll
+ }
+ } else {
+ $hash->{calcState} = CALC_STATE_ERROR;
+ }
+ } elsif ($hash->{calcState} == CALC_STATE_DATA_RECEIVED) {
+ # Data was received, optimize gain
+ my $autoGain = AttrVal($name, 'autoGain', 1);
+ if ($autoGain) {
+ # Get the hi/low threshold for the current integration time
+ my $it = $hash->{tsl2561IntegrationTime};
+ my $hi = TSL2561_AGC_THI_402MS;
+ my $lo = TSL2561_AGC_TLO_402MS;
+ if ($it==TSL2561_INTEGRATIONTIME_13MS) {
+ $hi = TSL2561_AGC_THI_13MS;
+ $lo = TSL2561_AGC_TLO_13MS;
+ } elsif ( $it==TSL2561_INTEGRATIONTIME_101MS) {
+ $hi = TSL2561_AGC_THI_101MS;
+ $lo = TSL2561_AGC_TLO_101MS;
+ }
+ if (($hash->{broadband} < $lo) && ($hash->{tsl2561Gain} == TSL2561_GAIN_1X)) {
+ # Increase gain and try again
+ I2C_TSL2561_SetGain($hash, TSL2561_GAIN_16X);
+ # Drop the previous conversion results
+ $hash->{calcState} = CALC_STATE_IDLE;
+ next;
+ } elsif (($hash->{broadband} > $hi) && ($hash->{tsl2561Gain} == TSL2561_GAIN_16X)) {
+ # Drop gain and try again
+ I2C_TSL2561_SetGain($hash, TSL2561_GAIN_1X);
+ # Drop the previous conversion results
+ $hash->{calcState} = CALC_STATE_IDLE;
+ next;
+ } else {
+ # Reading is either valid, or we're already at the chips limits
+ }
+ } else {
+ # Auto gain disabled, always valid
+ }
- I2C_TSL2561_GetData($hash);
+ # Optimize integration time (make sure the sensor isn't saturated at 402 ms)
+ my $clipThreshold = 0;
+ if ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
+ $clipThreshold = TSL2561_CLIPPING_13MS;
+ } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
+ $clipThreshold = TSL2561_CLIPPING_101MS;
+ } else {
+ $clipThreshold = TSL2561_CLIPPING_402MS;
+ }
+ my $autoIntegrationTime = AttrVal($name, 'autoIntegrationTime', 0);
+ if (($hash->{broadband} > $clipThreshold) || ($hash->{ir} > $clipThreshold)) {
+ # ADC saturated, try to decrease integration time
+ if ($autoIntegrationTime && $hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_402MS) {
+ # Drop integration time and try again
+ I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_101MS);
+ # Drop the previous conversion results
+ $hash->{calcState} = CALC_STATE_IDLE;
+ next;
+ } else {
+ # Integration time fixed or already below 402 ms, give up
+ $hash->{saturated} = 1;
+ }
+ } elsif ($autoIntegrationTime
+ && ($hash->{broadband} < ($clipThreshold >> 2) && $hash->{ir} < ($clipThreshold >> 2))
+ && ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS || $hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS)) {
+ # Integration time below 178 ms, maximize
+ I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_402MS);
+ # Drop the previous conversion results
+ $hash->{calcState} = CALC_STATE_IDLE;
+ next;
+ } else {
+ # Readings are not saturated or auto integration time is disabled
+ $hash->{saturated} = 0;
+ }
- # Run an auto-gain check if we haven't already done so ...
- if (!$agcCheck) {
- if (($hash->{broadband} < $lo) && ($hash->{tsl2561Gain} == TSL2561_GAIN_1X)) {
- # Increase the gain and try again
- I2C_TSL2561_SetGain($hash, TSL2561_GAIN_16X);
- # Drop the previous conversion results
- I2C_TSL2561_GetData($hash);
- # Set a flag to indicate we've adjusted the gain
- $agcCheck = 1;
- } elsif (($hash->{broadband} > $hi) && ($hash->{tsl2561Gain} == TSL2561_GAIN_16X)) {
- # Drop gain to 1x and try again
- I2C_TSL2561_SetGain($hash, TSL2561_GAIN_1X);
- # Drop the previous conversion results
- I2C_TSL2561_GetData($hash);
- # Set a flag to indicate we've adjusted the gain
- $agcCheck = 1;
- } else {
- # Nothing to look at here, keep moving ....
- # Reading is either valid, or we're already at the chips limits
- $valid = 1;
- }
- } else {
- # If we've already adjusted the gain once, just return the new results.
- # This avoids endless loops where a value is at one extreme pre-gain,
- # and the the other extreme post-gain
- $valid = 1;
- }
- }
+ # Received data is valid, calculate luminosity
+ $lux = I2C_TSL2561_CalculateLux($hash);
+ $hash->{calcState} = CALC_STATE_IDLE;
+ last; # Done, start again at next slow poll
+ } else {
+ # Undefined state
+ $hash->{calcState} = CALC_STATE_ERROR;
+ }
+ }
+
+# Log3 $name, 5, "I2C_TSL2561_GetLuminosity: end";
+
+ return $lux;
}
# get channel scale
sub I2C_TSL2561_GetChannelScale($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
- my $chScale = 0;
-
- if (AttrVal($name, 'floatArithmetics', 0)) {
- # Get the correct scale depending on the integration time
- if (!defined($hash->{tsl2561IntegrationTime})) {
- $chScale = 1.0;
- } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
- $chScale = 322.0/11;
- } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
- $chScale = 322.0/81;
- } else {
- $chScale = 1.0;
- }
+ my $chScale = 0;
+
+ if (AttrVal($name, 'floatArithmetics', 0)) {
+ # Get the correct scale depending on the integration time
+ if (!defined($hash->{tsl2561IntegrationTime})) {
+ $chScale = 1.0;
+ } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
+ $chScale = 322.0/11;
+ } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
+ $chScale = 322.0/81;
+ } else {
+ $chScale = 1.0;
+ }
- # Scale for gain (1x or 16x)
- if (!defined($hash->{tsl2561Gain}) || !$hash->{tsl2561Gain}) {
- $chScale = $chScale*16;
- }
- } else {
- # Get the correct scale depending on the integration time
- if ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
- $chScale = TSL2561_LUX_CHSCALE_TINT0;
- } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
- $chScale = TSL2561_LUX_CHSCALE_TINT1;
- } else {
- $chScale = (1 << TSL2561_LUX_CHSCALE);
- }
+ # Scale for gain (1x or 16x)
+ if (!defined($hash->{tsl2561Gain}) || !$hash->{tsl2561Gain}) {
+ $chScale = $chScale*16;
+ }
+ } else {
+ # Get the correct scale depending on the integration time
+ if ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
+ $chScale = TSL2561_LUX_CHSCALE_TINT0;
+ } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
+ $chScale = TSL2561_LUX_CHSCALE_TINT1;
+ } else {
+ $chScale = (1 << TSL2561_LUX_CHSCALE);
+ }
- # Scale for gain (1x or 16x)
- if (!$hash->{tsl2561Gain}) {
- $chScale = $chScale << 4;
- }
- }
-
- return $chScale;
+ # Scale for gain (1x or 16x)
+ if (!defined($hash->{tsl2561Gain}) || !$hash->{tsl2561Gain}) {
+ $chScale = $chScale << 4;
+ }
+ }
+
+ return $chScale;
}
=head2 I2C_TSL2561_CalculateLux
- Title: I2C_TSL2561_CalculateLux
- Function: Converts the raw sensor values to the standard SI lux equivalent. Returns 0 if the sensor is saturated and the values are unreliable.
- Returns: number
- Args: named arguments:
- -argument1 => hash: $hash hash of device
+ Title: I2C_TSL2561_CalculateLux
+ Function: Converts the raw sensor values to the standard SI lux equivalent. Returns 0 if the sensor is saturated and the values are unreliable.
+ Returns: number
+ Args: named arguments:
+ -argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_CalculateLux($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
+ my ($hash) = @_;
+ my $name = $hash->{NAME};
- I2C_TSL2561_GetLuminosity($hash);
+ # Get the correct scale depending on gain and integration time
+ my $chScale = I2C_TSL2561_GetChannelScale($hash);
+ if (AttrVal($name, 'floatArithmetics', 0)) {
+ # Scale the channel values
+ my $channel0 = $chScale*$hash->{broadband};
+ my $channel1 = $chScale*$hash->{ir};
- # Make sure the sensor isn't saturated!
- my $clipThreshold = 0;
- if ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_13MS) {
- $clipThreshold = TSL2561_CLIPPING_13MS;
- } elsif ($hash->{tsl2561IntegrationTime} == TSL2561_INTEGRATIONTIME_101MS) {
- $clipThreshold = TSL2561_CLIPPING_101MS;
- } else {
- $clipThreshold = TSL2561_CLIPPING_402MS;
- }
+ # Find the ratio of the channel values (Channel1/Channel0)
+ my $ratio = 0.0;
+ if ($channel0 != 0) {
+ $ratio = $channel1/$channel0;
+ }
+
+ # Calculate luminosity (see TSL2561 data sheet)
+ my $lux = undef;
+ if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
+ # CS package
+ if ($ratio <= 0.52) {
+ $lux = 0.0315*$channel0 - 0.0593*$channel1*pow($ratio, 1.4);
+ } elsif ($ratio <= 0.65) {
+ $lux = 0.0229*$channel0 - 0.0291*$channel1;
+ } elsif ($ratio <= 0.80) {
+ $lux = 0.0157*$channel0 - 0.0180*$channel1;
+ } elsif ($ratio <= 1.30) {
+ $lux = 0.00338*$channel0 - 0.00260*$channel1;
+ } else {
+ $lux = 0.0;
+ }
+ } else {
+ # T, FN and CL package
+ if ($ratio <= 0.50) {
+ $lux = 0.0304*$channel0 - 0.062*$channel1*pow($ratio, 1.4);
+ } elsif ($ratio <= 0.61) {
+ $lux = 0.0224*$channel0 - 0.031*$channel1;
+ } elsif ($ratio <= 0.80) {
+ $lux = 0.0128*$channel0 - 0.0153*$channel1;
+ } elsif ($ratio <= 1.30) {
+ $lux = 0.00146*$channel0 - 0.00112*$channel1;
+ } else {
+ $lux = 0.0;
+ }
+ }
+
+ if ($lux >= 100) {
+ # Round to 3 significant digits if at least 100
+ my $roundFactor = 10**(floor(log($lux)/log(10)) - 2);
+ $lux = $roundFactor*floor($lux/$roundFactor + 0.5);
+ } else {
+ # Round to 1 fractional digit if less than 100
+ $lux = floor(10*$lux + 0.5)/10;
+ }
+
+ return $lux;
+ } else {
+ # Scale the channel values
+ my $channel0 = ($hash->{broadband} * $chScale) >> TSL2561_LUX_CHSCALE;
+ my $channel1 = ($hash->{ir} * $chScale) >> TSL2561_LUX_CHSCALE;
- # Return 0 lux if the sensor is saturated
- if (($hash->{broadband} > $clipThreshold) || ($hash->{ir} > $clipThreshold)) {
- readingsSingleUpdate($hash, 'state', 'Saturated', 1);
- return 0;
- } else {
- readingsSingleUpdate($hash, 'state', 'Initialized', 1);
- }
+ # Find the ratio of the channel values (Channel1/Channel0)
+ my $ratio1 = 0;
+ if ($channel0 != 0) {
+ $ratio1 = ($channel1 << (TSL2561_LUX_RATIOSCALE+1)) / $channel0;
+ }
- # Get the correct scale depending on gain and integration time
- my $chScale = I2C_TSL2561_GetChannelScale($hash);
- if (AttrVal($name, 'floatArithmetics', 0)) {
- # Scale the channel values
- my $channel0 = $chScale*$hash->{broadband};
- my $channel1 = $chScale*$hash->{ir};
+ # round the ratio value
+ my $ratio = ($ratio1 + 1) >> 1;
+
+ my $b=0;
+ my $m=0;
- # Find the ratio of the channel values (Channel1/Channel0)
- my $ratio = 0.0;
- if ($channel0 != 0) {
- $ratio = $channel1/$channel0;
- }
-
- # Calculate luminosity (see TSL2561 data sheet)
- my $lux = undef;
- if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
- # CS package
- if ($ratio <= 0.52) {
- $lux = 0.0315*$channel0 - 0.0593*$channel1*pow($ratio, 1.4);
- } elsif ($ratio <= 0.65) {
- $lux = 0.0229*$channel0 - 0.0291*$channel1;
- } elsif ($ratio <= 0.80) {
- $lux = 0.0157*$channel0 - 0.0180*$channel1;
- } elsif ($ratio <= 1.30) {
- $lux = 0.00338*$channel0 - 0.00260*$channel1;
- } else {
- $lux = 0.0;
- }
- } else {
- # T, FN and CL package
- if ($ratio <= 0.50) {
- $lux = 0.0304*$channel0 - 0.062*$channel1*pow($ratio, 1.4);
- } elsif ($ratio <= 0.61) {
- $lux = 0.0224*$channel0 - 0.031*$channel1;
- } elsif ($ratio <= 0.80) {
- $lux = 0.0128*$channel0 - 0.0153*$channel1;
- } elsif ($ratio <= 1.30) {
- $lux = 0.00146*$channel0 - 0.00112*$channel1;
- } else {
- $lux = 0.0;
- }
- }
-
- if ($lux >= 100) {
- # Round to 3 significant digits if at least 100
- my $roundFactor = 10**(floor(log($lux)/log(10)) - 2);
- $lux = $roundFactor*floor($lux/$roundFactor + 0.5);
- } else {
- # Round to 1 fractional digit if less than 100
- $lux = floor(10*$lux + 0.5)/10;
- }
-
- return $lux;
- } else {
- # Scale the channel values
- my $channel0 = ($hash->{broadband} * $chScale) >> TSL2561_LUX_CHSCALE;
- my $channel1 = ($hash->{ir} * $chScale) >> TSL2561_LUX_CHSCALE;
+ if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
+ # CS package
+ if (($ratio >= 0) && ($ratio <= TSL2561_LUX_K1C)) {
+ $b=TSL2561_LUX_B1C;
+ $m=TSL2561_LUX_M1C;
+ } elsif ($ratio <= TSL2561_LUX_K2C) {
+ $b=TSL2561_LUX_B2C;
+ $m=TSL2561_LUX_M2C;
+ } elsif ($ratio <= TSL2561_LUX_K3C) {
+ $b=TSL2561_LUX_B3C;
+ $m=TSL2561_LUX_M3C;
+ } elsif ($ratio <= TSL2561_LUX_K4C) {
+ $b=TSL2561_LUX_B4C;
+ $m=TSL2561_LUX_M4C;
+ } elsif ($ratio <= TSL2561_LUX_K5C) {
+ $b=TSL2561_LUX_B5C;
+ $m=TSL2561_LUX_M5C;
+ } elsif ($ratio <= TSL2561_LUX_K6C) {
+ $b=TSL2561_LUX_B6C;
+ $m=TSL2561_LUX_M6C;
+ } elsif ($ratio <= TSL2561_LUX_K7C) {
+ $b=TSL2561_LUX_B7C;
+ $m=TSL2561_LUX_M7C;
+ } elsif ($ratio > TSL2561_LUX_K8C) {
+ $b=TSL2561_LUX_B8C;
+ $m=TSL2561_LUX_M8C;
+ }
+ } elsif ($hash->{tsl2561Package} == TSL2561_PACKAGE_T_FN_CL) {
+ # T, FN and CL package
+ if (($ratio >= 0) && ($ratio <= TSL2561_LUX_K1T)) {
+ $b=TSL2561_LUX_B1T;
+ $m=TSL2561_LUX_M1T;
+ } elsif ($ratio <= TSL2561_LUX_K2T) {
+ $b=TSL2561_LUX_B2T;
+ $m=TSL2561_LUX_M2T;
+ } elsif ($ratio <= TSL2561_LUX_K3T) {
+ $b=TSL2561_LUX_B3T;
+ $m=TSL2561_LUX_M3T;
+ } elsif ($ratio <= TSL2561_LUX_K4T) {
+ $b=TSL2561_LUX_B4T;
+ $m=TSL2561_LUX_M4T;
+ } elsif ($ratio <= TSL2561_LUX_K5T) {
+ $b=TSL2561_LUX_B5T;
+ $m=TSL2561_LUX_M5T;
+ } elsif ($ratio <= TSL2561_LUX_K6T) {
+ $b=TSL2561_LUX_B6T;
+ $m=TSL2561_LUX_M6T;
+ } elsif ($ratio <= TSL2561_LUX_K7T) {
+ $b=TSL2561_LUX_B7T;
+ $m=TSL2561_LUX_M7T;
+ } elsif ($ratio > TSL2561_LUX_K8T) {
+ $b=TSL2561_LUX_B8T;
+ $m=TSL2561_LUX_M8T;
+ }
+ }
- # Find the ratio of the channel values (Channel1/Channel0)
- my $ratio1 = 0;
- if ($channel0 != 0) {
- $ratio1 = ($channel1 << (TSL2561_LUX_RATIOSCALE+1)) / $channel0;
- }
+ my $temp = (($channel0 * $b) - ($channel1 * $m));
- # round the ratio value
- my $ratio = ($ratio1 + 1) >> 1;
-
- my $b=0;
- my $m=0;
+ # Do not allow negative lux value
+ if ($temp < 0) {
+ $temp = 0;
+ }
- if ($hash->{tsl2561Package} == TSL2561_PACKAGE_CS) {
- # CS package
- if (($ratio >= 0) && ($ratio <= TSL2561_LUX_K1C)) {
- $b=TSL2561_LUX_B1C;
- $m=TSL2561_LUX_M1C;
- } elsif ($ratio <= TSL2561_LUX_K2C) {
- $b=TSL2561_LUX_B2C;
- $m=TSL2561_LUX_M2C;
- } elsif ($ratio <= TSL2561_LUX_K3C) {
- $b=TSL2561_LUX_B3C;
- $m=TSL2561_LUX_M3C;
- } elsif ($ratio <= TSL2561_LUX_K4C) {
- $b=TSL2561_LUX_B4C;
- $m=TSL2561_LUX_M4C;
- } elsif ($ratio <= TSL2561_LUX_K5C) {
- $b=TSL2561_LUX_B5C;
- $m=TSL2561_LUX_M5C;
- } elsif ($ratio <= TSL2561_LUX_K6C) {
- $b=TSL2561_LUX_B6C;
- $m=TSL2561_LUX_M6C;
- } elsif ($ratio <= TSL2561_LUX_K7C) {
- $b=TSL2561_LUX_B7C;
- $m=TSL2561_LUX_M7C;
- } elsif ($ratio > TSL2561_LUX_K8C) {
- $b=TSL2561_LUX_B8C;
- $m=TSL2561_LUX_M8C;
- }
- } elsif ($hash->{tsl2561Package} == TSL2561_PACKAGE_T_FN_CL) {
- # T, FN and CL package
- if (($ratio >= 0) && ($ratio <= TSL2561_LUX_K1T)) {
- $b=TSL2561_LUX_B1T;
- $m=TSL2561_LUX_M1T;
- } elsif ($ratio <= TSL2561_LUX_K2T) {
- $b=TSL2561_LUX_B2T;
- $m=TSL2561_LUX_M2T;
- } elsif ($ratio <= TSL2561_LUX_K3T) {
- $b=TSL2561_LUX_B3T;
- $m=TSL2561_LUX_M3T;
- } elsif ($ratio <= TSL2561_LUX_K4T) {
- $b=TSL2561_LUX_B4T;
- $m=TSL2561_LUX_M4T;
- } elsif ($ratio <= TSL2561_LUX_K5T) {
- $b=TSL2561_LUX_B5T;
- $m=TSL2561_LUX_M5T;
- } elsif ($ratio <= TSL2561_LUX_K6T) {
- $b=TSL2561_LUX_B6T;
- $m=TSL2561_LUX_M6T;
- } elsif ($ratio <= TSL2561_LUX_K7T) {
- $b=TSL2561_LUX_B7T;
- $m=TSL2561_LUX_M7T;
- } elsif ($ratio > TSL2561_LUX_K8T) {
- $b=TSL2561_LUX_B8T;
- $m=TSL2561_LUX_M8T;
- }
- }
+ # Round lsb (2^(LUX_SCALE-1))
+ $temp += (1 << (TSL2561_LUX_LUXSCALE-1));
- my $temp = (($channel0 * $b) - ($channel1 * $m));
-
- # Do not allow negative lux value
- if ($temp < 0) {
- $temp = 0;
- }
-
- # Round lsb (2^(LUX_SCALE-1))
- $temp += (1 << (TSL2561_LUX_LUXSCALE-1));
-
- # Strip off fractional portion
- my $lux = $temp >> TSL2561_LUX_LUXSCALE;
-
- # Signal I2C had no errors
- return $lux;
- }
+ # Strip off fractional portion
+ my $lux = $temp >> TSL2561_LUX_LUXSCALE;
+ # Signal I2C had no errors
+ return $lux;
+ }
}
sub I2C_TSL2561_i2cread($$$) {
- my ($hash, $reg, $nbyte) = @_;
- my $success = 1;
-
- if ($hash->{HiPi_used}) {
- eval {
- my @values = $hash->{devTSL2561}->bus_read($reg, $nbyte);
- I2C_TSL2561_I2CRec($hash, {
- direction => "i2cread",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- nbyte => $nbyte,
- received => join (' ',@values),
- });
- };
- Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;
- } elsif (defined (my $iodev = $hash->{IODev})) {
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cread",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- nbyte => $nbyte
- });
- if ($hash->{$iodev->{NAME}.'_SENDSTAT'} eq 'error') {
- $hash->{tsl2561Package} = undef;
- readingsSingleUpdate($hash, 'state', 'I2C Error', 1);
- $success = 0;
- }
- } else {
- Log3 ($hash, 1, $hash->{NAME} . ': ' . "no IODev assigned to '$hash->{NAME}'");
- $success = 0;
- }
-
- return $success;
+ my ($hash, $reg, $nbyte) = @_;
+ my $success = 1;
+
+ local $SIG{__WARN__} = sub {
+ my $message = shift;
+ # turn warnings from RPII2C_HWACCESS_ioctl into exception
+ if ($message =~ /Exiting subroutine via last at.*00_RPII2C.pm/) {
+ die;
+ } else {
+ warn($message);
+ }
+ };
+
+ if ($hash->{HiPi_used}) {
+ eval {
+ my @values = $hash->{devTSL2561}->bus_read($reg, $nbyte);
+ I2C_TSL2561_I2CRec($hash, {
+ direction => "i2cread",
+ i2caddress => $hash->{I2C_Address},
+ reg => $reg,
+ nbyte => $nbyte,
+ received => join (' ',@values),
+ });
+ };
+ Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;
+ } elsif (defined (my $iodev = $hash->{IODev})) {
+ eval {
+ CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
+ direction => "i2cread",
+ i2caddress => $hash->{I2C_Address},
+ reg => $reg,
+ nbyte => $nbyte
+ });
+ };
+ if ($hash->{$iodev->{NAME}.'_SENDSTAT'} eq 'error') {
+ readingsSingleUpdate($hash, 'state', STATE_I2C_ERROR, 1);
+ $success = 0;
+ }
+ } else {
+ Log3 ($hash, 1, $hash->{NAME} . ': ' . "no IODev assigned to '$hash->{NAME}'");
+ $success = 0;
+ }
+
+ return $success;
}
sub I2C_TSL2561_i2cwrite($$$) {
- my ($hash, $reg, @data) = @_;
- my $success = 1;
-
- if ($hash->{HiPi_used}) {
- eval {
- $hash->{devTSL2561}->bus_write($reg, join (' ',@data));
- I2C_TSL2561_I2CRec($hash, {
- direction => "i2cwrite",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- data => join (' ',@data),
- });
- };
- Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;
- } elsif (defined (my $iodev = $hash->{IODev})) {
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cwrite",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- data => join (' ',@data),
- });
- if ($hash->{$iodev->{NAME}.'_SENDSTAT'} eq 'error') {
- $hash->{tsl2561Package} = undef;
- readingsSingleUpdate($hash, 'state', 'I2C Error', 1);
- $success = 0;
- }
- } else {
- Log3 ($hash, 1, $hash->{NAME} . ': ' . "no IODev assigned to '$hash->{NAME}'");
- $success = 0;
- }
-
- return $success;
+ my ($hash, $reg, @data) = @_;
+ my $success = 1;
+
+ if ($hash->{HiPi_used}) {
+ eval {
+ $hash->{devTSL2561}->bus_write($reg, join (' ',@data));
+ I2C_TSL2561_I2CRec($hash, {
+ direction => "i2cwrite",
+ i2caddress => $hash->{I2C_Address},
+ reg => $reg,
+ data => join (' ',@data),
+ });
+ };
+ Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;
+ } elsif (defined (my $iodev = $hash->{IODev})) {
+ eval {
+ CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
+ direction => "i2cwrite",
+ i2caddress => $hash->{I2C_Address},
+ reg => $reg,
+ data => join (' ',@data),
+ });
+ };
+ if ($hash->{$iodev->{NAME}.'_SENDSTAT'} eq 'error') {
+ readingsSingleUpdate($hash, 'state', STATE_I2C_ERROR, 1);
+ $success = 0;
+ }
+ } else {
+ Log3 ($hash, 1, $hash->{NAME} . ': ' . "no IODev assigned to '$hash->{NAME}'");
+ $success = 0;
+ }
+
+ return $success;
}
1;
@@ -1272,7 +1450,8 @@ sub I2C_TSL2561_i2cwrite($$$) {
Define
define TSL2561 I2C_TSL2561 [<I2C device>] <I2C address>
- <I2C device> must not be used if you connect via IODev. For HiPi it's mandatory.
+ <I2C device> mandatory for HiPi, must be omitted if you connect via IODev
+ <I2C address> may be 0x29, 0x39 or 0x49 (and 'AUTO' when using IODev to search for device at startup and after an I2C error)
Examples:
@@ -1284,40 +1463,91 @@ sub I2C_TSL2561_i2cwrite($$$) {
attr TSL2561 IODev I2CModule
attr TSL2561 poll_interval 5
+
+ define TSL2561 I2C_TSL2561 AUTO
+ attr TSL2561 IODev I2CModule
+ attr TSL2561 poll_interval 5
+
+ Set
+
+ get <name> update
+ Force immediate illumination measurement and restart a new poll_interval.
+ Note that the new readings are not yet available after set returns because the
+ measurement is performed asynchronously. Depending on the attributes integration time,
+ autoGain and autoIntegrationTime this may require more than one second to complete.
+
+
+
+ Readings
+
+ - luminosity
+ Good human eye reponse approximation of an illumination measurement in the range of 0.1 to 40000+ lux.
+ Rounded to 3 significant digits or one fractional digit.
+
+ - broadband
+ Broadband spectrum sensor sample.
+ Enable attribute normalizeRawValues for continuous readings independed of actual gain and integration time settings.
+
+ - ir
+ Infrared spectrum sensor sample.
+ Enable attribute normalizeRawValues for continuous readings independed of actual gain and integration time settings.
+
+ - gain
+ sensor gain used for current luminosity measurement (1 or 16)
+
+ - integrationTime
+ integration time in seconds used for current luminosity measurement
+
+ - state
+ Default: Initialized, valid values: Undefined, Defined, Initialized, Saturated, Disabled, I2C Error
+
+
+
+
Attributes
- IODev
- Set the name of a IODev module.
+ Set the name of an IODev module. If undefined the perl modules HiPi::Device::I2C are required.
Default: undefined
- if undefined the perl modules HiPi::Device::I2C are required
- poll_interval
Set the polling interval in minutes to query the sensor for new measured values.
Default: 5, valid values: 1, 2, 5, 10, 20, 30
- - integrationTime
- Set time in ms the sensor takes to measure the light.
- Default: 13, valid values: 13, 101, 402
- see this tutorial
- for more details
-
- gain
- Set gain factor.
+ Set gain factor. Attribute will be ignored if autoGain is enabled.
Default: 1, valid values: 1, 16
+ - integrationTime
+ Set time in ms the sensor takes to measure the light. Attribute will be ignored if autoIntegrationTime is enabled.
+ Default: 13, valid values: 13, 101, 402
+ See this tutorial
+ for more details.
+
- autoGain
- Enable auto gain.
- Default: 1, valid values: 0, 1
- if set to 1, the gain parameter is adjusted automatically depending on light conditions
+ Enable auto gain. If set to 1, the gain parameter is adjusted automatically depending on light conditions.
+ Default: 1, valid values: 0, 1
+
+ - autoIntegrationTime
+ Enable auto integration time. If set to 1, the integration time parameter is adjusted automatically depending on light conditions.
+ Default: 0, valid values: 0, 1
+
+ - normalizeRawValues
+ Scale the sensor raw values broadband and ir depending on actual gain and integrationTime to the equivalent of the settings for maximum sensitivity (gain=16 and integrationTime=403ms). This feature may be useful when autoGain or autoIntegrationTime is enabled to provide continuous values instead of jumping values when gain or integration time changes.
+ Default: 0, valid values: 0, 1
- floatArithmetics
Enable float arithmetics.
- Default: 1, valid values: 0, 1
- if set to 0, the luminosity is calculated using int arithmetics (for very low powered platforms)
- if set to 1, the luminosity is calculated using float arithmetics, yielding some additional precision
+ If set to 0, the luminosity is calculated using int arithmetics (for very low powered platforms).
+ If set to 1, the luminosity is calculated using float arithmetics, yielding some additional precision.
+ Default: 1, valid values: 0, 1
+
+ - disable
+ Disable I2C bus access.
+ Default: 0, valid values: 0, 1