diff --git a/fhem/CHANGED b/fhem/CHANGED index 481c87e0d..626c79a84 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: new module added: 52_I2C_SHT3x.pm (macs) - bugfix: 70_Jabber: log OTR empty message if debug-mode == 1 only - changed: 70_BRAVIA: re-worked state handling - feature: PRESENCE: new attribute presenceThreshold to define a number of diff --git a/fhem/FHEM/52_I2C_SHT3x.pm b/fhem/FHEM/52_I2C_SHT3x.pm new file mode 100644 index 000000000..5e3f159dd --- /dev/null +++ b/fhem/FHEM/52_I2C_SHT3x.pm @@ -0,0 +1,406 @@ +############################################## +# $Id$ +# +# 52_I2C_SHT3x.pm +# +# i2c sensor for humidity and temperature +# +# Basis for this module is "52_I2C_SHT21.pm" from klausw. I adapted the module so that it +# is suitable for the SHT3x family from Sensirion / Switzerland. At moment the SHT3x family +# consists of 3 sensors: +# SHT30 (Low-Cost): sensor with less accuracy +# SHT31 (Standard): sensor with good accuracy +# SHT35 (High-End): sensor with best accuracy +# +# Via hardware pin configuration the sensor can be configured for 2 different i2c addresses. +# +# +# If you have any questions, suggestions or like to report a failure, please feel free to cantact me: +# FHEM Forum username: macs +# +############################################## + +package main; + +use strict; +use warnings; + +use constant { + # 0x44 (default): ADDR (pin 2) connected to VSS (supply voltage) + # 0x45 : ADDR (pin 2) connected to VDD (ground) + SHT3x_I2C_ADDRESS => '0x44', +}; + +################################################## +# Forward declarations +# +sub I2C_SHT3x_Initialize($); +sub I2C_SHT3x_Define($$); +sub I2C_SHT3x_Attr(@); +sub I2C_SHT3x_Poll($); +sub I2C_SHT3x_Set($@); +sub I2C_SHT3x_Undef($$); +sub I2C_SHT3x_DbLog_splitFn($); + +my %sets = ( + 'readValues' => 1, +); + +sub I2C_SHT3x_Initialize($) { + my ($hash) = @_; + + $hash->{DefFn} = 'I2C_SHT3x_Define'; + $hash->{InitFn} = 'I2C_SHT3x_Init'; + $hash->{AttrFn} = 'I2C_SHT3x_Attr'; + $hash->{SetFn} = 'I2C_SHT3x_Set'; + $hash->{UndefFn} = 'I2C_SHT3x_Undef'; + $hash->{I2CRecFn} = 'I2C_SHT3x_I2CRec'; + $hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 poll_interval:1,2,5,10,20,30 ' . + 'roundHumidityDecimal:0,1,2 roundTemperatureDecimal:0,1,2 ' . + $readingFnAttributes; + $hash->{DbLog_splitFn} = "I2C_SHT3x_DbLog_splitFn"; +} + +sub I2C_SHT3x_Define($$) { + my ($hash, $def) = @_; + my @a = split('[ \t][ \t]*', $def); + + $hash->{STATE} = "defined"; + + if ($main::init_done) { + eval { I2C_SHT3x_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; + return I2C_SHT3x_Catch($@) if $@; + } + return undef; +} + +sub I2C_SHT3x_Init($$) { + my ( $hash, $args ) = @_; + + my $name = $hash->{NAME}; + + if (defined $args && int(@$args) > 1) + { + return "Define: Wrong syntax. Usage:\n" . + "define I2C_SHT3x []"; + } + + if (defined (my $address = shift @$args)) { + $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; + return "$name I2C Address not valid" unless (($hash->{I2C_Address} < 128) && ($hash->{I2C_Address} > 3)); + } else { + $hash->{I2C_Address} = hex(SHT3x_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; + } + } + AssignIoPort($hash); + $hash->{STATE} = 'Initialized'; + + return undef; +} + +sub I2C_SHT3x_Catch($) { + my $exception = shift; + if ($exception) { + $exception =~ /^(.*)( at.*FHEM.*)$/; + return $1; + } + return undef; +} + +sub I2C_SHT3x_Attr (@) {# hier noch Werteueberpruefung einfuegen + my ($command, $name, $attr, $val) = @_; + my $hash = $defs{$name}; + my $msg = ''; + if ($command && $command eq "set" && $attr && $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_SHT3x_Init($hash,\@def) if (defined ($hash->{IODev})); + } + }; + return I2C_SHT3x_Catch($@) if $@; + } + if ($attr eq 'poll_interval') { + if ($val > 0) { + RemoveInternalTimer($hash); + InternalTimer(gettimeofday() + 5, 'I2C_SHT3x_Poll', $hash, 0); + } else { + $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; + } + } elsif ($attr eq 'roundHumidityDecimal') { + $msg = 'Wrong $attr defined. Use one of 0, 1, 2' if defined($val) && $val <= 0 && $val >= 2 ; + } elsif ($attr eq 'roundTemperatureDecimal') { + $msg = 'Wrong $attr defined. Use one of 0, 1, 2' if defined($val) && $val <= 0 && $val >= 2 ; + } + return ($msg) ? $msg : undef; +} + +sub I2C_SHT3x_Poll($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + # Read values + I2C_SHT3x_Set($hash, ($name, 'readValues')); + + my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); + if ($pollInterval > 0) { + InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_SHT3x_Poll', $hash, 0); + } +} + +sub I2C_SHT3x_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') { + I2C_SHT3x_triggerTempHum($hash); + } +} + +sub I2C_SHT3x_Undef($$) { + my ($hash, $arg) = @_; + + RemoveInternalTimer($hash); + return undef; +} + +sub I2C_SHT3x_I2CRec ($$) { + my ($hash, $clientmsg) = @_; + my $name = $hash->{NAME}; + my $phash = $hash->{IODev}; + my $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/ ; + } + + # i2c data received? + if ( $clientmsg->{direction} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok" ) { + if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) { + Log3 $hash, 5, "empfangen: $clientmsg->{received}"; + my @raw = split(" ",$clientmsg->{received}); + I2C_SHT3x_GetTempHum ($hash, $clientmsg->{received}) if $clientmsg->{nbyte} == 6; + } + } +} + +sub I2C_SHT3x_GetTempHum ($$) { + my ($hash, $rawdata) = @_; + my @raw = split(" ",$rawdata); + + if ( defined (my $crc = I2C_SHT3x_CheckCrc(@raw[0..2])) ) { #CRC Test + Log3 $hash, 2, "CRC error temperature data(Temp_MSB Temp_LSB Temp_Chechsum Hum_MSB Hum_LSB Hum_Chechsum): $rawdata, Checksum calculated: $crc"; + $hash->{CRCErrorTemperature}++; + + return; + } + + if ( defined (my $crc = I2C_SHT3x_CheckCrc(@raw[3..5])) ) { #CRC Test + Log3 $hash, 2, "CRC error humidity data(Temp_MSB Temp_LSB Temp_Chechsum Hum_MSB Hum_LSB Hum_Chechsum): $rawdata, Checksum calculated: $crc"; + $hash->{CRCErrorHumidity}++; + + return; + } + + + my $name = $hash->{NAME}; + + my $temperature = ($raw[0] << 8) | $raw[1]; + $temperature = ( 175.0 * ($temperature / ((2**16)-1.0) )) - 45.0; + $temperature = sprintf( + '%.' . AttrVal($hash->{NAME}, 'roundTemperatureDecimal', 1) . 'f', + $temperature + ); + + my $humidity = ($raw[3] << 8) | $raw[4]; + $humidity = 100.0 * ($humidity / ((2**16)-1.0) ); + $humidity = sprintf( + '%.' . AttrVal($hash->{NAME}, 'roundHumidityDecimal', 1) . 'f', + $humidity + ); + + readingsBeginUpdate($hash); + readingsBulkUpdate( + $hash, + 'state', + 'T: ' . $temperature . ' H: ' . $humidity + ); + readingsBulkUpdate($hash, 'temperature', $temperature); + readingsBulkUpdate($hash, 'humidity', $humidity); + readingsEndUpdate($hash, 1); +} + +sub I2C_SHT3x_triggerTempHum($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + return "$name: no IO device defined" unless ($hash->{IODev}); + my $phash = $hash->{IODev}; + my $pname = $phash->{NAME}; + + # Write decimal 36 00 to device. This requests a "Repeatability = High, Clock stretching = disabled" temperature and humidity reading + my $i2creq = { i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" }; + $i2creq->{data} = "36 00"; + CallFn($pname, "I2CWrtFn", $phash, $i2creq); + RemoveInternalTimer($hash); + InternalTimer(gettimeofday() + 1, 'I2C_SHT3x_readValue', $hash, 0); #nach 1s Wert lesen + return; +} + +sub I2C_SHT3x_readValue($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + return "$name: no IO device defined" unless ($hash->{IODev}); + my $phash = $hash->{IODev}; + my $pname = $phash->{NAME}; + + # Reset Internal Timer to Poll Sub + RemoveInternalTimer($hash); + my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); + InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_SHT3x_Poll', $hash, 0) if ($pollInterval > 0); + # Read 6 byte: Temp MSB, Temp LSB, CRC, Hum MSB, Hum LSB, CRC + my $i2cread = { i2caddress => $hash->{I2C_Address}, direction => "i2cread" }; + $i2cread->{nbyte} = 6; + CallFn($pname, "I2CWrtFn", $phash, $i2cread); + + return; +} + +sub I2C_SHT3x_CheckCrc(@) { + my @data = @_; + my $crc = 0xFF; + my $poly = 0x131; #P(x)=x^8+x^5+x^4+1 = 100110001 + for (my $n = 0; $n < (scalar(@data) - 1); ++$n) { + $crc ^= $data[$n]; + for (my $bit = 8; $bit > 0; --$bit) { + $crc = ($crc & 0x80 ? $poly : 0 ) ^ ($crc << 1); + } + } + + return ($crc == $data[scalar(@data)-1] ? undef : $crc); +} + +sub I2C_SHT3x_DbLog_splitFn($) { + 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 = "%" if(lc($reading) =~ m/humi/); + return ($reading, $value, $unit); +} + +1; + +=pod +=begin html + + +

I2C_SHT3x

+(en | de) + + +=end html + +=begin html_DE + + +

I2C_SHT3x

+(en | de) + + +=end html_DE + +=cut \ No newline at end of file diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 362faf980..8094ab08d 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -213,6 +213,7 @@ FHEM/52_I2C_PCA9532 klausw http://forum.fhem.de Sonstige FHEM/52_I2C_PCA9685 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_PCF8574 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_SHT21 klausw http://forum.fhem.de Sonstige Systeme +FHEM/52_I2C_SHT3x macs http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_TSL2561 jensb http://forum.fhem.de Sonstige Systeme FHEM/53_GHoma.pm klausw http://forum.fhem.de Sonstige Systeme FHEM/55_InfoPanel.pm betateilchen http://forum.fhem.de Unterstuetzende Dienste