I2C_TSL2561 [devicename] address';
} elsif (@a == 3) {
$address = lc($a[2]);
} else {
$device = $a[2];
$address = lc($a[3]);
if ($libcheck_hasHiPi) {
$hash->{HiPi_used} = 1;
delete $validAdresses{'auto'};
} else {
$msg = '$name error: HiPi library not installed';
}
}
if ($msg) {
Log3 ($hash, 1, $msg);
return $msg;
}
$address = $validAdresses{$address};
if (!defined($address)) {
$msg = "Wrong address, must be one of " . join(' ', keys %validAdresses);
Log3 ($hash, 1, $msg);
return $msg;
}
if ($address eq TSL2561_ADDR_AUTO) {
# start with lowest address in auto mode
$hash->{autoAddress} = 1;
$address = TSL2561_ADDR_LOW;
} else {
$hash->{autoAddress} = 0;
}
if ($hash->{HiPi_used}) {
$hash->{autoAddress} = 0;
}
$hash->{I2C_Address} = hex($address);
# 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} = TSL2561_ACQUI_STATE_IDLE;
}
if (!defined($hash->{calcState})) {
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
}
if (!defined($hash->{operationCounter})) {
$hash->{operationCounter} = 0;
}
if (!defined($hash->{blockingIO})) {
$hash->{blockingIO} = 0;
}
readingsSingleUpdate($hash, 'state', TSL2561_STATE_DEFINED, 1);
if ($main::init_done || $hash->{HiPi_used}) {
eval {
I2C_TSL2561_Init($hash, [ $device ]);
};
Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_TSL2561_Catch($@)) if $@;;
}
Log3 $name, 5, "I2C_TSL2561_Define end";
return undef;
}
sub I2C_TSL2561_Init($$) {
my ($hash, $dev) = @_;
my $name = $hash->{NAME};
if ($hash->{HiPi_used}) {
# check for existing i2c device
my $i2cModulesLoaded = 0;
$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);
}
# clear package identification to force device reinitialization (device may have been powered off)
$hash->{tsl2561Package} = undef;
# start new measurement cycle
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 10, 'I2C_TSL2561_Poll', $hash, 0);
return undef;
}
sub I2C_TSL2561_Catch($) {
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
=cut
sub I2C_TSL2561_Attr (@) {
my ($cmd, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
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) {
# start new measurement cycle
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;
$hash->{timingModified} = 1;
} elsif ($attr eq 'autoIntegrationTime') {
my $autoIntegrationTime = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
$hash->{timingModified} = 1;
} 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
=cut
sub I2C_TSL2561_Poll($) {
my ($hash) = @_;
my $name = $hash->{NAME};
RemoveInternalTimer($hash);
Log3 $name, 5, "I2C_TSL2561_Poll: start";
my $pollDelay = 60*AttrVal($hash->{NAME}, 'poll_interval', 0); # seconds polling
if (!AttrVal($hash->{NAME}, "disable", 0)) {
# Request new samples from TSL2561 and calculate luminosity
my $lux = I2C_TSL2561_GetLuminosity($hash);
if ($hash->{calcState} == TSL2561_CALC_STATE_DATA_REQUESTED) {
# Measurement in progress
if ($hash->{acquiState} == TSL2561_ACQUI_STATE_ENABLED) {
$pollDelay = I2C_TSL2561_GetIntegrationTime($hash) + 0.003; # seconds measurement time
if (!$hash->{blockingIO}) {
$pollDelay += 0.200; # extra time for async transport jitter compensation
}
} else {
$pollDelay = 0.400; # seconds async I2C read reply timeout
}
} else {
# Measurement completed
if ($hash->{calcState} == TSL2561_CALC_STATE_COMPLETED) {
# success, update readings based on new data
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);
}
my $state = ReadingsVal($name, 'state', '');
if ($state ne TSL2561_STATE_SATURATED && $hash->{saturated}) {
readingsBulkUpdate($hash, 'state', TSL2561_STATE_SATURATED, 1);
} elsif ($state ne TSL2561_STATE_INITIALIZED && !$hash->{saturated}) {
readingsBulkUpdate($hash, 'state', TSL2561_STATE_INITIALIZED, 1);
}
readingsEndUpdate($hash, 1);
}
# backup required operations (for diagnostics)
$hash->{requiredOperations} = $hash->{operationCounter};
# Reset state
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
$hash->{operationCounter} = 0;
}
} else {
readingsSingleUpdate($hash, 'state', TSL2561_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);
}
return undef;
}
sub I2C_TSL2561_Set($@) {
my ( $hash, @args ) = @_;
my $name = $hash->{NAME};
my $cmd = $args[1];
if(!defined($sets{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
}
I2C_TSL2561_Poll($hash);
return undef;
}
sub I2C_TSL2561_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_I2CRcvControl: is enabled";
$hash->{acquiState} = TSL2561_ACQUI_STATE_ENABLED;
$hash->{acquiStarted} = [gettimeofday];
} else {
Log3 $name, 5, "I2C_TSL2561_I2CRcvControl: is disabled";
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
}
if (!$hash->{blockingIO}) {
I2C_TSL2561_Poll($hash);
}
return undef;
}
#
# 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 );
Log3 $name, 5, 'I2C_TSL2561_I2CRcvID: sensorId ' . $hash->{sensorType};
# init state
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
readingsSingleUpdate($hash, 'state', TSL2561_STATE_INITIALIZED, 1);
# force preset of integration time and gain (device may have been powered off)
$hash->{timingModified} = 1;
# I2C-API blocking/non-blocking detection
$hash->{blockingIO} = $hash->{operationInProgress};
if (!$hash->{blockingIO}) {
I2C_TSL2561_Poll($hash);
}
return undef;
}
#
# process received timing register
#
sub I2C_TSL2561_I2CRcvTiming ($$) {
my ($hash, $timing) = @_;
my $name = $hash->{NAME};
$hash->{tsl2561IntegrationTime} = $timing & 0x03;
$hash->{tsl2561Gain} = $timing & 0x10;
Log3 $name, 4, "I2C_TSL2561_I2CRcvTiming: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
if (!$hash->{blockingIO}) {
I2C_TSL2561_Poll($hash);
}
return undef;
}
#
# process received ADC channel 0 register
#
sub I2C_TSL2561_I2CRcvChan0 ($$) {
my ($hash, $broadband) = @_;
my $name = $hash->{NAME};
$hash->{broadband} = $broadband;
Log3 $name, 4, 'I2C_TSL2561_I2CRcvChan0 ' . $broadband;
$hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_CH0_RECEIVED;
return undef;
}
#
# process received ADC channel 1 register
#
sub I2C_TSL2561_I2CRcvChan1 ($$) {
my ($hash, $ir) = @_;
my $name = $hash->{NAME};
$hash->{ir} = $ir;
Log3 $name, 4, 'I2C_TSL2561_I2CRcvChan1 ' . $ir;
$hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_CH1_RECEIVED;
if (!$hash->{blockingIO}) {
I2C_TSL2561_Poll($hash);
}
return undef;
}
#
# 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";
}
}
}
return undef;
}
=head2 I2C_TSL2561_Enable
Title: I2C_TSL2561_Enable
Function: Enables the device
Returns: 1 if enabling sensor was initiated, 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 ';
my $success = 0;
if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, TSL2561_CONTROL_POWERON)) {
$success = I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_CONTROL, 1);
}
Log3 $name, 5, 'I2C_TSL2561_Enable: end ';
return $success;
}
=head2 I2C_TSL2561_Disable
Title: I2C_TSL2561_Disable
Function: Disables the device
Returns: 1 if disabling sensor was initiated, 0 if disabling sensor failed
Args: named arguments:
- argument1 => hash: $hash hash of device
=cut
sub I2C_TSL2561_Disable($) {
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);
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
=cut
sub I2C_TSL2561_GetData($) {
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} == TSL2561_ACQUI_STATE_ERROR) {
$success = 0;
last; # Abort, Start again at next slow poll
} elsif ($operations > 10) {
# Too many consecutive operations, abort
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
Log3 $name, 5, "I2C_TSL2561_GetData: state machine stuck, aborting";
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_IDLE) {
if (!defined($hash->{tsl2561Package})) {
# Choose an address to scan the I2C bus for device in auto address mode
if ($hash->{autoAddress}) {
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);
}
}
# Detect TLS2561 package type and init integration time and gain
Log3 $name, 5, "I2C_TSL2561_GetData: request device id";
$hash->{acquiState} = TSL2561_ACQUI_STATE_SETUP;
if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_ID, 1)) {
last; # Wait for id confirmation, check again after next fast poll
} else {
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
} elsif ($hash->{timingModified}) {
$hash->{acquiState} = TSL2561_ACQUI_STATE_SETUP;
if (I2C_TSL2561_SetTimingRegister($hash)) {
last; # Wait new timing to be confirmed, check again after next fast poll
} else {
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
} else {
# Enable the device
$hash->{acquiState} = TSL2561_ACQUI_STATE_ENABLE_REQUESTED;
if (I2C_TSL2561_Enable($hash)) {
last; # Wait for enable confirmation, check again after next fast poll
} else {
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
}
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_SETUP) {
last; # Wait for setup confirmation, check again after next fast poll
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_ENABLE_REQUESTED) {
last; # Wait for enable confirmation, check again after next fast poll
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_ENABLED) {
# Wait x ms for ADC to complete
my $now = [gettimeofday];
if (tv_interval($hash->{acquiStarted}, $now) >= I2C_TSL2561_GetIntegrationTime($hash)) {
$hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_AVAILABLE;
} else {
last; # Wait for measurement to complete, check again after next fast poll
}
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_DATA_AVAILABLE) {
# Read a two byte value from channel 0 and channel 1 (visible + infrared)
$hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_REQUESTED;
if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN0_LOW, 2)) {
if (!I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_WORD_BIT | TSL2561_REGISTER_CHAN1_LOW, 2)) {
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
} else {
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_DATA_REQUESTED) {
last; # Wait for channel 0 or channel 1 data to be read
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_DATA_CH0_RECEIVED) {
# Read a two byte value from channel 1 (infrared)
$hash->{acquiState} = TSL2561_ACQUI_STATE_DATA_REQUESTED;
last; # Wait for channel 1 data to be read
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_DATA_CH1_RECEIVED) {
$hash->{calcState} = TSL2561_CALC_STATE_DATA_RECEIVED;
# Try to turn the device off to save power
I2C_TSL2561_Disable($hash);
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
last; # Done, start again at next slow poll
} else {
# Undefined state
$hash->{acquiState} = TSL2561_ACQUI_STATE_ERROR;
}
}
return $success;
}
#
# write integration time and gain to device
#
sub I2C_TSL2561_SetTimingRegister($) {
my ($hash) = @_;
my $name = $hash->{NAME};
my $success = 0;
if (!AttrVal($hash->{NAME}, "disable", 0) && defined($hash->{tsl2561Package})) {
# Update the timing register
my $autoGain = AttrVal($name, 'autoGain', 1);
if (!$autoGain) {
my $attrVal = AttrVal($name, 'gain', 1);
$hash->{tsl2561Gain} = $attrVal == 16? TSL2561_GAIN_16X : TSL2561_GAIN_1X;
}
my $autoIntegrationTime = AttrVal($name, 'autoIntegrationTime', 0);
if (!$autoIntegrationTime) {
my $attrVal = AttrVal($name, 'integrationTime', 13);
$hash->{tsl2561IntegrationTime} = $attrVal == 402? TSL2561_INTEGRATIONTIME_402MS : $attrVal == 101? TSL2561_INTEGRATIONTIME_101MS : TSL2561_INTEGRATIONTIME_13MS;
}
Log3 $name, 4, "I2C_TSL2561_SetTimingRegister: time $hash->{tsl2561IntegrationTime}, gain $hash->{tsl2561Gain}";
if (I2C_TSL2561_i2cwrite($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, $hash->{tsl2561IntegrationTime} | $hash->{tsl2561Gain})) {
if (I2C_TSL2561_i2cread($hash, TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, 1)) {
$success = 1;
}
}
}
$hash->{timingModified} = 0;
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
=cut
sub I2C_TSL2561_SetIntegrationTime($$) {
my ($hash, $time) = @_;
my $name = $hash->{NAME};
# store the value even if $hash->{tsl2561Package} is not set (yet). That happens
# during fhem startup.
if (defined($hash->{tsl2561IntegrationTime}) && $hash->{tsl2561IntegrationTime} != $time) {
Log3 $name, 4, "I2C_TSL2561_SetIntegrationTime: $hash->{tsl2561IntegrationTime} -> $time";
}
$hash->{tsl2561IntegrationTime} = $time;
$hash->{timingModified} = 1;
return undef;
}
#
# 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
=cut
sub I2C_TSL2561_SetGain($$) {
my ($hash, $gain) = @_;
my $name = $hash->{NAME};
# store the value even if $hash->{tsl2561Package} is not set (yet). That happens
# during fhem startup.
if (defined($hash->{tsl2561Gain}) && $hash->{tsl2561Gain} != $gain) {
Log3 $name, 4, "I2C_TSL2561_SetGain: $hash->{tsl2561Gain} -> $gain";
}
$hash->{tsl2561Gain} = $gain;
$hash->{timingModified} = 1;
return undef;
}
#
# 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 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};
# Log3 $name, 5, "I2C_TSL2561_GetLuminosity: start";
$hash->{operationInProgress} = 1;
# Luminosity calculation state machine
my $lux = undef;
while(1) {
$hash->{operationCounter}++;
Log3 $name, 5, "I2C_TSL2561_GetLuminosity: calc state $hash->{calcState} acqui state $hash->{acquiState}";
if ($hash->{calcState} == TSL2561_CALC_STATE_ERROR) {
Log3 $name, 5, "I2C_TSL2561_GetLuminosity: error, aborting";
# Try to turn the device off to save power
I2C_TSL2561_Disable($hash);
# Reset package to force device reinitialization
$hash->{tsl2561Package} = undef;
# Claim I2C error
readingsSingleUpdate($hash, 'state', TSL2561_STATE_I2C_ERROR, 1);
last; # Abort, start again at next slow poll
} elsif ($hash->{operationCounter} > TSL2561_MAX_CONSECUTIVE_OPERATIONS) {
# Too many consecutive operations, abort
$hash->{calcState} = TSL2561_CALC_STATE_ERROR;
Log3 $name, 5, "I2C_TSL2561_GetLuminosity: state machine stuck, aborting";
} elsif ($hash->{calcState} == TSL2561_CALC_STATE_IDLE) {
# Enable device and request data
Log3 $name, 5, "I2C_TSL2561_GetLuminosity: starting new measurement";
if (I2C_TSL2561_GetData($hash)) {
$hash->{calcState} = TSL2561_CALC_STATE_DATA_REQUESTED;
} else {
$hash->{calcState} = TSL2561_CALC_STATE_ERROR;
}
} elsif ($hash->{calcState} == TSL2561_CALC_STATE_DATA_REQUESTED) {
# Wait for device
if (I2C_TSL2561_GetData($hash)) {
if ($hash->{acquiState} == TSL2561_ACQUI_STATE_SETUP) {
last; # Wait for setup confirmation, check again after next fast poll
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_ENABLE_REQUESTED) {
last; # Wait for enable to be confirmed, check again at next fast poll
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_ENABLED) {
last; # Wait for measurement to complete, check again at next fast poll
} elsif ($hash->{acquiState} == TSL2561_ACQUI_STATE_DATA_REQUESTED) {
last; # Wait for data to be read, check again at next fast poll
}
} else {
$hash->{calcState} = TSL2561_CALC_STATE_ERROR;
}
} elsif ($hash->{calcState} == TSL2561_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);
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
next;
} elsif (($hash->{broadband} > $hi) && ($hash->{tsl2561Gain} == TSL2561_GAIN_16X)) {
# Drop gain and try again
I2C_TSL2561_SetGain($hash, TSL2561_GAIN_1X);
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
next;
} else {
# Reading is either valid, or we're already at the chips limits
}
} else {
# Auto gain disabled, always valid
}
# 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);
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
$hash->{acquiState} = TSL2561_ACQUI_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 and try again
I2C_TSL2561_SetIntegrationTime($hash, TSL2561_INTEGRATIONTIME_402MS);
$hash->{calcState} = TSL2561_CALC_STATE_IDLE;
$hash->{acquiState} = TSL2561_ACQUI_STATE_IDLE;
next;
} else {
# Readings are not saturated or auto integration time is disabled
$hash->{saturated} = 0;
}
# Received data is valid, calculate luminosity
$lux = I2C_TSL2561_CalculateLux($hash);
$hash->{calcState} = TSL2561_CALC_STATE_COMPLETED;
last; # Done, start again at next slow poll
} else {
# Undefined state
$hash->{calcState} = TSL2561_CALC_STATE_ERROR;
}
}
$hash->{operationInProgress} = 0;
# Log3 $name, 5, "I2C_TSL2561_GetLuminosity: end";
return $lux;
}
#
# get channel scale
#
sub I2C_TSL2561_GetChannelScale($) {
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;
}
# 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 << 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
=cut
sub I2C_TSL2561_CalculateLux($) {
my ($hash) = @_;
my $name = $hash->{NAME};
# 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};
# 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*$channel0*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*$channel0*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;
# Find the ratio of the channel values (Channel1/Channel0)
my $ratio1 = 0;
if ($channel0 != 0) {
$ratio1 = ($channel1 << (TSL2561_LUX_RATIOSCALE+1)) / $channel0;
}
# round the ratio value
my $ratio = ($ratio1 + 1) >> 1;
my $b=0;
my $m=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;
}
}
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;
}
}
=head2 I2C_TSL2561_i2cread
Title: I2C_TSL2561_i2cread
Function: implements I2C read operation abstraction
Returns: 1 on success, 0 on error
Args: - argument1 => hash
- argument2 => I2C register
- argument3 => number of bytes to read
=cut
sub I2C_TSL2561_i2cread($$$) {
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
});
};
my $sendStat = $hash->{$iodev->{NAME}.'_SENDSTAT'};
if (defined($sendStat) && $sendStat ne 'Ok') {
readingsSingleUpdate($hash, 'state', TSL2561_STATE_I2C_ERROR, 1);
Log3 ($hash, 5, $hash->{NAME} . ": i2cread on $iodev->{NAME} failed");
$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})) {
eval {
CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
direction => "i2cwrite",
i2caddress => $hash->{I2C_Address},
reg => $reg,
data => join (' ',@data),
});
};
my $sendStat = $hash->{$iodev->{NAME}.'_SENDSTAT'};
if (defined($sendStat) && $sendStat ne 'Ok') {
readingsSingleUpdate($hash, 'state', TSL2561_STATE_I2C_ERROR, 1);
Log3 ($hash, 5, $hash->{NAME} . ": i2cwrite on $iodev->{NAME} failed");
$success = 0;
}
} else {
Log3 ($hash, 1, $hash->{NAME} . ': ' . "no IODev assigned to '$hash->{NAME}'");
$success = 0;
}
return $success;
}
1;
=pod
=item summary TSL2561 luminosity sensor
=item summary_DE TSL2561 Helligkeitssensor
=begin html
I2C_TSL2561
With this module you can read values from the ambient light sensor TSL2561
via the i2c bus on Raspberry Pi.
The luminosity value returned is a good human eye reponse approximation of an
illumination measurement in the range of 0.1 to 40000+ lux (but not a replacement for a
precision measurement, relation between measured value and true value may vary by 40%).
There are two possibilities connecting to I2C bus:
- via IODev module
The I2C messages are send through an I2C interface module like RPII2C, FRM
or NetzerI2C so this device must be defined first.
attribute IODev must be set
- via HiPi library
Add these two lines to your /etc/modules file to load the I2C relevant kernel modules
automaticly during booting your Raspberry Pi.
i2c-bcm2708
i2c-dev
Install HiPi perl modules:
wget http://raspberry.znix.com/hipifiles/hipi-install perl hipi-install
To change the permissions of the I2C device create file:
/etc/udev/rules.d/98_i2c.rules
with this content:
SUBSYSTEM=="i2c-dev", MODE="0666"
Reboot
To use the sensor on the second I2C bus at P5 connector
(only for version 2 of Raspberry Pi) you must add the bold
line of following code to your FHEM start script:
case "$1" in
'start')
sudo hipi-i2c e 0 1
...
Define
define TSL2561 I2C_TSL2561 [<I2C device>] <I2C address>
<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:
define TSL2561 I2C_TSL2561 /dev/i2c-0 0x39
attr TSL2561 poll_interval 5
define TSL2561 I2C_TSL2561 0x39
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 an IODev module. If undefined the perl modules HiPi::Device::I2C are required.
Default: undefined
- poll_interval
Set the polling interval in minutes to query the sensor for new measured values.
By changing this attribute a new illumination measurement will be triggered.
Default: 5, valid values: 1, 2, 5, 10, 20, 30
- gain
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. 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.
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
Notes
- Because the measurement may take several 100 milliseconds a measurement cycle will be executed asynchronously, so
do not expect to have new values immediately available after "set update" returns. If autoGain or autoIntegrationTime
are enabled, more than one measurement cycle will be required if light conditions change.
- With HiPi and especially IODev there are several I2C interfaces available, some blocking, some non-blocking and
some with different physical layers. The module has no knowledge of the specific properties of an interface and
therefore module operation and timing may not be exactly the same with each interface type.
- If AUTO is used as device address, one address per measurement cycle will be tested. Depending on your device address
it may be necessary to execute "set update" several times to find your device.
- When using Firmata the I2C write/read delay attribute "i2c-config" of the FRM module can be set to any value.
=end html
=cut