mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-07 22:29:19 +00:00
51_I2C_BH1750.pm: added new module for BH1750 light sensor
git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@11549 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
49cda3fa90
commit
cd3aa45deb
681
FHEM/51_I2C_BH1750.pm
Normal file
681
FHEM/51_I2C_BH1750.pm
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
# $Id$
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# 51_I2C_BH1750.pm
|
||||||
|
#
|
||||||
|
# This file is part of FHEM.
|
||||||
|
#
|
||||||
|
# Fhem is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# BDKM is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with FHEM. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# Written by Arno Augustin
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use Time::HiRes qw(tv_interval);
|
||||||
|
|
||||||
|
# BH1750 chip constants
|
||||||
|
use constant
|
||||||
|
{
|
||||||
|
# I2C addresses
|
||||||
|
BH1750_ADDR_DEFAULT => 0x23,
|
||||||
|
BH1750_ADDR_OTHER => 0x5C,
|
||||||
|
|
||||||
|
# I2C registers
|
||||||
|
BH1750_RAW_VALUE => 0x00,
|
||||||
|
BH1750_POWER_DOWN => 0x00,
|
||||||
|
BH1750_POWER_ON => 0x01,
|
||||||
|
BH1750_RESET => 0x07,
|
||||||
|
BH1750_H_MODE => 0x10,
|
||||||
|
BH1750_H_MODE2 => 0x11,
|
||||||
|
BH1750_L_MODE => 0x13,
|
||||||
|
BH1750_H_MODE_ONCE => 0x20,
|
||||||
|
BH1750_H_MODE2_ONCE => 0x21,
|
||||||
|
BH1750_L_MODE_ONCE => 0x23,
|
||||||
|
|
||||||
|
BH1750_MT_MIN => 31,
|
||||||
|
BH1750_MT_DEFAULT => 69,
|
||||||
|
BH1750_MT_MAX => 254,
|
||||||
|
};
|
||||||
|
|
||||||
|
#state
|
||||||
|
use constant
|
||||||
|
{
|
||||||
|
BH1750_STATE_DEFINED => 'Defined',
|
||||||
|
BH1750_STATE_I2C_ERROR => 'I2C Error',
|
||||||
|
BH1750_STATE_SATURATED => 'Saturated',
|
||||||
|
BH1750_STATE_OK => 'Ok'
|
||||||
|
};
|
||||||
|
|
||||||
|
# PollState
|
||||||
|
use constant
|
||||||
|
{
|
||||||
|
BH1750_POLLSTATE_IDLE => 0,
|
||||||
|
BH1750_POLLSTATE_START_MEASURE => 1,
|
||||||
|
BH1750_POLLSTATE_PRE_LUX_WAIT => 2,
|
||||||
|
BH1750_POLLSTATE_PRE_LUX_DONE => 3,
|
||||||
|
BH1750_POLLSTATE_LUX_WAIT => 4,
|
||||||
|
BH1750_POLLSTATE_LUX_DONE => 5
|
||||||
|
};
|
||||||
|
|
||||||
|
# chip parameter selection for different LUX values
|
||||||
|
# BH1750 has the following limitations:
|
||||||
|
# MODE_L, MT= 31, LUX 0-121556, res. >8 LUX
|
||||||
|
# MODE_L, MT= 69, LUX 0- 54612, res. >4 LUX
|
||||||
|
# MODE2_H, MT= 31, LUX 0- 60778, res. >1 LUX
|
||||||
|
# MODE2_H, MT= 69, LUX 0- 27306, res. >0.5 LUX
|
||||||
|
# MODE2_H, MT=254, LUX 0- 7417, res. >0.11 LUX
|
||||||
|
|
||||||
|
my @I2C_BH1750_ranges=(
|
||||||
|
[ 5500,254, BH1750_H_MODE2_ONCE],
|
||||||
|
[ 11000,127, BH1750_H_MODE2_ONCE],
|
||||||
|
[ 22000, 69, BH1750_H_MODE2_ONCE],
|
||||||
|
[ 50000, 31, BH1750_H_MODE2_ONCE]
|
||||||
|
# else use 31, BH1750_L_MODE_ONCE
|
||||||
|
);
|
||||||
|
|
||||||
|
sub I2C_BH1750_Initialize($);
|
||||||
|
sub I2C_BH1750_Define($$);
|
||||||
|
sub I2C_BH1750_Attr(@);
|
||||||
|
sub I2C_BH1750_Poll($);
|
||||||
|
sub I2C_BH1750_Restart_Measure($);
|
||||||
|
sub I2C_BH1750_Set($@);
|
||||||
|
sub I2C_BH1750_Get($);
|
||||||
|
sub I2C_BH1750_Undef($$);
|
||||||
|
|
||||||
|
my $libcheck_hasHiPi = 1;
|
||||||
|
|
||||||
|
sub I2C_BH1750_Initialize($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
eval "use HiPi::Device::I2C;";
|
||||||
|
$libcheck_hasHiPi = 0 if($@);
|
||||||
|
|
||||||
|
$hash->{STATE} = "Init";
|
||||||
|
$hash->{DefFn} = "I2C_BH1750_Define";
|
||||||
|
$hash->{UndefFn} = "I2C_BH1750_Undef";
|
||||||
|
$hash->{InitFn} = "I2C_BH1750_Init";
|
||||||
|
$hash->{AttrFn} = "I2C_BH1750_Attr";
|
||||||
|
$hash->{SetFn} = "I2C_BH1750_Set";
|
||||||
|
$hash->{I2CRecFn} = 'I2C_BH1750_I2CRec';
|
||||||
|
$hash->{AttrList} = "poll_interval:1,2,5,10,20,30 IODev percentdelta ".
|
||||||
|
$readingFnAttributes;
|
||||||
|
$hash->{AttrList} .= " useHiPiLib:0,1 " if ($libcheck_hasHiPi);
|
||||||
|
$hash->{VERSION} = '$Id$';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_Define($$)
|
||||||
|
{
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
my @a = split(/\s+/, $def);
|
||||||
|
my $name = shift(@a);
|
||||||
|
my $device=0;
|
||||||
|
|
||||||
|
my $usage=sprintf "usage: \"define <devicename> I2C_BH1750 [devicename] [0x%x|0x%x]\"\n",
|
||||||
|
BH1750_ADDR_DEFAULT,BH1750_ADDR_OTHER;
|
||||||
|
|
||||||
|
$hash->{I2C_Address}=BH1750_ADDR_DEFAULT; # default
|
||||||
|
$hash->{HiPi_used} = 0;
|
||||||
|
|
||||||
|
shift(@a);
|
||||||
|
if(@a) {
|
||||||
|
$_=shift(@a);
|
||||||
|
if(m+^/dev/+) {
|
||||||
|
$device = $_;
|
||||||
|
if ($libcheck_hasHiPi) {
|
||||||
|
$hash->{HiPi_used} = 1;
|
||||||
|
} else {
|
||||||
|
return '$name error: HiPi library not installed';
|
||||||
|
}
|
||||||
|
if(@a) {
|
||||||
|
$_=shift(@a);
|
||||||
|
/0x(5c|23)/i or return $usage;
|
||||||
|
$hash->{I2C_Address}=hex($_);
|
||||||
|
}
|
||||||
|
} elsif (/0x(5c|23)/i) {
|
||||||
|
$hash->{I2C_Address}=hex($_);
|
||||||
|
} else {
|
||||||
|
return $usage;
|
||||||
|
}
|
||||||
|
@a and return $usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{BASEINTERVAL} = 0;
|
||||||
|
$hash->{DELTA} = 0;
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_IDLE;
|
||||||
|
|
||||||
|
readingsSingleUpdate($hash, 'state', BH1750_STATE_DEFINED, 1);
|
||||||
|
if ($main::init_done || $hash->{HiPi_used}) {
|
||||||
|
eval {
|
||||||
|
I2C_BH1750_Init($hash, [ $device ]);
|
||||||
|
};
|
||||||
|
Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_BH1750_Catch($@)) if $@;;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_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->{devBH1750} = HiPi::Device::I2C->new(
|
||||||
|
devicename => $dev,
|
||||||
|
address => $hash->{I2C_Address},
|
||||||
|
busmode => 'i2c',
|
||||||
|
);
|
||||||
|
Log3 $name, 3, "I2C_BH1750_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# start new measurement cycle
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
InternalTimer(gettimeofday() + 10, 'I2C_BH1750_Poll', $hash, 0);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_Catch($)
|
||||||
|
{
|
||||||
|
my $exception = shift;
|
||||||
|
if ($exception) {
|
||||||
|
$exception =~ /^(.*)( at.*FHEM.*)$/;
|
||||||
|
return $1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
sub I2C_BH1750_Attr (@)
|
||||||
|
{
|
||||||
|
my ($cmd,$name,$attr,$val) = @_;
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
my $error = "$name: ERROR attribute $attr ";
|
||||||
|
my $del = $cmd =~ /del/;
|
||||||
|
local $_;
|
||||||
|
|
||||||
|
Log3 $name, 3, "I2C_BH1750_Attr $cmd,$name,$attr,$val";
|
||||||
|
|
||||||
|
if ($attr eq "percentdelta") {
|
||||||
|
if($del) {
|
||||||
|
$val = 0; # default
|
||||||
|
} else {
|
||||||
|
$val =~ /^([0-9]+|[0-9]+\.?[0.9]+)$/ or return $error."needs numeric value";
|
||||||
|
}
|
||||||
|
$hash->{DELTA} = $val/100;
|
||||||
|
} elsif ($attr eq "poll_interval") {
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
if($del) {
|
||||||
|
$hash->{BASEINTERVAL} = 0;
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_IDLE;
|
||||||
|
} else {
|
||||||
|
if($val !~ /^\d+$/ or $val < 1) {
|
||||||
|
return $error."needs interger value";
|
||||||
|
} else {
|
||||||
|
$hash->{BASEINTERVAL} = 60*$val;
|
||||||
|
I2C_BH1750_Restart_Measure($hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_Restart_Measure($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_IDLE;
|
||||||
|
my $delay=$hash->{BASEINTERVAL};
|
||||||
|
I2C_BH1750_i2cwrite($hash, BH1750_POWER_DOWN);
|
||||||
|
$delay and InternalTimer(gettimeofday() + 10, 'I2C_BH1750_Poll', $hash, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub I2C_BH1750_Poll($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
Log3 $name, 4, "I2C_BH1750_Poll ".gettimeofday()." PollState=$hash->{PollState}";
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
my $delay=$hash->{BASEINTERVAL};
|
||||||
|
$hash->{PollState} == BH1750_POLLSTATE_IDLE and $hash->{PollState}++;
|
||||||
|
my $state = $hash->{PollState};
|
||||||
|
if ($state == BH1750_POLLSTATE_START_MEASURE) {
|
||||||
|
I2C_BH1750_i2cwrite($hash, BH1750_POWER_ON);
|
||||||
|
# check in lowest resolution first
|
||||||
|
$delay = I2C_BH1750_start_measure($hash,BH1750_MT_MIN,BH1750_L_MODE_ONCE);
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_PRE_LUX_WAIT;
|
||||||
|
} elsif($state == BH1750_POLLSTATE_PRE_LUX_WAIT) {
|
||||||
|
I2C_BH1750_request_measure($hash);
|
||||||
|
} elsif($state == BH1750_POLLSTATE_PRE_LUX_DONE) {
|
||||||
|
my $lux=$hash->{LUX};
|
||||||
|
my $i;
|
||||||
|
for($i=0; $i<@I2C_BH1750_ranges; $i++) {
|
||||||
|
$lux <= $I2C_BH1750_ranges[$i][0] and last;
|
||||||
|
}
|
||||||
|
if($i == @I2C_BH1750_ranges) {
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_LUX_DONE;
|
||||||
|
return I2C_BH1750_Poll($hash);
|
||||||
|
} else {
|
||||||
|
# do finer reading
|
||||||
|
my (undef,$mt,$mode)=@{$I2C_BH1750_ranges[$i]};
|
||||||
|
Log3 $name, 4, "I2C_BH1750_Poll using mt=$mt, mode=$mode";
|
||||||
|
$delay=I2C_BH1750_start_measure($hash,$mt,$mode);
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_LUX_WAIT;
|
||||||
|
}
|
||||||
|
} elsif($state == BH1750_POLLSTATE_LUX_WAIT) {
|
||||||
|
I2C_BH1750_request_measure($hash);
|
||||||
|
} elsif($state == BH1750_POLLSTATE_LUX_DONE) {
|
||||||
|
I2C_BH1750_update_lux($hash); # no finer resolution possible
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_IDLE;
|
||||||
|
I2C_BH1750_i2cwrite($hash, BH1750_POWER_DOWN);
|
||||||
|
} else {
|
||||||
|
Log3 $name, 1, "I2C_BH1750_Poll wrong state state=$state";
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$delay and InternalTimer(gettimeofday() + $delay, 'I2C_BH1750_Poll', $hash, 0);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_Set($@)
|
||||||
|
{
|
||||||
|
my ( $hash, @args ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $cmd = $args[1];
|
||||||
|
|
||||||
|
if($cmd eq "update") {
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
$hash->{PollState} = BH1750_POLLSTATE_START_MEASURE;
|
||||||
|
I2C_BH1750_Poll($hash);
|
||||||
|
} elsif($cmd eq "deleteminmax") {
|
||||||
|
delete($hash->{READINGS}{minimum});
|
||||||
|
delete($hash->{READINGS}{maximum});
|
||||||
|
} else {
|
||||||
|
return "Unknown argument ".$cmd.", choose one of update deleteminmax";
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_Undef($$)
|
||||||
|
{
|
||||||
|
my ($hash, $arg) = @_;
|
||||||
|
|
||||||
|
RemoveInternalTimer($hash);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub I2C_BH1750_i2cread($$$)
|
||||||
|
{
|
||||||
|
my ($hash, $reg, $nbyte) = @_;
|
||||||
|
my $success = 1;
|
||||||
|
|
||||||
|
Log3 $hash->{NAME}, 5, "I2C_BH1750_i2cread $reg,$nbyte";
|
||||||
|
|
||||||
|
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->{devBH1750}->bus_read($reg, $nbyte);
|
||||||
|
I2C_BH1750_I2CRec($hash, {
|
||||||
|
direction => "i2cread",
|
||||||
|
i2caddress => $hash->{I2C_Address},
|
||||||
|
reg => $reg,
|
||||||
|
nbyte => $nbyte,
|
||||||
|
received => join (' ',@values),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_BH1750_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 eq 'error') {
|
||||||
|
readingsSingleUpdate($hash, 'state', BH1750_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_BH1750_i2cwrite
|
||||||
|
{
|
||||||
|
my ($hash, $reg, @data) = @_;
|
||||||
|
my $success = 1;
|
||||||
|
|
||||||
|
Log3 $hash->{NAME}, 5, "I2C_BH1750_i2write $reg,@data";
|
||||||
|
if ($hash->{HiPi_used}) {
|
||||||
|
eval {
|
||||||
|
$hash->{devBH1750}->bus_write($reg, join (' ',@data));
|
||||||
|
I2C_BH1750_I2CRec($hash, {
|
||||||
|
direction => "i2cwrite",
|
||||||
|
i2caddress => $hash->{I2C_Address},
|
||||||
|
reg => $reg,
|
||||||
|
data => join (' ',@data),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_BH1750_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 eq 'error') {
|
||||||
|
readingsSingleUpdate($hash, 'state', BH1750_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_I2CRec ($$)
|
||||||
|
{
|
||||||
|
my ($hash, $clientmsg) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
Log3 $hash->{NAME}, 5, "I2C_BH1750_i2Rec";
|
||||||
|
my $pname = undef;
|
||||||
|
unless ($hash->{HiPi_used}) {
|
||||||
|
my $phash = $hash->{IODev};
|
||||||
|
$pname = $phash->{NAME};
|
||||||
|
while (my ( $k, $v ) = each %$clientmsg) {
|
||||||
|
$hash->{$k} = $v if $k =~ /^$pname/;
|
||||||
|
Log3 $hash->{NAME}, 5, "I2C_BH1750_i2Rec $k $v";
|
||||||
|
}
|
||||||
|
if($clientmsg->{$pname . "_SENDSTAT"} ne "Ok") {
|
||||||
|
Log3 $hash->{NAME}, 3, "I2C_BH1750_i2Rec bad sendstat: ".$clientmsg->{$pname."_SENDSTAT"};
|
||||||
|
if($clientmsg->{direction} eq "i2cread" or $clientmsg->{reg}) {
|
||||||
|
# avoid recoursion on power down
|
||||||
|
I2C_BH1750_Restart_Measure($hash);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received})) {
|
||||||
|
my $register = $clientmsg->{reg};
|
||||||
|
Log3 $hash, 4, "$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[0] << 8 | $raw[1];
|
||||||
|
}
|
||||||
|
if ($register == BH1750_RAW_VALUE) {
|
||||||
|
if($word == 0xFFFF) {
|
||||||
|
readingsSingleUpdate($hash, 'state', BH1750_STATE_SATURATED, 1);
|
||||||
|
Log3 $hash, 3, "$name sensor saturated ";
|
||||||
|
I2C_BH1750_Restart_Measure($hash);
|
||||||
|
} else {
|
||||||
|
readingsSingleUpdate($hash, 'state', BH1750_STATE_OK, 1);
|
||||||
|
my $lux = $word/1.2*(69/$hash->{MT_VAL})/$hash->{MODE};
|
||||||
|
$hash->{LUX}=$lux;
|
||||||
|
Log3 $hash->{NAME}, 4, "I2C_BH1750_I2CRec: lux=$lux";
|
||||||
|
if($hash->{PollState} == BH1750_POLLSTATE_PRE_LUX_WAIT ||
|
||||||
|
$hash->{PollState} == BH1750_POLLSTATE_LUX_WAIT) {
|
||||||
|
$hash->{PollState}++;
|
||||||
|
I2C_BH1750_Poll($hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_update_lux
|
||||||
|
{
|
||||||
|
my($hash)=@_;
|
||||||
|
my $name=$hash->{NAME};
|
||||||
|
my $lux=$hash->{LUX};
|
||||||
|
my $delta=$hash->{DELTA};
|
||||||
|
|
||||||
|
if($delta) { # update only if delta large enough
|
||||||
|
my $lastlux=ReadingsNum($name,"luminosity",1000000);
|
||||||
|
$lux == $lastlux and return; # no delta, no update
|
||||||
|
|
||||||
|
# check if we have too less delta and return
|
||||||
|
if($lastlux > $lux) {
|
||||||
|
($lastlux-$lastlux*$delta < $lux) and return;
|
||||||
|
} else {
|
||||||
|
($lastlux+$lastlux*$delta > $lux) and return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# round value
|
||||||
|
if($lux < 100) {
|
||||||
|
$lux = int($lux*10+0.5)/10;
|
||||||
|
} elsif($lux < 1000) {
|
||||||
|
$lux=int($lux+0.5);
|
||||||
|
} elsif($lux < 10000) {
|
||||||
|
$lux=int($lux/10+0.5); $lux *= 10;
|
||||||
|
} elsif($lux < 100000) {
|
||||||
|
$lux=int($lux/100+0.5); $lux *= 100;
|
||||||
|
} else {
|
||||||
|
$lux=int($lux/1000+0.5); $lux *= 1000;
|
||||||
|
}
|
||||||
|
Log3 $hash->{NAME}, 4, "I2C_BH1750_update_lux: luminosity=$lux";
|
||||||
|
readingsBeginUpdate($hash);
|
||||||
|
readingsBulkUpdate($hash, "luminosity", $lux);
|
||||||
|
$lux < ReadingsNum($name,"minimum", 1000000) and readingsBulkUpdate($hash, "minimum", $lux);
|
||||||
|
$lux > ReadingsNum($name,"maximum",-1000000) and readingsBulkUpdate($hash, "maximum", $lux);
|
||||||
|
readingsEndUpdate($hash, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_request_measure
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
I2C_BH1750_i2cread($hash,BH1750_RAW_VALUE,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub I2C_BH1750_start_measure
|
||||||
|
{
|
||||||
|
my ($hash,$mt,$mode)=@_;
|
||||||
|
$hash->{MT_VAL} = $mt;
|
||||||
|
$hash->{MODE} = ($mode == BH1750_H_MODE2 || $mode == BH1750_H_MODE2_ONCE) ? 2 : 1;
|
||||||
|
my $hi=($mt>>5) | 0x40;
|
||||||
|
my $lo=($mt&0x1F) | 0x60;
|
||||||
|
I2C_BH1750_i2cwrite($hash,BH1750_RESET);
|
||||||
|
I2C_BH1750_i2cwrite($hash,$hi); # set MT value
|
||||||
|
I2C_BH1750_i2cwrite($hash,$lo);
|
||||||
|
I2C_BH1750_i2cwrite($hash,$mode);
|
||||||
|
my $mindelay = ($mode == BH1750_L_MODE || $mode == BH1750_L_MODE_ONCE) ? 24 : 180;
|
||||||
|
$mindelay = $mindelay * ($mt/BH1750_MT_DEFAULT);
|
||||||
|
Log3 $hash->{NAME}, 5, "I2C_BH1750_start_measure: duration ".int($mindelay)."ms";
|
||||||
|
$mindelay /=1000; # seconds
|
||||||
|
|
||||||
|
return $mindelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub I2C_BH1750_sleep
|
||||||
|
{
|
||||||
|
select(undef, undef, undef, $_[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=pod
|
||||||
|
=begin html
|
||||||
|
|
||||||
|
<a name="I2C_BH1750"></a>
|
||||||
|
<h3>I2C_BH1750</h3>
|
||||||
|
<ul>
|
||||||
|
<a name="I2C_BH1750"></a>
|
||||||
|
<p>
|
||||||
|
Module for the I<sup>2</sup>C BH1750 light sensor.
|
||||||
|
|
||||||
|
The BH1750 sensor supports a luminous flux from 0 up to 120k lx
|
||||||
|
and a resolution up to 0.11lx.
|
||||||
|
It supports different modes to be able to cover this large range of
|
||||||
|
data. <br>
|
||||||
|
The I2C_BH1750 module tries always to get
|
||||||
|
the luminosity data from the sensor as good as possible. To achieve
|
||||||
|
this the module first reads flux data in the least sensitive mode and then
|
||||||
|
decides which mode to take to get best results.
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
For the I<sup>2</sup>C bus the same things are valid as described in the
|
||||||
|
<a href="#I2C_TSL2561">I2C_TSL2561</a>
|
||||||
|
module.<br>
|
||||||
|
|
||||||
|
<b>Define</b>
|
||||||
|
<ul>
|
||||||
|
<code>define BH1750 I2C_BH1750 [<I2C device>] [I2C address]</code><br><br>
|
||||||
|
<I2C device> mandatory for HiPi, must be omitted if you connect
|
||||||
|
via IODev<br>
|
||||||
|
I2C address must be 0x23 or 0x5C (if omitted default address 0x23 is used)
|
||||||
|
<br>
|
||||||
|
Examples:
|
||||||
|
<pre>
|
||||||
|
# Use HiPi with i2c address 0x5C:
|
||||||
|
define BH1750 I2C_BH1750 /dev/i2c-0 0x5C
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
# Use IODev I2C_2 with default i2c address 0x23
|
||||||
|
# set poll interval to 1 min
|
||||||
|
# generate luminosity value only if difference to last value is at least 10%
|
||||||
|
define BH1750 I2C_BH1750
|
||||||
|
attr BH1750 IODev I2C_2
|
||||||
|
attr BH1750 poll_interval 1
|
||||||
|
attr BH1750 percentdelta 10
|
||||||
|
</pre>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<b>Set</b>
|
||||||
|
<ul>
|
||||||
|
<li><code>set <device name> update</code><br>
|
||||||
|
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 flux value
|
||||||
|
this may require more than one second to complete.<br>
|
||||||
|
</li>
|
||||||
|
<li><code>set <device name> deleteminmax</code><br>
|
||||||
|
Delete the minimum maximum readings to start new
|
||||||
|
min/max measurement
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<b>Readings</b>
|
||||||
|
<ul>
|
||||||
|
<li>luminosity<br>
|
||||||
|
Illumination measurement in the range of 0 to 121557 lx.<br>
|
||||||
|
The generated luminosity value is stored with up to one
|
||||||
|
fractional digit for values below 100 and
|
||||||
|
rounded to 3 significant digits for all other values.
|
||||||
|
Compared with the accuracy of the sensor it makes no
|
||||||
|
sense to store the values with more precision.
|
||||||
|
</li>
|
||||||
|
<li>minimum<br>
|
||||||
|
minimum of measured luminosity
|
||||||
|
</li>
|
||||||
|
<li>maximum<br>
|
||||||
|
maximum of measured luminosity
|
||||||
|
</li>
|
||||||
|
<li>state<br>
|
||||||
|
Default: Defined, Ok, Saturated, I2C Error
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
<a name="I2C_BH1750attr"></a>
|
||||||
|
<b>Attributes</b>
|
||||||
|
<ul>
|
||||||
|
<li>IODev<br>
|
||||||
|
Set the name of an IODev module. If undefined the perl modules HiPi::Device::I2C are required.<br>
|
||||||
|
Default: undefined<br>
|
||||||
|
</li>
|
||||||
|
<li>poll_interval<br>
|
||||||
|
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.<br>
|
||||||
|
valid values: 1, 2, 5, 10, 20, 30<br>
|
||||||
|
</li>
|
||||||
|
<li>percentdelta<br>
|
||||||
|
If set a luminosity reading is only generated if
|
||||||
|
the difference between the current luminosity value and the last reading is
|
||||||
|
at least percentdelta percents.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
<br>
|
||||||
|
</ul>
|
||||||
|
=end html
|
||||||
|
|
||||||
|
=cut
|
Loading…
x
Reference in New Issue
Block a user