# # # 81_M232Counter.pm # written by Dr. Boris Neubert 2007-11-26 # e-mail: omega at online dot de # ############################################## # $Id$ package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); sub M232Counter_Get($@); sub M232Counter_Set($@); sub M232Counter_SetBasis($@); sub M232Counter_Define($$); sub M232Counter_GetStatus($); ################################### sub M232Counter_Initialize($) { my ($hash) = @_; $hash->{GetFn} = "M232Counter_Get"; $hash->{SetFn} = "M232Counter_Set"; $hash->{DefFn} = "M232Counter_Define"; $hash->{AttrList} = "dummy:1,0 model:M232Counter"; } ################################### sub M232Counter_GetStatus($) { my ($hash) = @_; if(!$hash->{LOCAL}) { InternalTimer(gettimeofday()+$hash->{INTERVAL}, "M232Counter_GetStatus", $hash, 1); } my $name = $hash->{NAME}; my $r= $hash->{READINGS}; my $d = IOWrite($hash, "z"); if(!defined($d)) { my $msg = "M232Counter $name tick count read error"; Log3 $name, 2, $msg; return $msg; } # time my $tn = TimeNow(); #tsecs my $tsecs= time(); # number of non-leap seconds since January 1, 1970, UTC # previous tsecs my $tsecs_prev; if(defined($r->{tsecs})) { $tsecs_prev= $r->{tsecs}{VAL}; } else{ $tsecs_prev= $tsecs; # 1970-01-01 } # basis my $basis; if(defined($r->{basis})) { $basis= $r->{basis}{VAL}; } else { $basis= 0; } my $basis_prev= $basis; # previous count (this variable is currently unused) my $count_prev; if(defined($r->{count})) { $count_prev= $r->{count}{VAL}; } else { $count_prev= 0; } # current count my $count= hex $d; # If the counter reaches 65536, the counter does not wrap around but # stops at 0. We therefore purposefully reset the counter to 0 before # it reaches its final tick count. if($count > 64000) { $basis+= $count; $count= 0; $r->{basis}{VAL} = $basis; $r->{basis}{TIME}= $tn; my $ret = IOWrite($hash, "Z1"); if(!defined($ret)) { my $msg = "M232Counter $name reset error"; Log3 $name, 2, $msg; return $msg; } } # previous value my $value_prev; if(defined($r->{value})) { $value_prev= $r->{value}{VAL}; } else { $value_prev= 0; } # current value my $value= ($basis+$count) * $hash->{FACTOR}; # round to 3 digits $value= int($value*1000.0+0.5)/1000.0; # set new values $r->{count}{TIME} = $tn; $r->{count}{VAL} = $count; $r->{value}{TIME} = $tn; $r->{value}{VAL} = $value; $r->{tsecs}{TIME} = $tn; $r->{tsecs}{VAL} = $tsecs; $hash->{CHANGED}[0]= "count: $count"; $hash->{CHANGED}[1]= "value: $value"; # delta my $tsecs_delta= $tsecs-$tsecs_prev; my $count_delta= ($count+$basis)-($count_prev+$basis_prev); if($tsecs_delta>0) { my $delta= ($count_delta/$tsecs_delta)*$hash->{DELTAFACTOR}; # round to 3 digits $delta= int($delta*1000.0+0.5)/1000.0; $r->{delta}{TIME} = $tn; $r->{delta}{VAL} = $delta; $hash->{CHANGED}[2]= "delta: $delta"; } if(!$hash->{LOCAL}) { DoTrigger($name, undef) if($init_done); } $hash->{STATE} = $value; Log3 $name, 4, "M232Counter $name: $value $hash->{UNIT}"; return $hash->{STATE}; } ################################### sub M232Counter_Get($@) { my ($hash, @a) = @_; return "argument is missing" if(int(@a) != 2); my $msg; if($a[1] ne "status") { return "unknown get value, valid is status"; } $hash->{LOCAL} = 1; my $v = M232Counter_GetStatus($hash); delete $hash->{LOCAL}; return "$a[0] $a[1] => $v"; } ############################# sub M232Counter_Calibrate($@) { my ($hash, $value) = @_; my $rm= undef; my $name = $hash->{NAME}; # adjust basis my $tn = TimeNow(); $hash->{READINGS}{basis}{VAL}= $value / $hash->{FACTOR}; $hash->{READINGS}{basis}{TIME}= $tn; $hash->{READINGS}{count}{VAL}= 0; $hash->{READINGS}{count}{TIME}= $tn; # recalculate value $hash->{READINGS}{value}{VAL} = $value; $hash->{READINGS}{value}{TIME} = $tn; # reset counter my $ret = IOWrite($hash, "Z1"); if(!defined($ret)) { my $rm = "M232Counter $name read error"; Log3 $name, 2, $rm; } return $rm; } ############################# sub M232Counter_Set($@) { my ($hash, @a) = @_; my $u = "Usage: set value \n" . "set interval \n" ; return $u if(int(@a) != 3); my $reading= $a[1]; if($a[1] eq "value") { my $value= $a[2]; my $rm= M232Counter_Calibrate($hash, $value); } elsif($a[1] eq "interval") { my $interval= $a[2]; $hash->{INTERVAL}= $interval; } else { return $u; } return undef; } ############################# sub M232Counter_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); return "syntax: define M232Counter [unit] [factor] [deltaunit] [deltafactor]" if(int(@a) < 2 && int(@a) > 6); my $unit= ((int(@a) > 2) ? $a[2] : "ticks"); my $factor= ((int(@a) > 3) ? $a[3] : 1.0); my $deltaunit= ((int(@a) > 4) ? $a[4] : "ticks per second"); my $deltafactor= ((int(@a) > 5) ? $a[5] : 1.0); $hash->{UNIT}= $unit; $hash->{FACTOR}= $factor; $hash->{DELTAUNIT}= $deltaunit; $hash->{DELTAFACTOR}= $deltafactor; $hash->{INTERVAL}= 60; # poll every minute per default AssignIoPort($hash); if(!$hash->{LOCAL}) { InternalTimer(gettimeofday()+60, "M232Counter_GetStatus", $hash, 0); } return undef; } 1; =pod =item summary digital input of a ELV M232 module =item summary_DE digitaler Eingang eines ELV-M232-Moduls =begin html

