I2C_BMP180 devicename';
}
# create default attributes
$msg = CommandAttr(undef, $name . ' poll_interval 5');
$msg = CommandAttr(undef, $name . ' oversampling_settings 3');
if ($msg) {
Log (1, $msg);
return $msg;
}
# check for existing i2c device
my $i2cModulesLoaded = 0;
$i2cModulesLoaded = 1 if -e $dev;
if ($i2cModulesLoaded) {
$hash->{devBPM180} = HiPi::Device::I2C->new(
devicename => $dev,
address => hex(BMP180_I2C_ADDRESS),
busmode => 'i2c',
);
# read calibration data from sensor
$hash->{calibrationData}{ac1} = I2C_BMP180_ReadInt($hash, 0xAA);
if ( defined($hash->{calibrationData}{ac1}) ) {
$hash->{calibrationData}{ac2} = I2C_BMP180_ReadInt($hash, 0xAC);
$hash->{calibrationData}{ac3} = I2C_BMP180_ReadInt($hash, 0xAE);
$hash->{calibrationData}{ac4} = I2C_BMP180_ReadInt($hash, 0xB0, 0);
$hash->{calibrationData}{ac5} = I2C_BMP180_ReadInt($hash, 0xB2, 0);
$hash->{calibrationData}{ac6} = I2C_BMP180_ReadInt($hash, 0xB4, 0);
$hash->{calibrationData}{b1} = I2C_BMP180_ReadInt($hash, 0xB6);
$hash->{calibrationData}{b2} = I2C_BMP180_ReadInt($hash, 0xB8);
$hash->{calibrationData}{mb} = I2C_BMP180_ReadInt($hash, 0xBA);
$hash->{calibrationData}{mc} = I2C_BMP180_ReadInt($hash, 0xBC);
$hash->{calibrationData}{md} = I2C_BMP180_ReadInt($hash, 0xBE);
} else {
return $name . ': Error! I2C failure: Please check your i2c bus ' . $dev . ' and the connected device address: ' . BMP180_I2C_ADDRESS;
}
$hash->{STATE} = 'Initialized';
} else {
return $name . ': Error! I2C device not found: ' . $dev . '. Please check kernelmodules must loaded: i2c_bcm2708, i2c_dev';
}
return undef;
}
=head2 I2C_BMP180_Attr
Title: I2C_BMP180_Attr
Function: Implements AttrFn function.
Returns: string|undef
Args: named arguments:
-argument1 => array
=cut
sub I2C_BMP180_Attr (@) {
my (undef, $name, $attr, $val) = @_;
my $hash = $defs{$name};
my $msg = '';
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_BMP180_Poll', $hash, 0);
} else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
}
}
return ($msg) ? $msg : undef;
}
=head2 I2C_BMP180_Poll
Title: I2C_BMP180_Poll
Function: Start polling the sensor at interval defined in attribute
Returns: -
Args: named arguments:
-argument1 => hash
=cut
sub I2C_BMP180_Poll($) {
my ($hash) = @_;
my $name = $hash->{NAME};
# Read values
I2C_BMP180_Set($hash, ($name, 'readValues'));
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BMP180_Poll', $hash, 0);
}
}
=head2 I2C_BMP180_Set
Title: I2C_BMP180_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_BMP180_Set($@) {
my ($hash, @a) = @_;
my $name =$a[0];
my $cmd = $a[1];
if(!defined($sets{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
}
if ($cmd eq 'readValues') {
my $overSamplingSettings = AttrVal($hash->{NAME}, 'oversampling_settings', 3);
# query sensor
my $ut = I2C_BMP180_readUncompensatedTemperature($hash);
my $up = I2C_BMP180_readUncompensatedPressure($hash, $overSamplingSettings);
my $temperature = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
I2C_BMP180_calcTrueTemperature($hash, $ut) / 10
);
my $pressure = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
I2C_BMP180_calcTruePressure($hash, $up, $overSamplingSettings) / 100
);
my $altitude = AttrVal('global', 'altitude', 0);
# simple barometric height formula
my $pressureNN = sprintf(
'%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
$pressure + ($altitude / 8.5)
);
readingsBeginUpdate($hash);
readingsBulkUpdate(
$hash,
'state',
'T: ' . $temperature . ' P: ' . $pressure . ' P-NN: ' . $pressureNN
);
readingsBulkUpdate($hash, 'temperature', $temperature);
readingsBulkUpdate($hash, 'pressure', $pressure);
readingsBulkUpdate($hash, 'pressure-nn', $pressureNN);
readingsBulkUpdate($hash, 'altitude', $altitude, 0);
readingsEndUpdate($hash, 1);
}
}
=head2 I2C_BMP180_Undef
Title: I2C_BMP180_Undef
Function: Implements UndefFn function.
Returns: undef
Args: named arguments:
-argument1 => hash: $hash hash of device
-argument2 => array: @a argument array
=cut
sub I2C_BMP180_Undef($$) {
my ($hash, $arg) = @_;
RemoveInternalTimer($hash);
return undef;
}
=head2 I2C_BMP180_ReadInt
Title: I2C_BMP180_ReadInt
Function: Read 2 bytes from i2c device from given register.
Returns: number
Args: named arguments:
-argument1 => hash: $hash hash of device
-argument2 => number: $register
-argument3 => boolean: $returnSigned 1, if number returned signed (optional)
=cut
sub I2C_BMP180_ReadInt($$;$) {
my ($hash, $register, $returnSigned) = @_;
my $name = $hash->{NAME};
$returnSigned = (!defined($returnSigned) || $returnSigned == 1) ? 1 : 0;
my $retVal = undef;
try {
my @values = $hash->{devBPM180}->bus_read($register, 2);
$retVal = $values[0] << 8 | $values[1];
# check if we need return signed or unsigned int
if ($returnSigned == 1) {
$retVal = $retVal >> 15 ? $retVal - 2**16 : $retVal;
}
} catch Error with {
Log (1, $name . ': ERROR: I2C_BMP180: i2c-bus_read failure');
};
return $retVal;
}
=head2 I2C_BMP180_readUncompensatedTemperature
Title: I2C_BMP180_readUncompensatedTemperature
Function: Read the uncompensated temperature value.
Returns: number
Args: named arguments:
-argument1 => hash: $hash hash of device
=cut
sub I2C_BMP180_readUncompensatedTemperature($) {
my ($hash) = @_;
# Write 0x2E into Register 0xF4. This requests a temperature reading
$hash->{devBPM180}->bus_write( (0xF4, 0x2E) );
usleep(4500);
# Read the two byte result from address 0xF6
my @values = $hash->{devBPM180}->bus_read(0xF6, 2);
my $retVal = $values[0] << 8 | $values[1];
return $retVal;
}
=head2 I2C_BMP180_readUncompensatedPressure
Title: I2C_BMP180_readUncompensatedPressure
Function: Read the uncompensated pressure value.
Returns: number
Args: named arguments:
-argument1 => hash: $hash hash of device
-argument2 => number: $overSamplingSettings
=cut
sub I2C_BMP180_readUncompensatedPressure($$) {
my ($hash, $overSamplingSettings) = @_;
# Write 0x34+($overSamplingSettings << 6) into register 0xF4
# Request a pressure reading with oversampling setting
$hash->{devBPM180}->bus_write( (0xF4, 0x34 + ($overSamplingSettings << 6)) );
# Wait for conversion, delay time dependent on oversampling setting
usleep( (2 + (3 << $overSamplingSettings)) * 1000 );
# Read the three byte result from 0xF6. 0xF6 = MSB, 0xF7 = LSB and 0xF8 = XLSB
my @values = $hash->{devBPM180}->bus_read(0xF6, 3);
my $retVal = ( ( ($values[0] << 16) | ($values[1] << 8) | $values[2] ) >> (8 - $overSamplingSettings) );
return $retVal;
}
=head2 I2C_BMP180_calcTrueTemperature
Title: I2C_BMP180_calcTrueTemperature
Function: Calculate temperature from given uncalibrated temperature
Returns: number
Args: named arguments:
-argument1 => hash: $hash hash of device
-argument2 => number: $ut uncalibrated temperature
=cut
sub I2C_BMP180_calcTrueTemperature($$) {
my ($hash, $ut) = @_;
my $x1 = ($ut - $hash->{calibrationData}{ac6}) * $hash->{calibrationData}{ac5} / 32768;
my $x2 = ($hash->{calibrationData}{mc} * 2048) / ($x1 + $hash->{calibrationData}{md});
$hash->{calibrationData}{b5} = $x1 + $x2;
my $retVal = (($hash->{calibrationData}{b5} + 8) / 16);
return $retVal;
}
=head2 I2C_BMP180_calcTruePressure
Title: I2C_BMP180_calcTruePressure
Function: Calculate the pressure from given uncalibrated pressure
Returns: number
Args: named arguments:
-argument1 => hash: $hash hash of device
-argument2 => number: $up uncalibrated pressure
-argument3 => number: $overSamplingSettings
=cut
sub I2C_BMP180_calcTruePressure($$$) {
my ($hash, $up, $overSamplingSettings) = @_;
my $b6 = $hash->{calibrationData}{b5} - 4000;
my $x1 = ($hash->{calibrationData}{b2} * ($b6 * $b6 / 4096)) / 2048;
my $x2 = ($hash->{calibrationData}{ac2} * $b6) / 2048;
my $x3 = $x1 + $x2;
my $b3 = ((($hash->{calibrationData}{ac1} * 4 + $x3) << $overSamplingSettings) + 2) / 4;
$x1 = $hash->{calibrationData}{ac3} * $b6 / 8192;
$x2 = ($hash->{calibrationData}{b1} * ($b6 * $b6 / 4096)) / 65536;
$x3 = (($x1 + $x2) + 2) / 4;
my $b4 = $hash->{calibrationData}{ac4} * ($x3 + 32768) / 32768;
my $b7 = ($up - $b3) * (50000 >> $overSamplingSettings);
my $p = ($b7 < 0x80000000) ? (($b7 * 2) / $b4) : (($b7 / $b4) * 2);
$x1 = ($p / 256) * ($p / 256);
$x1 = ($x1 * 3038) / 65536;
$x2 = (-7357 * $p) / 65536;
$p += (($x1 + $x2 + 3791) / 16);
return $p;
}
1;
=pod
=begin html
I2C_BMP180
With this module you can read values from the digital pressure sensors BMP180 and BMP085
via the i2c bus on Raspberry Pi.
Before you can use the Modul on the Raspberry Pi you must load the I2C kernel
modules.
Add these two lines to your /etc/modules file to load the kernel modules
automaticly during booting your Raspberry Pi.
i2c-bcm2708
i2c-dev
Please note:
For the i2c communication, the perl modules HiPi::Device::I2C
are required.
For a simple automated installation:
wget http://raspberry.znix.com/hipifiles/hipi-install
perl hipi-install
To change the permissions of the I2C device you must create the following
file /etc/udev/rules.d/98_i2c.rules with this content:
SUBSYSTEM=="i2c-dev", MODE="0666"
After these changes you must restart your Raspberry Pi.
If you want to use the sensor on the second I2C bus at the P5 connector
(only available at the version 2 of the Raspberry Pi) you must add the bold
line of this code in your FHEM start script:
case "$1" in
'start')
sudo hipi-i2c e 0 1
...
Define
define BMP180 I2C_BMP180 <I2C device>
Examples:
define BMP180 I2C_BMP180 /dev/i2c-0
attr BMP180 oversampling_settings 3
attr BMP180 poll_interval 5
Set
set BMP180 <readValues>
Reads the current temperature and pressure values from sensor.
Normaly this execute automaticly at each poll intervall. You can execute
this manually if you want query the current values.
Get
Attributes
- oversampling_settings
Controls the oversampling setting of the pressure measurement in the sensor.
Default: 3, valid values: 0, 1, 2, 3
- 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
- roundTemperatureDecimal
Round temperature values to given decimal places.
Default: 1, valid values: 0, 1, 2
- roundPressureDecimal
Round temperature values to given decimal places.
Default: 1, valid values: 0, 1, 2
- altitude
if set, this altitude is used for calculating the pressure related to sea level (nautic null) NN
Note: this is a global attributes, e.g
=end html
=cut