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);
+ my $txtAltitude = '';
+ my $pressureNN = 0;
+
+ # if altitude given
+ if ($altitude != 0) {
+ # simple barometric height formula
+ $pressureNN = sprintf(
+ '%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
+ $pressure + ($altitude / 8.5)
+ );
+
+ $txtAltitude = ' in ' . $altitude . ' m, Pressure-NN: ' . $pressureNN . ' hPa';
+ }
+
+ readingsBeginUpdate($hash);
+ readingsBulkUpdate(
+ $hash,
+ 'state',
+ 'Temp: ' . $temperature . ' °C , Pressure: ' . $pressure . ' hPa' . $txtAltitude,
+ 0
+ );
+ readingsBulkUpdate($hash, 'temperature', $temperature);
+ readingsBulkUpdate($hash, 'pressure', $pressure);
+
+ # if altitude given
+ if ($altitude >= 0) {
+ readingsBulkUpdate($hash, 'pressure-nn', $pressureNN);
+ }
+
+ 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
+
+ attr global altitude 220
+
+
+
+
+
+
+
+=end html
+=cut
diff --git a/fhem/HISTORY b/fhem/HISTORY
index 005e0f35d..e21c7bcd8 100644
--- a/fhem/HISTORY
+++ b/fhem/HISTORY
@@ -529,4 +529,7 @@
- Added new module "remotecontrol"
- Sun Jul 14 2013 (Alexus)
- - Added new module "EGPM2LAN" and "EGPM"
\ No newline at end of file
+ - Added new module "EGPM2LAN" and "EGPM"
+
+- Fri Jul 26 2013 (Dirk)
+ - Added new module "I2C_BMP180"
\ No newline at end of file
diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index 931179fa6..5a3e3237c 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -99,6 +99,7 @@ FHEM/46_TRX_SECURITY.pm wherzig http://forum.fhem.de RFXTRX
FHEM/46_TRX_WEATHER.pm wherzig http://forum.fhem.de RFXTRX
FHEM/49_IPCAM.pm mfr69bs http://forum.fhem.de Sonstiges
FHEM/50_WS300.pm tdressler http://forum.fhem.de SlowRF
+FHEM/51_I2C_BMP180.pm Dirk http://forum.fhem.de/ Raspberry Pi
FHEM/56_POKEYS.pm axelberner http://forum.fhem.de Sonstiges
FHEM/57_Calendar.pm borisneubert http://forum.fhem.de Sonstiges
FHEM/59_HCS.pm mfr69bs http://forum.fhem.de Automatisierung
@@ -122,8 +123,8 @@ FHEM/70_VIERA.pm teevau http://forum.fhem.de Sonstiges
FHEM/70_WS3600.pm painseeker http://forum.fhem.de Sonstiges
FHEM/71_LISTENLIVE.pm betateilchen http://forum.fhem.de Multimedia
FHEM/71_YAMAHA_AVR.pm markusbloch http://forum.fhem.de Multimedia
-FHEM/72_FB_CALLMONITOR.pm markusbloch http://forum.fhem.de Unterstützende Dienste
-FHEM/73_PRESENCE.pm markusbloch http://forum.fhem.de Unterstützende Dienste
+FHEM/72_FB_CALLMONITOR.pm markusbloch http://forum.fhem.de Unterstützende Dienste
+FHEM/73_PRESENCE.pm markusbloch http://forum.fhem.de Unterstützende Dienste
FHEM/75_MSG.pm ruebedo http://forum.fhem.de Automatisierung
FHEM/76_MSGFile.pm ruebedo http://forum.fhem.de Automatisierung
FHEM/76_MSGMail.pm ruebedo http://forum.fhem.de Automatisierung