M232Counter

    Define
      define <name> M232Counter [unit [factor [deltaunit [deltafactor]]]]

      Define at most one M232Counter for a M232 device. Defining a M232Counter will schedule an internal task, which periodically reads the status of the counter, and triggers notify/filelog commands. unit is the unit name, factor is used to calculate the reading of the counter from the number of ticks. deltaunit is the unit name of the counter differential per second, deltafactor is used to calculate the counter differential per second from the number of ticks per second.

      Default values:
      • unit: ticks
      • factor: 1.0
      • deltaunit: ticks per second
      • deltafactor: 1.0

      Note: the parameters in square brackets are optional. If you wish to specify an optional parameter, all preceding parameters must be specified as well.

      Examples:
        define counter M232Counter turns
        define counter M232Counter kWh 0.0008 kW 2.88 (one tick equals 1/1250th kWh)

      Do not forget to start the counter (with set .. start for M232) or to start the counter and set the reading to a specified value (with set ... value for M232Counter).

      To avoid issues with the tick count reaching the end point, the device's internal counter is automatically reset to 0 when the tick count is 64,000 or above and the reading basis is adjusted accordingly.

    Set
      set <name> value <value>

      Sets the reading of the counter to the given value. The counter is reset and started and the offset is adjusted to value/unit.

      set <name> interval <interval>

      Sets the status polling interval in seconds to the given value. The default is 60 seconds.

    Get
      get <name> status

      Gets the reading of the counter multiplied by the factor from the define statement. Wraparounds of the counter are accounted for by an offset (see reading basis in the output of the list statement for the device).

    Attributes
=end html =cut