51_I2C_BMP180.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. # $Id: 51_I2C_BMP180.pm 12059 2016-08-22 21:14:59Z klauswitt $
  2. =head1
  3. 51_I2C_BMP180.pm
  4. =head1 SYNOPSIS
  5. Modul for FHEM for reading a BMP180 or BMP085 digital pressure sensor via I2C
  6. connected to the Raspberry Pi.
  7. contributed by Dirk Hoffmann 2013
  8. =head1 DESCRIPTION
  9. 51_I2C_BMP180.pm reads the air pressure of the digital pressure sensor BMP180
  10. or BMP085 via i2c bus connected to the Raspberry Pi.
  11. This module needs the HiPi Perl Modules
  12. see: http://raspberrypi.znix.com/hipidocs/
  13. For a simple automated installation:<br>
  14. wget http://raspberry.znix.com/hipifiles/hipi-install
  15. perl hipi-install
  16. Example:
  17. define BMP180 I2C_BMP180 /dev/i2c-0
  18. attr BMP180 poll_iterval 5
  19. attr BMP180 oversampling_settings 3
  20. =head1 AUTHOR - Dirk Hoffmann
  21. dirk@FHEM_Forum (forum.fhem.de)
  22. modified for use with physical I2C devices by Klaus Wittstock (klausw)
  23. modified to prevent division by zero when calibration data is invalid by Jens Beyer (jensb)
  24. =cut
  25. package main;
  26. use strict;
  27. use warnings;
  28. use Time::HiRes qw(usleep);
  29. use Scalar::Util qw(looks_like_number);
  30. use constant {
  31. BMP180_I2C_ADDRESS => '0x77',
  32. };
  33. ##################################################
  34. # Forward declarations
  35. #
  36. sub I2C_BMP180_Initialize($);
  37. sub I2C_BMP180_Define($$);
  38. sub I2C_BMP180_Attr(@);
  39. sub I2C_BMP180_Poll($);
  40. sub I2C_BMP180_Set($@);
  41. sub I2C_BMP180_Undef($$);
  42. sub I2C_BMP180_readUncompensatedTemperature($);
  43. sub I2C_BMP180_readUncompensatedPressure($$);
  44. sub I2C_BMP180_calcTrueTemperature($$);
  45. sub I2C_BMP180_calcTruePressure($$$);
  46. sub I2C_BMP180_DbLog_splitFn($);
  47. my $libcheck_hasHiPi = 1;
  48. my %sets = (
  49. 'readValues' => 1,
  50. );
  51. =head2 I2C_BMP180_Initialize
  52. Title: I2C_BMP180_Initialize
  53. Function: Implements the initialize function.
  54. Returns: -
  55. Args: named arguments:
  56. -argument1 => hash
  57. =cut
  58. sub I2C_BMP180_Initialize($) {
  59. my ($hash) = @_;
  60. eval "use HiPi::Device::I2C;";
  61. $libcheck_hasHiPi = 0 if($@);
  62. $hash->{DefFn} = 'I2C_BMP180_Define';
  63. $hash->{InitFn} = 'I2C_BMP180_Init';
  64. $hash->{AttrFn} = 'I2C_BMP180_Attr';
  65. $hash->{SetFn} = 'I2C_BMP180_Set';
  66. $hash->{UndefFn} = 'I2C_BMP180_Undef';
  67. $hash->{I2CRecFn} = 'I2C_BMP180_I2CRec';
  68. $hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 model:BMP180,BMP085 ' .
  69. 'poll_interval:1,2,5,10,20,30 oversampling_settings:0,1,2,3 ' .
  70. 'roundPressureDecimal:0,1,2 roundTemperatureDecimal:0,1,2 ' .
  71. $readingFnAttributes;
  72. $hash->{AttrList} .= " useHiPiLib:0,1 " if( $libcheck_hasHiPi );
  73. $hash->{DbLog_splitFn} = "I2C_BMP180_DbLog_splitFn";
  74. }
  75. =head2 I2C_BMP180_Define
  76. Title: I2C_BMP180_Define
  77. Function: Implements the define function.
  78. Returns: string|undef
  79. Args: named arguments:
  80. -argument1 => hash
  81. -argument2 => string
  82. =cut
  83. sub I2C_BMP180_Define($$) {
  84. my ($hash, $def) = @_;
  85. my @a = split('[ \t][ \t]*', $def);
  86. $hash->{STATE} = 'defined';
  87. my $name = $a[0];
  88. my $msg = '';
  89. $hash->{HiPi_exists} = $libcheck_hasHiPi if($libcheck_hasHiPi);
  90. if (@a == 3) {
  91. if ($libcheck_hasHiPi) {
  92. $hash->{HiPi_used} = 1;
  93. } else {
  94. $msg = '$name error: HiPi library not installed';
  95. }
  96. } elsif((@a < 2)) {
  97. $msg = 'wrong syntax: define <name> I2C_BMP180 [devicename]';
  98. }
  99. if ($msg) {
  100. Log3 ($hash, 1, $msg);
  101. return $msg;
  102. }
  103. if ($main::init_done || $hash->{HiPi_used}) {
  104. eval { I2C_BMP180_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
  105. return I2C_BMP180_Catch($@) if $@;
  106. }
  107. }
  108. sub I2C_BMP180_Init($$) {
  109. my ( $hash, $args ) = @_;
  110. my $name = $hash->{NAME};
  111. $hash->{I2C_Address} = hex(BMP180_I2C_ADDRESS);
  112. my $msg = '';
  113. # create default attributes
  114. if (AttrVal($name, 'poll_interval', '?') eq '?') {
  115. $msg = CommandAttr(undef, $name . ' poll_interval 5');
  116. if ($msg) {
  117. Log3 ($hash, 1, $msg);
  118. return $msg;
  119. }
  120. }
  121. if (AttrVal($name, 'oversampling_settings', '?') eq '?') {
  122. $msg = CommandAttr(undef, $name . ' oversampling_settings 3');
  123. if ($msg) {
  124. Log3 ($hash, 1, $msg);
  125. return $msg;
  126. }
  127. }
  128. if ($hash->{HiPi_used}) {
  129. my $dev = shift @$args;
  130. my $i2cModulesLoaded = 0;
  131. $i2cModulesLoaded = 1 if -e $dev;
  132. if ($i2cModulesLoaded) {
  133. $hash->{devBPM180} = HiPi::Device::I2C->new(
  134. devicename => $dev,
  135. address => hex(BMP180_I2C_ADDRESS),
  136. busmode => 'i2c',
  137. );
  138. } else {
  139. return $name . ': Error! I2C device not found: ' . $dev . '. Please check kernelmodules must loaded: i2c_bcm2708, i2c_dev';
  140. }
  141. } else {
  142. AssignIoPort($hash);
  143. }
  144. $hash->{STATE} = 'getCalData';
  145. I2C_BMP180_i2cread($hash, hex("AA"), 22);
  146. return undef;
  147. }
  148. sub I2C_BMP180_Catch($) {
  149. my $exception = shift;
  150. if ($exception) {
  151. $exception =~ /^(.*)( at.*FHEM.*)$/;
  152. return $1;
  153. }
  154. return undef;
  155. }
  156. =head2 I2C_BMP180_Attr
  157. Title: I2C_BMP180_Attr
  158. Function: Implements AttrFn function.
  159. Returns: string|undef
  160. Args: named arguments:
  161. -argument1 => array
  162. =cut
  163. sub I2C_BMP180_Attr (@) {
  164. my (undef, $name, $attr, $val) = @_;
  165. my $hash = $defs{$name};
  166. my $msg = '';
  167. if ($attr eq 'poll_interval') {
  168. my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0;
  169. if ($val > 0) {
  170. RemoveInternalTimer($hash);
  171. InternalTimer(1, 'I2C_BMP180_Poll', $hash, 0);
  172. } else {
  173. $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
  174. }
  175. }
  176. return ($msg) ? $msg : undef;
  177. }
  178. =head2 I2C_BMP180_Poll
  179. Title: I2C_BMP180_Poll
  180. Function: Start polling the sensor at interval defined in attribute
  181. Returns: -
  182. Args: named arguments:
  183. -argument1 => hash
  184. =cut
  185. sub I2C_BMP180_Poll($) {
  186. my ($hash) = @_;
  187. my $name = $hash->{NAME};
  188. # Read values
  189. I2C_BMP180_Set($hash, ($name, 'readValues'));
  190. my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
  191. if ($pollInterval > 0) {
  192. InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BMP180_Poll', $hash, 0);
  193. }
  194. }
  195. =head2 I2C_BMP180_Set
  196. Title: I2C_BMP180_Set
  197. Function: Implements SetFn function.
  198. Returns: string|undef
  199. Args: named arguments:
  200. -argument1 => hash: $hash hash of device
  201. -argument2 => array: @a argument array
  202. =cut
  203. sub I2C_BMP180_Set($@) {
  204. my ($hash, @a) = @_;
  205. my $name = $a[0];
  206. my $cmd = $a[1];
  207. if(!defined($sets{$cmd})) {
  208. return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %sets)
  209. }
  210. if ($cmd eq 'readValues') {
  211. my $overSamplingSettings = AttrVal($hash->{NAME}, 'oversampling_settings', 3);
  212. if (defined($hash->{calibrationData}{md}) && $hash->{calibrationData}{md} != 0 && $hash->{calibrationData}{ac4} != 0) { # query sensor
  213. I2C_BMP180_readUncompensatedTemperature($hash);
  214. I2C_BMP180_readUncompensatedPressure($hash, $overSamplingSettings);
  215. } else { #..but get calibration variables first
  216. I2C_BMP180_i2cread($hash, hex("AA"), 22);
  217. }
  218. }
  219. }
  220. =head2 I2C_BMP180_Undef
  221. Title: I2C_BMP180_Undef
  222. Function: Implements UndefFn function.
  223. Returns: undef
  224. Args: named arguments:
  225. -argument1 => hash: $hash hash of device
  226. -argument2 => array: @a argument array
  227. =cut
  228. sub I2C_BMP180_Undef($$) {
  229. my ($hash, $arg) = @_;
  230. RemoveInternalTimer($hash);
  231. return undef;
  232. }
  233. sub I2C_BMP180_I2CRec ($$) {
  234. my ($hash, $clientmsg) = @_;
  235. my $name = $hash->{NAME};
  236. my $pname = undef;
  237. unless ($hash->{HiPi_used}) {#nicht nutzen wenn HiPi Bibliothek in Benutzung
  238. my $phash = $hash->{IODev};
  239. $pname = $phash->{NAME};
  240. while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
  241. $hash->{$k} = $v if $k =~ /^$pname/ ;
  242. }
  243. }
  244. if ( $clientmsg->{direction} && $clientmsg->{reg} && (
  245. ($pname && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok")
  246. || $hash->{HiPi_used}) ) {
  247. if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
  248. Log3 $hash, 5, "$name empfangen: $clientmsg->{received}";
  249. I2C_BMP180_GetCal ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("AA");
  250. I2C_BMP180_GetTemp ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("F6") && $clientmsg->{nbyte} == 2;
  251. I2C_BMP180_GetPress ($hash, $clientmsg->{received}) if $clientmsg->{reg} == hex("F6") && $clientmsg->{nbyte} == 3;
  252. }
  253. }
  254. }
  255. sub I2C_BMP180_GetCal ($$) {
  256. my ($hash, $rawdata) = @_;
  257. my @raw = split(" ",$rawdata);
  258. my $n = 0;
  259. Log3 $hash, 5, "in get cal: $rawdata";
  260. $hash->{calibrationData}{ac1} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  261. $hash->{calibrationData}{ac2} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  262. $hash->{calibrationData}{ac3} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  263. $hash->{calibrationData}{ac4} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++], 0);
  264. $hash->{calibrationData}{ac5} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++], 0);
  265. $hash->{calibrationData}{ac6} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++], 0);
  266. $hash->{calibrationData}{b1} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  267. $hash->{calibrationData}{b2} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  268. $hash->{calibrationData}{mb} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  269. $hash->{calibrationData}{mc} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  270. $hash->{calibrationData}{md} = I2C_BMP180_GetCalVar($raw[$n++], $raw[$n++]);
  271. $hash->{STATE} = 'Initialized';
  272. return
  273. }
  274. sub I2C_BMP180_GetCalVar ($$;$) {
  275. my ($msb, $lsb, $returnSigned) = @_;
  276. $returnSigned = (!defined($returnSigned) || $returnSigned == 1) ? 1 : 0;
  277. my $retVal = undef;
  278. $retVal = $msb << 8 | $lsb;
  279. # check if we need return signed or unsigned int
  280. if ($returnSigned == 1) {
  281. $retVal = $retVal >> 15 ? $retVal - 2**16 : $retVal;
  282. }
  283. return $retVal;
  284. }
  285. sub I2C_BMP180_GetTemp ($$) {
  286. my ($hash, $rawdata) = @_;
  287. my @raw = split(" ",$rawdata);
  288. $hash->{uncompTemp} = $raw[0] << 8 | $raw[1];
  289. }
  290. sub I2C_BMP180_GetPress ($$) {
  291. my ($hash, $rawdata) = @_;
  292. my @raw = split(" ",$rawdata);
  293. my $overSamplingSettings = AttrVal($hash->{NAME}, 'oversampling_settings', 3);
  294. my $ut = $hash->{uncompTemp};
  295. delete $hash->{uncompTemp};
  296. return undef unless defined($hash->{calibrationData}{md}) && $hash->{calibrationData}{md} != 0 && $hash->{calibrationData}{ac4} != 0;
  297. my $temperature = sprintf(
  298. '%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
  299. I2C_BMP180_calcTrueTemperature($hash, $ut) / 10
  300. );
  301. my $up = ( ( ($raw[0] << 16) | ($raw[1] << 8) | $raw[2] ) >> (8 - $overSamplingSettings) );
  302. my $pressure = sprintf(
  303. '%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
  304. I2C_BMP180_calcTruePressure($hash, $up, $overSamplingSettings) / 100
  305. );
  306. my $altitude = AttrVal('global', 'altitude', 0);
  307. # simple barometric height formula
  308. my $pressureNN = sprintf(
  309. '%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
  310. $pressure + ($altitude / 8.5)
  311. );
  312. readingsBeginUpdate($hash);
  313. readingsBulkUpdate(
  314. $hash,
  315. 'state',
  316. 'T: ' . $temperature . ' P: ' . $pressure . ' P-NN: ' . $pressureNN
  317. );
  318. readingsBulkUpdate($hash, 'temperature', $temperature);
  319. readingsBulkUpdate($hash, 'pressure', $pressure);
  320. readingsBulkUpdate($hash, 'pressure-nn', $pressureNN);
  321. #readingsBulkUpdate($hash, 'altitude', $altitude, 0);
  322. readingsEndUpdate($hash, 1);
  323. }
  324. =head2 I2C_BMP180_readUncompensatedTemperature
  325. Title: I2C_BMP180_readUncompensatedTemperature
  326. Function: Read the uncompensated temperature value.
  327. Returns: number
  328. Args: named arguments:
  329. -argument1 => hash: $hash hash of device
  330. =cut
  331. sub I2C_BMP180_readUncompensatedTemperature($) {
  332. my ($hash) = @_;
  333. # Write 0x2E into Register 0xF4. This requests a temperature reading
  334. I2C_BMP180_i2cwrite($hash, hex("F4"), hex("2E"));
  335. usleep(4500);
  336. # Read the two byte result from address 0xF6
  337. I2C_BMP180_i2cread($hash, hex("F6"), 2);
  338. return;
  339. }
  340. =head2 I2C_BMP180_readUncompensatedPressure
  341. Title: I2C_BMP180_readUncompensatedPressure
  342. Function: Read the uncompensated pressure value.
  343. Returns: number
  344. Args: named arguments:
  345. -argument1 => hash: $hash hash of device
  346. -argument2 => number: $overSamplingSettings
  347. =cut
  348. sub I2C_BMP180_readUncompensatedPressure($$) {
  349. my ($hash, $overSamplingSettings) = @_;
  350. # Write 0x34+($overSamplingSettings << 6) into register 0xF4
  351. # Request a pressure reading with oversampling setting
  352. my $data = hex("34") + ($overSamplingSettings << 6);
  353. I2C_BMP180_i2cwrite($hash, hex("F4"), $data);
  354. # Wait for conversion, delay time dependent on oversampling setting
  355. usleep( (2 + (3 << $overSamplingSettings)) * 1000 );
  356. # Read the three byte result from 0xF6. 0xF6 = MSB, 0xF7 = LSB and 0xF8 = XLSB
  357. I2C_BMP180_i2cread($hash, hex("F6"), 3);
  358. return;
  359. }
  360. sub I2C_BMP180_i2cread($$$) {
  361. my ($hash, $reg, $nbyte) = @_;
  362. if ($hash->{HiPi_used}) {
  363. eval {
  364. my @values = $hash->{devBPM180}->bus_read($reg, $nbyte);
  365. I2C_BMP180_I2CRec($hash, {
  366. direction => "i2cread",
  367. i2caddress => $hash->{I2C_Address},
  368. reg => $reg,
  369. nbyte => $nbyte,
  370. received => join (' ',@values),
  371. });
  372. };
  373. Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_BMP180_Catch($@)) if $@;;
  374. } else {
  375. if (defined (my $iodev = $hash->{IODev})) {
  376. CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
  377. direction => "i2cread",
  378. i2caddress => $hash->{I2C_Address},
  379. reg => $reg,
  380. nbyte => $nbyte
  381. });
  382. } else {
  383. return "no IODev assigned to '$hash->{NAME}'";
  384. }
  385. }
  386. }
  387. sub I2C_BMP180_i2cwrite($$$) {
  388. my ($hash, $reg, @data) = @_;
  389. if ($hash->{HiPi_used}) {
  390. eval {
  391. $hash->{devBPM180}->bus_write($reg, join (' ',@data));
  392. I2C_BMP180_I2CRec($hash, {
  393. direction => "i2cwrite",
  394. i2caddress => $hash->{I2C_Address},
  395. reg => $reg,
  396. data => join (' ',@data),
  397. });
  398. };
  399. Log3 ($hash, 1, $hash->{NAME} . ': ' . I2C_BMP180_Catch($@)) if $@;;
  400. } else {
  401. if (defined (my $iodev = $hash->{IODev})) {
  402. CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
  403. direction => "i2cwrite",
  404. i2caddress => $hash->{I2C_Address},
  405. reg => $reg,
  406. data => join (' ',@data),
  407. });
  408. } else {
  409. return "no IODev assigned to '$hash->{NAME}'";
  410. }
  411. }
  412. }
  413. =head2 I2C_BMP180_calcTrueTemperature
  414. Title: I2C_BMP180_calcTrueTemperature
  415. Function: Calculate temperature from given uncalibrated temperature
  416. Returns: number
  417. Args: named arguments:
  418. -argument1 => hash: $hash hash of device
  419. -argument2 => number: $ut uncalibrated temperature
  420. =cut
  421. sub I2C_BMP180_calcTrueTemperature($$) {
  422. my ($hash, $ut) = @_;
  423. my $x1 = ($ut - $hash->{calibrationData}{ac6}) * $hash->{calibrationData}{ac5} / 32768;
  424. my $x2 = ($hash->{calibrationData}{mc} * 2048) / ($x1 + $hash->{calibrationData}{md});
  425. $hash->{calibrationData}{b5} = $x1 + $x2;
  426. my $retVal = (($hash->{calibrationData}{b5} + 8) / 16);
  427. return $retVal;
  428. }
  429. =head2 I2C_BMP180_calcTruePressure
  430. Title: I2C_BMP180_calcTruePressure
  431. Function: Calculate the pressure from given uncalibrated pressure
  432. Returns: number
  433. Args: named arguments:
  434. -argument1 => hash: $hash hash of device
  435. -argument2 => number: $up uncalibrated pressure
  436. -argument3 => number: $overSamplingSettings
  437. =cut
  438. sub I2C_BMP180_calcTruePressure($$$) {
  439. my ($hash, $up, $overSamplingSettings) = @_;
  440. my $b6 = $hash->{calibrationData}{b5} - 4000;
  441. my $x1 = ($hash->{calibrationData}{b2} * ($b6 * $b6 / 4096)) / 2048;
  442. my $x2 = ($hash->{calibrationData}{ac2} * $b6) / 2048;
  443. my $x3 = $x1 + $x2;
  444. my $b3 = ((($hash->{calibrationData}{ac1} * 4 + $x3) << $overSamplingSettings) + 2) / 4;
  445. $x1 = $hash->{calibrationData}{ac3} * $b6 / 8192;
  446. $x2 = ($hash->{calibrationData}{b1} * ($b6 * $b6 / 4096)) / 65536;
  447. $x3 = (($x1 + $x2) + 2) / 4;
  448. my $b4 = $hash->{calibrationData}{ac4} * ($x3 + 32768) / 32768;
  449. my $b7 = ($up - $b3) * (50000 >> $overSamplingSettings);
  450. my $p = ($b7 < 0x80000000) ? (($b7 * 2) / $b4) : (($b7 / $b4) * 2);
  451. $x1 = ($p / 256) * ($p / 256);
  452. $x1 = ($x1 * 3038) / 65536;
  453. $x2 = (-7357 * $p) / 65536;
  454. $p += (($x1 + $x2 + 3791) / 16);
  455. return $p;
  456. }
  457. sub I2C_BMP180_DbLog_splitFn($) {
  458. my ($event) = @_;
  459. Log3 undef, 5, "in DbLog_splitFn empfangen: $event";
  460. my ($reading, $value, $unit) = "";
  461. my @parts = split(/ /,$event);
  462. $reading = shift @parts;
  463. $reading =~ tr/://d;
  464. $value = $parts[0];
  465. $unit = "\xB0C" if(lc($reading) =~ m/temp/);
  466. $unit = "hPa" if(lc($reading) =~ m/pres/);
  467. return ($reading, $value, $unit);
  468. }
  469. 1;
  470. =pod
  471. =item device
  472. =item summary reads pressure and temperature from an via I2C connected BMP180/BMP085
  473. =item summary_DE lese Druck und Temperatur eines &uuml;ber I2C angeschlossenen BMP180/BMP085
  474. =begin html
  475. <a name="I2C_BMP180"></a>
  476. <h3>I2C_BMP180</h3>
  477. <ul>
  478. <a name="I2C_BMP180"></a>
  479. <p>
  480. With this module you can read values from the digital pressure sensors BMP180 and BMP085
  481. via the i2c bus on Raspberry Pi.<br><br>
  482. <b>There are two possibilities connecting to I2C bus:</b><br>
  483. <ul>
  484. <li><b>via RPII2C module</b><br>
  485. The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  486. or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
  487. <b>attribute IODev must be set</b><br><br>
  488. </li>
  489. <li><b>via HiPi library</b><br>
  490. Add these two lines to your <b>/etc/modules</b> file to load the I2C relevant kernel modules
  491. automaticly during booting your Raspberry Pi.<br>
  492. <code><pre>i2c-bcm2708
  493. i2c-dev</pre></code>
  494. Install HiPi perl modules:<br>
  495. <code><pre>wget http://raspberry.znix.com/hipifiles/hipi-install
  496. perl hipi-install</pre></code>
  497. To change the permissions of the I2C device create file:<br>
  498. <code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
  499. with this content:<br>
  500. <code><pre>SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
  501. <b>Reboot</b><br><br>
  502. To use the sensor on the second I2C bus at P5 connector
  503. (only for version 2 of Raspberry Pi) you must add the bold
  504. line of following code to your FHEM start script:
  505. <code><pre> case "$1" in
  506. 'start')
  507. <b>sudo hipi-i2c e 0 1</b>
  508. ...</pre></code>
  509. </li></ul>
  510. <p>
  511. <b>Define</b>
  512. <ul>
  513. <code>define BMP180 I2C_BMP180 [&lt;I2C device&gt;]</code><br><br>
  514. &lt;I2C device&gt; must not be used if you connect via RPII2C module. For HiPi it's mandatory. <br>
  515. <br>
  516. Examples:
  517. <pre>
  518. define BMP180 I2C_BMP180 /dev/i2c-0
  519. attr BMP180 oversampling_settings 3
  520. attr BMP180 poll_interval 5
  521. </pre>
  522. <pre>
  523. define BMP180 I2C_BMP180
  524. attr BMP180 IODev RPiI2CMod
  525. attr BMP180 oversampling_settings 3
  526. attr BMP180 poll_interval 5
  527. </pre>
  528. </ul>
  529. <a name="I2C_BMP180set"></a>
  530. <b>Set</b>
  531. <ul>
  532. <code>set BMP180 &lt;readValues&gt;</code>
  533. <br><br>
  534. Reads the current temperature and pressure values from sensor.<br>
  535. Normaly this execute automaticly at each poll intervall. You can execute
  536. this manually if you want query the current values.
  537. <br><br>
  538. </ul>
  539. <a name="I2C_BMP180get"></a>
  540. <b>Get</b>
  541. <ul>
  542. N/A
  543. </ul>
  544. <br>
  545. <a name="I2C_BMP180attr"></a>
  546. <b>Attributes</b>
  547. <ul>
  548. <li>oversampling_settings<br>
  549. Controls the oversampling setting of the pressure measurement in the sensor.<br>
  550. Default: 3, valid values: 0, 1, 2, 3<br><br>
  551. </li>
  552. <li>poll_interval<br>
  553. Set the polling interval in minutes to query the sensor for new measured
  554. values.<br>
  555. Default: 5, valid values: 1, 2, 5, 10, 20, 30<br><br>
  556. </li>
  557. <li>roundTemperatureDecimal<br>
  558. Round temperature values to given decimal places.<br>
  559. Default: 1, valid values: 0, 1, 2<br><br>
  560. </li>
  561. <li>roundPressureDecimal<br>
  562. Round temperature values to given decimal places.<br>
  563. Default: 1, valid values: 0, 1, 2<br><br>
  564. </li>
  565. <li>altitude<br>
  566. if set, this altitude is used for calculating the pressure related to sea level (nautic null) NN<br><br>
  567. Note: this is a global attributes, e.g<br>
  568. <ul>
  569. attr global altitude 220
  570. </ul>
  571. </li>
  572. </ul>
  573. <br>
  574. <b>Notes</b>
  575. <ul>
  576. <li>I2C bus timing<br>
  577. For all sensor operations an I2C interface with blocking IO is assumed (e.g. RPII2C).
  578. If you use an I2C interface with non-blocking IO (e.g. FRM over ethernet) operation errors may occur,
  579. especially if setting the attribute oversampling_settings to a value higher than 1.
  580. This may be compensated depending on I2C interface used. For Firmata try setting the attribute
  581. i2c-config in the FRM module to a value of about 30000 microseconds.<br><br>
  582. </li>
  583. </ul>
  584. <br>
  585. </ul>
  586. =end html
  587. =begin html_DE
  588. <a name="I2C_BMP180"></a>
  589. <h3>I2C_BMP180</h3>
  590. <ul>
  591. <a name="I2C_BMP180"></a>
  592. <p>
  593. Dieses Modul erm&ouml;glicht das Auslesen der digitalen (Luft)drucksensoren
  594. BMP085 und BMP180 &uuml;ber den I2C Bus des Raspberry Pi.<br><br>
  595. <b>Es gibt zwei M&ouml;glichkeiten das Modul mit dem I2C Bus zu verbinden:</b><br>
  596. <ul>
  597. <li><b>&Uuml;ber das RPII2C Modul</b><br>
  598. I2C-Botschaften werden &uuml;ber ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
  599. oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
  600. <b>Das Attribut IODev muss definiert sein.</b><br><br>
  601. </li>
  602. <li><b>&Uuml;ber die HiPi Bibliothek</b><br>
  603. Diese beiden Zeilen m&uuml;ssen in die Datei <b>/etc/modules</b> angef&uuml;gt werden,
  604. um die Kernel Module automatisch beim Booten des Raspberry Pis zu laden.<br>
  605. <code><pre>i2c-bcm2708
  606. i2c-dev</pre></code>
  607. Installation des HiPi Perl Moduls:<br>
  608. <code><pre>wget http://raspberry.znix.com/hipifiles/hipi-install
  609. perl hipi-install</pre></code>
  610. Um die Rechte f&uuml;r die I2C Devices anzupassen, folgende Datei:<br>
  611. <code><pre> /etc/udev/rules.d/98_i2c.rules</pre></code>
  612. mit diesem Inhalt anlegen:<br>
  613. <code><pre>SUBSYSTEM=="i2c-dev", MODE="0666"</pre></code>
  614. <b>Reboot</b><br><br>
  615. Falls der Sensor am zweiten I2C Bus am Stecker P5 (nur in Version 2 des
  616. Raspberry Pi) verwendet werden soll, muss die fett gedruckte Zeile
  617. des folgenden Codes in das FHEM Start Skript aufgenommen werden:
  618. <code><pre> case "$1" in
  619. 'start')
  620. <b>sudo hipi-i2c e 0 1</b>
  621. ...</pre></code>
  622. </li></ul>
  623. <p>
  624. <b>Define</b>
  625. <ul>
  626. <code>define BMP180 &lt;BMP180_name&gt; &lt;I2C_device&gt;</code><br><br>
  627. &lt;I2C device&gt; darf nicht verwendet werden, wenn der I2C Bus &uuml;ber das RPII2C Modul angesprochen wird. F&uuml;r HiPi ist es allerdings notwendig. <br>
  628. <br>
  629. Beispiel:
  630. <pre>
  631. define BMP180 I2C_BMP180 /dev/i2c-0
  632. attr BMP180 oversampling_settings 3
  633. attr BMP180 poll_interval 5
  634. </pre>
  635. <pre>
  636. define BMP180 I2C_BMP180
  637. attr BMP180 IODev RPiI2CMod
  638. attr BMP180 oversampling_settings 3
  639. attr BMP180 poll_interval 5
  640. </pre>
  641. </ul>
  642. <a name="I2C_BMP180set"></a>
  643. <b>Set</b>
  644. <ul>
  645. <code>set BMP180 readValues</code>
  646. <br><br>
  647. Liest die aktuelle Temperatur und den Luftdruck des Sensors aus.<br>
  648. Dies wird automatisch nach Ablauf des definierten Intervalls ausgef&uuml;hrt.
  649. Wenn der aktuelle Wert gelesen werden soll, kann dieser Befehl auch manuell
  650. ausgef&uuml;hrt werden.
  651. <br><br>
  652. </ul>
  653. <a name="I2C_BMP180get"></a>
  654. <b>Get</b>
  655. <ul>
  656. N/A
  657. </ul>
  658. <br>
  659. <a name="I2C_BMP180attr"></a>
  660. <b>Attribute</b>
  661. <ul>
  662. <li>oversampling_settings<br>
  663. Steuert das Oversampling der Druckmessung im Sensor.<br>
  664. Default: 3, g&uuml;ltige Werte: 0, 1, 2, 3<br><br>
  665. </li>
  666. <li>poll_interval<br>
  667. Definiert das Poll Intervall in Minuten f&uuml;r das Auslesen einer neuen Messung.<br>
  668. Default: 5, g&uuml;ltige Werte: 1, 2, 5, 10, 20, 30<br><br>
  669. </li>
  670. <li>roundTemperatureDecimal<br>
  671. Rundet den Temperaturwert mit den angegebenen Nachkommastellen.<br>
  672. Default: 1, g&uuml;ltige Werte: 0, 1, 2<br><br>
  673. </li>
  674. <li>roundPressureDecimal<br>
  675. Rundet die Drucksensorwerte mit den angegebenen Nachkommastellen.<br>
  676. Default: 1, valid values: 0, 1, 2<br><br>
  677. </li>
  678. <li>altitude<br>
  679. Wenn dieser Wert definiert ist, wird diese Angabe zus&auml;tzlich f&uuml;r die Berechnung des
  680. Luftdrucks bezogen auf Meeresh&ouml;he (Normalnull) NN herangezogen.<br>
  681. Bemerkung: Dies ist ein globales Attribut.<br><br>
  682. <code>attr global altitude 220</code>
  683. </li>
  684. </ul>
  685. <br>
  686. <b>Hinweise</b>
  687. <ul>
  688. <li>I2C-Bustiming<br>
  689. Zur Abfrage des Sensors wird von einer I2C-Schnittstelle mit blockierendem IO-Zugriff (z.B. RPII2C) ausgegangen.
  690. Bei I2C-Schnittstellen, die nicht-blockierend arbeiten (z.B. FRM mit Ethernet), kann es zu Verarbeitungsfehlern kommen,
  691. insbesondere wenn das Attribut oversampling_settings auf einen Wert gr&ouml;&szlig;er 1 eingestellt wird.
  692. Dies l&auml;sst sich je nach I2C-Schnittstelle kompensieren. Bei Firmata empfiehlt es sich,
  693. das Attribut i2c-config im Modul FRM auf einen Wert von ca. 30000 Mikrosekunden einzustellen.<br><br>
  694. </li>
  695. </ul>
  696. <br>
  697. </ul>
  698. =end html_DE
  699. =cut