| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- #
- #
- # 09_USF1000.pm
- # written by Dr. Boris Neubert 2009-06-20
- # e-mail: omega at online dot de
- #
- ##############################################
- # $Id: 09_USF1000.pm 16375 2018-03-10 15:40:02Z neubert $
- package main;
- use strict;
- use warnings;
- my $PI= 3.141592653589793238;
- my $dev= "a5ce aa";
- #############################
- sub
- USF1000_Initialize($)
- {
- my ($hash) = @_;
- $hash->{Match} = "^81..(04|0c)..0101a001a5ceaa00....";
- $hash->{DefFn} = "USF1000_Define";
- $hash->{UndefFn} = "USF1000_Undef";
- $hash->{ParseFn} = "USF1000_Parse";
- $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:0,1 showtime:0,1 " .
- "model:usf1000s " . $readingFnAttributes;
- }
- #############################
- sub
- USF1000_Define($$)
- {
- my ($hash, $def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- my $u= "wrong syntax: define <name> USF1000 geometry";
- my $g= "wrong geometry for USF1000";
- # geometry (units: meter)
- # cub length width height offset cuboid 3+4
- # cylv diameter height offset vertical cylinder 3+3
- # the offset is measured from the TOP of the box!
- return $u if(int(@a)< 6);
- my $name = $a[0];
- my $geometry = $a[2];
- if($geometry eq "cub") {
- # cuboid
- return $g if(int(@a)< 7);
- $hash->{GEOMETRY}= $geometry;
- $hash->{LENGTH}= $a[3];
- $hash->{WIDTH}= $a[4];
- $hash->{HEIGHT}= $a[5];
- $hash->{OFFSET}= $a[6];
- $hash->{CAPACITY}= int($hash->{LENGTH}*$hash->{WIDTH}*$hash->{HEIGHT}*100.0+0.5)*10.0;
- } elsif($geometry eq "cylv") {
- # vertical cylinder
- return $g if(int(@a)< 6);
- $hash->{GEOMETRY}= $geometry;
- $hash->{DIAMETER}= $a[3];
- $hash->{HEIGHT}= $a[4];
- $hash->{OFFSET}= $a[5];
- $hash->{CAPACITY}= int($PI*$hash->{DIAMETER}*$hash->{DIAMETER}/4.0*$hash->{HEIGHT}*100.0+0.5)*10.0;
- } else {
- return $g;
- }
- $modules{USF1000}{defptr}{$dev} = $hash;
- AssignIoPort($hash);
- }
- #############################
- sub
- USF1000_Undef($$)
- {
- my ($hash, $name) = @_;
- delete($modules{USF1000}{defptr}{$dev});
- return undef;
- }
- #############################
- sub
- USF1000_Parse($$)
- {
- my ($hash, $msg) = @_; # hash points to the FHZ, not to the USF1000
- if(!defined($modules{USF1000}{defptr}{$dev})) {
- Log3 $hash, 3, "USF1000 Unknown device, please define it";
- return "UNDEFINED USF1000 USF1000 cylv 1 1 0.5";
- }
- my $def= $modules{USF1000}{defptr}{$dev};
- my $name= $def->{NAME};
- return "" if(IsIgnored($name));
- my $t= TimeNow();
- # Msg format:
- # 01 23 45 67 8901 2345 6789 01 23 45 67
- # 81 0c 04 .. 0101 a001 a5ce aa 00 cc xx
- my $cc= substr($msg, 24, 2);
- my $xx= substr($msg, 26, 2);
- my $lowbattery= (hex($cc) & 0x40 ? 1 : 0);
- my $testmode= (hex($cc) & 0x80 ? 1 : 0);
- my $distance= hex($xx)/100.0; # in meters
- my $valid= (($distance>0.00) && ($distance<2.55));
- readingsBeginUpdate($def);
- if($valid) {
- my $wlevel = $def->{HEIGHT}-($distance-$def->{OFFSET}); # water level
- my $geometry= $def->{GEOMETRY};
- my $capacity= $def->{CAPACITY}; # capacity of tank (for distance= offset) in liters
- my $volume; # current volume in tank in liters
- my $flevel; # fill level in percent
- if($geometry eq "cub") {
- # cuboid
- $volume = $def->{LENGTH}*$def->{WIDTH}*$wlevel*1000.0;
- } elsif($geometry eq "cylv") {
- # vertical cylinder
- $volume = $PI*$def->{DIAMETER}*$def->{DIAMETER}/4.0*$wlevel*1000.0;
- } else {
- return 0;
- }
- $flevel = int($volume/$capacity*100.0+0.5);
- $volume= int($volume/10.0+0.5)*10.0;
- if($flevel>-5) {
- # reflections may lead to false reading (distance too large)
- # the meaningless results are suppressed
- my $state= sprintf("v: %d V: %d", $flevel, $volume);
- readingsBulkUpdate($def, "state", $state);
- readingsBulkUpdate($def, "distance", $distance);
- readingsBulkUpdate($def, "level", $flevel);
- readingsBulkUpdate($def, "volume", $volume);
-
- #Debug "USF1000 $name: $state";
- }
- }
- my $warnings= ($lowbattery ? "Battery low" : "");
- if($testmode) {
- $warnings.= "; " if($warnings);
- $warnings.= "Test mode";
- }
- $warnings= $warnings ? $warnings : "none";
- readingsBulkUpdate($def, "warnings", $warnings);
- readingsEndUpdate($def, 1);
- return $name;
- }
- #############################
- 1;
- =pod
- =item summary USF1000S ultrasonic level transmitter communicating over FHZ
- =item summary_DE USF1000S Ultraschall-Füllstandsmesser angebunden über FHZ
- =begin html
- <a name="USF1000"></a>
- <h3>USF1000</h3>
- <ul>
- Fhem can receive your tank's fill level from the USF1000S device
- through a <a href="#FHZ">FHZ</a> device, so one must be defined first.
- The state contains the fill level in % (lower case v in the device state)
- and the current volume in liters (upper case V in the device state).
- Measured distance to the liquid's surface, fill level, volume and warnings
- (Test mode, Battery low) are available. Due to the design of the USF1000S
- protocol, you can have only one USF1000S in range of your FHZ as these
- devices cannot be distinguished.<br>
- <br>
- <a name="USF1000Define"></a>
- <b>Define</b>
- <ul>
- <code>define <name> USF1000 <geometry></code>
- <br><br>
- <code><geometry></code> determines the form of the tank and the
- position of the sensor. The following geometries are currently
- supported:<br><br>
- <ul>
- <li><code>cub <length> <width> <height> <offset></code></li>
- <li><code>cylv <diameter> <height> <offset></code></li>
- </ul>
- <br>
- <code>cub</code> stands for a cuboid whose base is <length> × <width>.
- <code>cylv</code> stands for a vertical cylinder whose diameter is <diameter>.
- <height> is the distance of the surface of the liquid from the ground
- if the tank is full. <offset> is the distance of the sensor relative to
- the surface of the liquid. All quantities are expressed in meters.<br>
- <br>
- Example:<br>
- <ul>
- <code>define MyTank USF1000 cylv 2 1 0.3</code>: a cylindrical water tank with
- 2 meters diameter. The water stands 1 meter high if the tank is full. The
- sensor is fixed 1,3 meters above ground.<br>
- </ul>
- </ul>
- <br>
- <a name="USF1000set"></a>
- <b>Set </b>
- <ul>
- N/A
- </ul>
- <br>
- <a name="USF1000get"></a>
- <b>Get</b>
- <ul>
- N/A
- </ul>
- <br>
- <a name="USF1000attr"></a>
- <b>Attributes</b>
- <ul>
- <li><a href="#IODev">IODev</a></li><br>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#showtime">showtime</a></li>
- <li><a href="#model">model</a> (usf1000s)</li>
- <li><a href="#ignore">ignore</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- </ul>
- <br>
- </ul>
- =end html
- =cut
|