| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- # $Id: 52_I2C_BME280.pm 12714 2016-12-04 21:34:03Z klauswitt $
- =head1
- 52_I2C_BME280.pm
- =head1 SYNOPSIS
- Modul for FHEM for reading a BME280 digital pressure/humidity sensor via I2C
- =cut
- package main;
- use strict;
- use warnings;
- use constant {
- BME280_I2C_ADDRESS => 0x76,
- };
- ##################################################
- # Forward declarations
- #
- sub I2C_BME280_I2CRec ($$);
- sub I2C_BME280_GetReadings ($$);
- sub I2C_BME280_GetTemp ($@);
- sub I2C_BME280_GetPress ($@);
- sub I2C_BME280_GetHum ($@);
- sub I2C_BME280_calcTrueTemperature($$);
- sub I2C_BME280_calcTrueHumidity($$);
- sub I2C_BME280_calcTruePressure($$);
- my %sets = (
- 'readValues' => 1,
- );
- sub I2C_BME280_Initialize($) {
- my ($hash) = @_;
- $hash->{DefFn} = 'I2C_BME280_Define';
- $hash->{InitFn} = 'I2C_BME280_Init';
- $hash->{AttrFn} = 'I2C_BME280_Attr';
- $hash->{SetFn} = 'I2C_BME280_Set';
- #$hash->{GetFn} = 'I2C_BME280_Get';
- $hash->{UndefFn} = 'I2C_BME280_Undef';
- $hash->{I2CRecFn} = 'I2C_BME280_I2CRec';
- $hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 poll_interval:1,2,5,10,20,30 ' .
- 'oversampling_t:0,1,2,3,4,5 oversampling_p:0,1,2,3,4,5 oversampling_h:0,1,2,3,4,5 ' .
- 'roundPressureDecimal:0,1,2 roundTemperatureDecimal:0,1,2 roundHumidityDecimal:0,1,2 ' .
- $readingFnAttributes;
- $hash->{DbLog_splitFn} = "I2C_BME280_DbLog_splitFn";
- }
- sub I2C_BME280_Define($$) {
- my ($hash, $def) = @_;
- my @a = split('[ \t][ \t]*', $def);
- $hash->{STATE} = 'defined';
-
- my $name = $a[0];
- my $msg = '';
- if((@a < 2)) {
- $msg = 'wrong syntax: define <name> I2C_BME280 [I2C-Address]';
- }
- if ($msg) {
- Log3 ($hash, 1, $msg);
- return $msg;
- }
- if ($main::init_done) {
- eval { I2C_BME280_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
- return I2C_BME280_Catch($@) if $@;
- }
- }
- sub I2C_BME280_Init($$) { # wird bei FHEM start Define oder wieder
- my ( $hash, $args ) = @_;
- my $name = $hash->{NAME};
-
- if (defined (my $address = shift @$args)) {
- $hash->{I2C_Address} = $address =~ /^0x.*$/ ? oct($address) : $address;
- return "$name: I2C Address not valid" unless ($hash->{I2C_Address} < 128 && $hash->{I2C_Address} > 3);
- } else {
- $hash->{I2C_Address} = BME280_I2C_ADDRESS;
- }
- my $msg = '';
- # create default attributes
- #if (AttrVal($name, 'poll_interval', '?') eq '?') {
- # $msg = CommandAttr(undef, $name . ' poll_interval 5');
- # if ($msg) {
- # Log3 ($hash, 1, $msg);
- # return $msg;
- # }
- #}
- eval {
- AssignIoPort($hash, AttrVal($hash->{NAME},"IODev",undef));
- I2C_BME280_i2cread($hash, 0xD0, 1); #get Id
- $hash->{STATE} = 'getCalData';
- I2C_BME280_i2cread($hash, 0x88, 26);
- I2C_BME280_i2cread($hash, 0xE1, 8);
- };
- return I2C_BME280_Catch($@) if $@;
- }
- sub I2C_BME280_Catch($) { # Fehlermeldungen formattieren
- my $exception = shift;
- if ($exception) {
- $exception =~ /^(.*)( at.*FHEM.*)$/;
- return $1;
- }
- return undef;
- }
- sub I2C_BME280_Attr (@) { # Wird beim Attribut anlegen/aendern aufgerufen
- my ($command, $name, $attr, $val) = @_;
- my $hash = $defs{$name};
- my $msg = '';
- if (defined $command && $command eq "set" && $attr eq "IODev") {
- eval {
- if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) {
- main::AssignIoPort($hash,$val);
- my @def = split (' ',$hash->{DEF});
- I2C_BME280_Init($hash,\@def) if (defined ($hash->{IODev}));
- }
- };
- $msg = I2C_BME280_Catch($@) if $@;
- } elsif ($attr eq 'poll_interval') {
- if (defined($val)) {
- if ($val =~ m/^(0*[1-9][0-9]*)$/) {
- RemoveInternalTimer($hash);
- I2C_BME280_Poll($hash) if ($main::init_done);
- #InternalTimer(gettimeofday() + 5, 'I2C_BME280_Poll', $hash, 0) if ($main::init_done);
- } else {
- $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
- }
- } else {
- RemoveInternalTimer($hash);
- }
- } elsif ($attr =~ m/^oversampling_.$/ && defined($val)) {
- $msg = 'Wrong value: $val for $attr defined. value must be a one of 0,1,2,3,4,5' unless ($val =~ m/^(0*[0-5])$/);
- } elsif ($attr =~ m/^round(Pressure|Temperature|Humidity)Decimal$/ && defined($val)) {
- $msg = 'Wrong value: $val for $attr defined. value must be a one of 0,1,2' unless ($val =~ m/^(0*[0-2])$/);
- }
- return ($msg) ? $msg : undef;
- }
- sub I2C_BME280_Poll($) { # Messwerte regelmaessig anfordern
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- I2C_BME280_Set($hash, ($name, 'readValues')); # Read values
- my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
- if ($pollInterval > 0) {
- InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BME280_Poll', $hash, 0);
- }
- }
- sub I2C_BME280_Set($@) { # Messwerte manuell anfordern
- 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) . ":noArg"
- }
-
- if ($cmd eq 'readValues') {
- if (defined($hash->{calibrationData}{dig_H6})) { # query sensor
- I2C_BME280_i2cwrite($hash, 0xF2, AttrVal($name, 'oversampling_h', 1) & 7);
- my $data = ( AttrVal($name, 'oversampling_t', 1) & 7 ) << 5 | ( AttrVal($name, 'oversampling_p', 1) & 7 ) << 2 | 1; #Register 0xF4 “ctrl_meas” zusammenbasteln
- I2C_BME280_i2cwrite($hash, 0xF4, $data);
- RemoveInternalTimer($hash);
- InternalTimer(gettimeofday() + 1, 'I2C_BME280_UpdateReadings', $hash, 0); #nach 1s Werte auslesen
- } else { #..but get calibration variables first
- Log3 $hash, 5, "$name: in set but no calibrationData, requesting again";
- I2C_BME280_i2cread($hash, 0x88, 26);
- I2C_BME280_i2cread($hash, 0xE1, 8);
- }
- }
- return undef
- }
- sub I2C_BME280_Get($@) { # Messwerte manuell anfordern
- my ($hash, @a) = @_;
- my $name = $a[0];
- my $cmd = $a[1];
- if (defined($cmd) && $cmd eq 'readValues') {
- if (defined($hash->{calibrationData}{dig_H6})) { # query sensor
- I2C_BME280_i2cwrite($hash, 0xF2, AttrVal($name, 'oversampling_h', 1) & 7);
- my $data = ( AttrVal($name, 'oversampling_t', 1) & 7 ) << 5 | ( AttrVal($name, 'oversampling_p', 1) & 7 ) << 2 | 1; #Register 0xF4 “ctrl_meas” zusammenbasteln
- I2C_BME280_i2cwrite($hash, 0xF4, $data);
- RemoveInternalTimer($hash);
- InternalTimer(gettimeofday() + 1, 'I2C_BME280_UpdateReadings', $hash, 0); #nach 1s Werte auslesen
- } else { #..but get calibration variables first
- Log3 $hash, 5, "$name: in set but no calibrationData, requesting again";
- I2C_BME280_i2cread($hash, 0x88, 26);
- I2C_BME280_i2cread($hash, 0xE1, 8);
- }
- } else {
- return 'Unknown argument ' . $cmd . ', choose one of readValues:noArg';
- }
- return undef
- }
- sub I2C_BME280_UpdateReadings($) { # Messwerte auslesen
- my ($hash) = @_;
- I2C_BME280_i2cread($hash, 0xF7, 8); # alle Werte auslesen
- my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); #poll_interval Timer wiederherstellen
- InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_BME280_Poll', $hash, 0) if ($pollInterval > 0);
- }
- sub I2C_BME280_Undef($$) { # Device loeschen
- my ($hash, $arg) = @_;
- RemoveInternalTimer($hash);
- return undef;
- }
- sub I2C_BME280_I2CRec ($$) { # wird vom IODev aus aufgerufen wenn I2C Daten vorliegen
- my ($hash, $clientmsg) = @_;
- my $name = $hash->{NAME};
- my $pname = undef;
- my $phash = $hash->{IODev};
- $pname = $phash->{NAME};
- while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen
- $hash->{$k} = $v if $k =~ /^$pname/ ;
- }
- if ( $clientmsg->{direction} && $clientmsg->{reg} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) {
- if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {
- Log3 $hash, 5, "$name Rx, Reg: $clientmsg->{reg}, Data: $clientmsg->{received}";
- I2C_BME280_GetCal1 ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0x88 && $clientmsg->{nbyte} == 26;
- I2C_BME280_GetCal2 ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xE1 && $clientmsg->{nbyte} >= 8;
- I2C_BME280_GetId ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xD0;
- I2C_BME280_GetReadings ($hash, $clientmsg->{received}) if $clientmsg->{reg} == 0xF7 && $clientmsg->{nbyte} == 8;
- }
- }
-
- return undef
- }
- sub I2C_BME280_GetId ($$) { # empfangenes Id Byte auswerten
- my ($hash, $rawdata) = @_;
- if ($rawdata == hex("60")) {
- $hash->{DeviceType} = "BME280";
- } elsif ($rawdata == hex("58")) {
- $hash->{DeviceType} = "BMP280";
- } if ($rawdata == hex("56") || $rawdata == hex("57")) {
- $hash->{DeviceType} = "BMP280s";
- }
- }
- sub I2C_BME280_GetCal1 ($$) { # empfangene Cal Daten in Internals Speichern
- my ($hash, $rawdata) = @_;
- my @raw = split(" ",$rawdata);
- my $n = 0;
- $hash->{calibrationData}{dig_T1} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++], 0); # unsigned
- $hash->{calibrationData}{dig_T2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_T3} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P1} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++], 0); # unsigned
- $hash->{calibrationData}{dig_P2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P3} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P4} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P5} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P6} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P7} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P8} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_P9} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $n++;
- $hash->{calibrationData}{dig_H1} = $raw[$n++]; # unsigned
- $hash->{STATE} = 'First calibration block received';
- return
- }
- sub I2C_BME280_GetCal2 ($$) { # empfangene Cal Daten in Internals Speichern Teil 2
- my ($hash, $rawdata) = @_;
- my @raw = split(" ",$rawdata);
- my $n = 0;
- $hash->{calibrationData}{dig_H2} = I2C_BME280_GetCalVar($raw[$n++], $raw[$n++]);
- $hash->{calibrationData}{dig_H3} = $raw[$n++]; # unsigned
- $hash->{calibrationData}{dig_H4} = ($raw[$n++] << 4) | $raw[$n] & 0xF; # signed word, kann aber nur positiv sein, da nur 12 bit
- $hash->{calibrationData}{dig_H5} = (($raw[$n++] >> 4) & 0x0F) | ($raw[$n++] << 4); # signed word, kann aber nur positiv sein, da nur 12 bit
- $hash->{calibrationData}{dig_H6} = I2C_BME280_GetCalVar($raw[$n++], undef); # signed 8bit #geht das? oder muss I2C_BME280_GetCalVar ($;$$) angepasst werden
- $hash->{STATE} = 'Initialized';
- I2C_BME280_Poll($hash) if defined(AttrVal($hash->{NAME}, 'poll_interval', undef)); # wenn poll_interval definiert -> timer starten
- return
- }
- sub I2C_BME280_GetCalVar ($$;$) { # Variablen aus Bytes zusammenbauen (signed und unsigned)
- my ($lsb, $msb, $returnSigned) = @_;
- $returnSigned = (!defined($returnSigned) || $returnSigned == 1) ? 1 : 0;
- my $retVal = undef;
- if (defined $msb) { # 16 bit Variable
- $retVal = $msb << 8 | $lsb;
- # check if we need return signed or unsigned int
- if ($returnSigned == 1) {
- $retVal = $retVal >> 15 ? $retVal - 2**16 : $retVal;
- }
- } else { # 8 bit Variable
- $retVal = $lsb >> 7 ? $lsb - 2 ** 8 : $lsb;
- }
- return $retVal;
- }
- sub I2C_BME280_GetReadings ($$) { # Empfangene Messwerte verarbeiten
- my ($hash, $rawdata) = @_;
- my @raw = split(" ",$rawdata);
- my @pres = splice(@raw,0,3);
- my @temp = splice(@raw,0,3);
- I2C_BME280_GetTemp ($hash, @temp );
- I2C_BME280_GetPress ($hash, @pres);
- I2C_BME280_GetHum ($hash, @raw );
-
- my $tem = ReadingsVal($hash->{NAME},"temperature", undef);
- my $hum = ReadingsVal($hash->{NAME},"humidity", undef);
- my $prs = ReadingsVal($hash->{NAME},"pressure", undef);
- readingsSingleUpdate(
- $hash,
- 'state',
- ((defined $tem ? "T: $tem " : "") . (defined $hum ? "H: $hum " : "") . (defined $prs ? ("P: $prs P-NN: " . ReadingsVal($hash->{NAME},"pressure-nn", 0)) : "")),
- 1
- );
- }
- sub I2C_BME280_GetTemp ($@) { # Temperatur Messwerte verarbeiten
- my ($hash, @raw) = @_;
- if ( $raw[0] == 0x80 && $raw[1] == 0 && $raw[2] == 0 ) { # 0x80000 (MSB = 0x80) wird ausgegeben, wenn Temperaturmessung deaktiviert (oversampling_t = 0)
- Log3 $hash, 4, "temperature reading deleted due to oversampling_t = 0";
- delete ($hash->{READINGS}{temperature});
- } else {
- my $ut = $raw[0] << 12 | $raw[1] << 4 | $raw[2] >> 4 ;
- my $temperature = sprintf(
- '%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f',
- I2C_BME280_calcTrueTemperature($hash, $ut)
- );
- readingsSingleUpdate($hash, 'temperature', $temperature, 1);
- }
- }
- sub I2C_BME280_GetPress ($@) { # Luftdruck Messwerte verarbeiten
- my ($hash, @raw) = @_;
- if ( $raw[0] == 0x80 && $raw[1] == 0 && $raw[2] == 0 ) { # 0x80000 (MSB = 0x80) wird ausgegeben, wenn Luftdruckmessung deaktiviert (oversampling_p = 0)
- Log3 $hash, 4, "pressure readings seleted due to oversampling_p = 0";
- delete ($hash->{READINGS}{'pressure'});
- delete ($hash->{READINGS}{'pressure-nn'});
- } else {
- my $up = $raw[0] << 12 | $raw[1] << 4 | $raw[2] >> 4 ;
- my $pressure = sprintf(
- '%.' . AttrVal($hash->{NAME}, 'roundPressureDecimal', 1) . 'f',
- I2C_BME280_calcTruePressure($hash, $up) / 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, 'pressure', $pressure);
- readingsBulkUpdate($hash, 'pressure-nn', $pressureNN);
- readingsEndUpdate($hash, 1);
- }
- }
- sub I2C_BME280_GetHum ($@) { # Luftfeuchte Messwerte verarbeiten
- my ($hash, @raw) = @_;
- if ( $raw[0] == 0x80 && $raw[1] == 0 ) { # 0x8000 (MSB = 0x80) wird ausgegeben, wenn Feuchtemessung deaktiviert (oversampling_h = 0)
- Log3 $hash, 4, "humidity readings seleted due to oversampling_h = 0";
- delete ($hash->{READINGS}{humidity})
- } else {
- my $uh = $raw[0] << 8 | $raw[1];
- my $humidity = sprintf(
- '%.' . AttrVal($hash->{NAME}, 'roundHumidityDecimal', 1) . 'f',
- I2C_BME280_calcTrueHumidity($hash, $uh)
- );
- readingsSingleUpdate($hash, 'humidity', $humidity, 1);
- }
- }
- sub I2C_BME280_i2cread($$$) { # Lesebefehl an Hardware absetzen (antwort kommt in I2C_*****_I2CRec an)
- my ($hash, $reg, $nbyte) = @_;
- if (defined (my $iodev = $hash->{IODev})) {
- Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} read $nbyte Byte from Register $reg";
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cread",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- nbyte => $nbyte
- });
- } else {
- return "no IODev assigned to '$hash->{NAME}'";
- }
- }
- sub I2C_BME280_i2cwrite($$$) { # Schreibbefehl an Hardware absetzen
- my ($hash, $reg, @data) = @_;
- if (defined (my $iodev = $hash->{IODev})) {
- Log3 $hash, 5, "$hash->{NAME}: $hash->{I2C_Address} write " . join (' ',@data) . " to Register $reg";
- CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, {
- direction => "i2cwrite",
- i2caddress => $hash->{I2C_Address},
- reg => $reg,
- data => join (' ',@data),
- });
- } else {
- return "no IODev assigned to '$hash->{NAME}'";
- }
- }
- sub I2C_BME280_calcTrueTemperature($$) { # Temperatur aus Rohwerten berechnen
- my ($hash, $ut) = @_;
- my $dig_T1 = $hash->{calibrationData}{dig_T1};
- my $dig_T2 = $hash->{calibrationData}{dig_T2};
- my $dig_T3 = $hash->{calibrationData}{dig_T3};
-
- my $h1 = ( $ut / 16384.0 - $dig_T1 / 1024.0 ) * $dig_T2;
- my $h2 = ( ( $ut / 131072.0 - $dig_T1 / 8192.0 ) * ( $ut / 131072.0 - $dig_T1 / 8192.0 ) ) * $dig_T3;
- $hash->{calibrationData}{t_fine} = $h1 + $h2;
- my $ct = $hash->{calibrationData}{t_fine} / 5120.0;
-
- return $ct;
- }
- sub I2C_BME280_calcTruePressure($$) { # Luftdruck aus Rohwerten berechnen
- my ($hash, $up) = @_;
- my $t_fine = $hash->{calibrationData}{t_fine};
- my $dig_P1 = $hash->{calibrationData}{dig_P1};
- my $dig_P2 = $hash->{calibrationData}{dig_P2};
- my $dig_P3 = $hash->{calibrationData}{dig_P3};
- my $dig_P4 = $hash->{calibrationData}{dig_P4};
- my $dig_P5 = $hash->{calibrationData}{dig_P5};
- my $dig_P6 = $hash->{calibrationData}{dig_P6};
- my $dig_P7 = $hash->{calibrationData}{dig_P7};
- my $dig_P8 = $hash->{calibrationData}{dig_P8};
- my $dig_P9 = $hash->{calibrationData}{dig_P9};
-
- my $h1 = ($t_fine / 2) - 64000.0;
- my $h2 = $h1 * $h1 * $dig_P6 / 32768;
- $h2 = $h2 + $h1 * $dig_P5 * 2;
- $h2 = $h2 / 4 + $dig_P4 * 65536;
- #$h1 = $dig_P3 * $h1 * $h1 / 524288 + $dig_P2 * $h1 / 524288;
- $h1 = ((($dig_P3 * ((($h1/4.0) * ($h1/4.0)) / 8192)) / 8) + (($dig_P2 * $h1) / 2.0)) / 262144;
- #$h1 = ( 1 + $h1 / 32768) * $dig_P1;
- $h1 = ((32768 + $h1) * $dig_P1) / 32768;
- return 0 if ($h1 == 0);
- my $p = ((1048576 - $up) - ($h2 / 4096)) * 3125;
- if ($p < 0x80000000) {
- $p = ($p * 2) / $h1;
- } else {
- $p = ($p / $h1) * 2 ;
- }
- #$p = ( $p - $h2 / 4096 ) * 6250 / $h1;
- $h1 = ($dig_P9 * ((($p/8.0) * ($p/8.0)) / 8192.0)) / 4096;
- $h2 = (($p/4.0) * $dig_P8) / 8192.0;
- $p = $p + (($h1 + $h2 + $dig_P7) / 16);
- return $p;
- }
- sub I2C_BME280_calcTrueHumidity($$) { # Luftfeuchte aus Rohwerten berechnen
- my ($hash, $uh) = @_;
-
- my $t_fine = $hash->{calibrationData}{t_fine};
- my $dig_H1 = $hash->{calibrationData}{dig_H1};
- my $dig_H2 = $hash->{calibrationData}{dig_H2};
- my $dig_H3 = $hash->{calibrationData}{dig_H3};
- my $dig_H4 = $hash->{calibrationData}{dig_H4};
- my $dig_H5 = $hash->{calibrationData}{dig_H5};
- my $dig_H6 = $hash->{calibrationData}{dig_H6};
-
- my $t1 = $t_fine - 76800;
- $t1 = ( $uh - ( $dig_H4 * 64 + $dig_H5 / 16384 * $t1 ) ) * ( $dig_H2 / 65536 * ( 1 + $dig_H6 / 67108864 * $t1 * ( 1 + $dig_H3 / 67108864 * $t1 ) ) );
- $t1 = $t1 * ( 1 - $dig_H1 * $t1 / 524288);
- if ($t1 > 100) {
- $t1 = 100;
- } elsif ($t1 < 0) {
- $t1 = 0;
- }
- return $t1;
- }
- sub I2C_BME280_DbLog_splitFn($) { # Einheiten
- my ($event) = @_;
- Log3 undef, 5, "in DbLog_splitFn empfangen: $event";
- my ($reading, $value, $unit) = "";
- my @parts = split(/ /,$event);
- $reading = shift @parts;
- $reading =~ tr/://d;
- $value = $parts[0];
- $unit = "\xB0C" if(lc($reading) =~ m/temp/);
- $unit = "hPa" if(lc($reading) =~ m/pres/);
- $unit = "%" if(lc($reading) =~ m/humi/);
- return ($reading, $value, $unit);
- }
- 1;
- =pod
- =item device
- =item summary reads pressure, humidity and temperature from an via I2C connected BME280
- =item summary_DE lese Druck, Feuchte und Temperatur eines über I2C angeschlossenen BME280
- =begin html
- <a name="I2C_BME280"></a>
- <h3>I2C_BME280</h3>
- (en | <a href="commandref_DE.html#I2C_BME280">de</a>)
- <ul>
- <a name="I2C_BME280"></a>
- Provides an interface to the digital pressure/humidity sensor BME280
- The I2C messages are send through an I2C interface module like <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
- or <a href="#NetzerI2C">NetzerI2C</a> so this device must be defined first.<br>
- <b>attribute IODev must be set</b><br>
- <b>Define</b>
- <ul>
- <code>define BME280 I2C_BME280 [<I2C Address>]</code><br><br>
- without defined <code><I2C Address></code> 0x76 will be used as address<br>
- <br>
- Examples:
- <pre>
- define BME280 I2C_BME280 0x77
- attr BME280 poll_interval 5
- </pre>
- </ul>
- <a name="I2C_BME280set"></a>
- <b>Set</b>
- <ul>
- <code>set BME280 <readValues></code>
- <br><br>
- Reads current temperature, humidity and pressure values from the sensor.<br>
- Normaly this execute automaticly at each poll intervall. You can execute
- this manually if you want query the current values.
- <br><br>
- </ul>
- <a name="I2C_BME280attr"></a>
- <b>Attributes</b>
- <ul>
- <li>oversampling_t,oversampling_h,oversampling_p<br>
- Controls the oversampling settings of the temperature,humidity or pressure measurement in the sensor.<br>
- Default: 1, valid values: 0, 1, 2, 3, 4, 5<br>
- 0 switches the respective measurement off<br>
- 1 to 5 complies to oversampling value 2^value/2<br><br>
- </li>
- <li>poll_interval<br>
- Set the polling interval in minutes to query the sensor for new measured
- values.<br>
- Default: 5, valid values: any whole number<br><br>
- </li>
- <li>roundTemperatureDecimal,roundHumidityDecimal,roundPressureDecimal<br>
- Round temperature, humidity or pressure values to given decimal places.<br>
- Default: 1, valid values: 0, 1, 2<br><br>
- </li>
- <li>altitude<br>
- if set, this altitude is used for calculating the pressure related to sea level (nautic null) NN<br><br>
- Note: this is a global attributes, e.g<br>
- <code>attr global altitude 220</code>
- </li>
- <li><a href="#IODev">IODev</a></li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#showtime">showtime</a></li>
- </ul><br>
- </ul>
- =end html
- =begin html_DE
- <a name="I2C_BME280"></a>
- <h3>I2C_BME280</h3>
- (<a href="commandref.html#I2C_BME280">en</a> | de)
- <ul>
- <a name="I2C_BME280"></a>
- Ermöglicht die Verwendung eines digitalen (Luft)druck/feuchtesensors BME280 über den I2C Bus des Raspberry Pi.<br><br>
- I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das <a href="#RPII2C">RPII2C</a>, <a href="#FRM">FRM</a>
- oder <a href="#NetzerI2C">NetzerI2C</a> gesendet. Daher muss dieses vorher definiert werden.<br>
- <b>Das Attribut IODev muss definiert sein.</b><br>
-
- <b>Define</b>
- <ul>
- <code>define BME280 <BME280_name> [<I2C Addresse>]</code><br><br>
- Fehlt <code><I2C Address></code> wird 0x76 verwendet<br>
- <br>
- Beispiel:
- <pre>
- define BME280 I2C_BME280 0x77
- attr BME280 poll_interval 5
- </pre>
- </ul>
- <a name="I2C_BME280set"></a>
- <b>Set</b>
- <ul>
- <code>set BME280 readValues</code>
- <br><br>
- <code>set <name> readValues</code><br>
- Aktuelle Temperatur, Feuchte und Luftdruck Werte vom Sensor lesen.<br><br>
- </ul>
- <a name="I2C_BME280attr"></a>
- <b>Attribute</b>
- <ul>
- <li>oversampling_t,oversampling_h,oversampling_p<br>
- Steuert das jeweils das Oversampling der Temperatur-, Feuchte-, oder Druckmessung im Sensor.<br>
- Standard: 1, gültige Werte: 0, 1, 2, 3, 4, 5<br>
- 0 deaktiviert die jeweilige Messung<br>
- 1 to 5 entspricht einem Oversampling von 2^zahl/2<br><br>
- </li>
- <li>poll_interval<br>
- Definiert das Poll Intervall in Minuten für das Auslesen einer neuen Messung.<br>
- Default: 5, gültige Werte: 1, 2, 5, 10, 20, 30<br><br>
- </li>
- <li>roundTemperatureDecimal, roundHumidityDecimal, roundPressureDecimal<br>
- Rundet jeweils den Temperatur-, Feuchte-, oder Druckwert mit den angegebenen Nachkommastellen.<br>
- Standard: 1, gültige Werte: 0, 1, 2<br><br>
- </li>
- <li>altitude<br>
- Wenn dieser Wert definiert ist, wird diese Angabe zusä für die Berechnung des
- Luftdrucks bezogen auf Meereshöhe (Normalnull) NN herangezogen.<br>
- Bemerkung: Dies ist ein globales Attribut.<br><br>
- <code>attr global altitude 220</code>
- </li>
- <li><a href="#IODev">IODev</a></li>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#showtime">showtime</a></li>
- </ul><br>
- </ul>
- =end html_DE
- =cut
|