diff --git a/fhem/contrib/93_PWMR.pm b/fhem/contrib/93_PWMR.pm index 5e37e62a1..458c16f4f 100644 --- a/fhem/contrib/93_PWMR.pm +++ b/fhem/contrib/93_PWMR.pm @@ -28,6 +28,7 @@ # 26.01.16 GA add implementation of PID regulation # 27.01.16 GA add attribute desiredTempFrom to take desiredTemp from another object # 04.02.16 GA add DLookBackCnt, buffer holding previouse temperatures used for PID D-Part calculation +# 08.02.16 GA add ILookBackCnt, buffer holding previouse temperatures used for PID I-Part calculation # module for PWM (Pulse Width Modulation) calculation @@ -36,7 +37,7 @@ # reference to the PWM object is via IODev # PWMR object defines: # IODev: reference to PWM -# factor (also used in PID calculation): +# factor (also used in Pulse calculation): # temperatur difference * factor * cycletime (from PWM) defines on/off periods (pulse) # sensor delivering the temperature (temperature is read from reading using a regexp) # actor to switch on/off the heating devices (may be a structure if more than on actor..) @@ -463,7 +464,7 @@ PWMR_Define($$) my $name = $hash->{NAME}; - return "syntax: define PWMR [:] [[,][:]] [::::]" + return "syntax: define PWMR [:] [[,][:]] [::[,]:[,]]" if(int(@a) < 6 || int(@a) > 9); my $iodevname = $a[2]; @@ -542,17 +543,25 @@ PWMR_Define($$) ########## # check pid definition - my ($usePID, $PFactor, $IFactor, $DFactor, $DLookBackCnt) = split (":", $pid, 5); + my ($usePID, $PFactor, $IFactorTemp, $DFactorTemp) = split (":", $pid, 5); + + $IFactorTemp = "0,1" unless (defined ($IFactorTemp)); + $DFactorTemp = "0,1"unless (defined ($DFactorTemp)); + + my ($IFactor, $ILookBackCnt) = split (",", $IFactorTemp, 2); + my ($DFactor, $DLookBackCnt) = split (",", $DFactorTemp, 2); $hash->{c_PID_useit} = !defined($usePID) ? -1 : $usePID; $hash->{c_PID_PFactor} = !defined($PFactor) ? 0 : $PFactor; $hash->{c_PID_IFactor} = !defined($IFactor) ? 0 : $IFactor; $hash->{c_PID_DFactor} = !defined($DFactor) ? 0 : $DFactor; + + $hash->{c_PID_ILookBackCnt} = !defined($ILookBackCnt) ? 3 : $ILookBackCnt; $hash->{c_PID_DLookBackCnt} = !defined($DLookBackCnt) ? 1 : $DLookBackCnt; $hash->{h_deltaTemp} = 0 unless defined ($hash->{h_deltaTemp}); - $hash->{h_deltaTemp_prev} = 0 unless defined ($hash->{h_deltaTemp_prev}); - $hash->{h_pid_integrator} = 0; + $hash->{h_deltaTemp_D} = 0 unless defined ($hash->{h_deltaTemp_D}); + #$hash->{h_pid_integrator} = 0; if ($pid eq "") { @@ -562,46 +571,68 @@ PWMR_Define($$) delete ($hash->{READINGS}{PID_PWMPulse}) if (defined($hash->{READINGS}{PID_PWMPulse})); delete ($hash->{READINGS}{PID_PWMOnTime}) if (defined($hash->{READINGS}{PID_PWMOnTime})); - delete ($hash->{helper}{PID_previousTemps}) if (defined (($hash->{helper}{PID_previousTemps}))); + delete ($hash->{helper}{PID_D_previousTemps}) if (defined (($hash->{helper}{PID_D_previousTemps}))); #delete ($hash->{h_deltaTemp}) if (defined($hash->{h_deltaTemp})); #delete ($hash->{h_pid_integrator}) if (defined($hash->{h_pid_integrator})); } else { + ### I-Factor + # initialize if not yet done - $hash->{helper}{PID_previousTemps} = [] unless defined (($hash->{helper}{PID_previousTemps})); + $hash->{helper}{PID_I_previousTemps} = [] unless defined (($hash->{helper}{PID_I_previousTemps})); # shorter reference to array - my $TBuffer = $hash->{helper}{PID_previousTemps}; - my $cnt = ( @{$TBuffer} ); # or scalar @{$TBuffer} + my $IBuffer = $hash->{helper}{PID_I_previousTemps}; + my $Icnt = ( @{$IBuffer} ); # or scalar @{$IBuffer} # reference - Log3 ($hash, 3, "org reference is $hash->{helper}{PID_previousTemps} short is $TBuffer, cnt is ". scalar @{$TBuffer}." (starting from 0)"); - Log3 ($hash, 3, "content of TBuffer is @{$TBuffer}"); + #Log3 ($hash, 3, "org reference IBuffer is $hash->{helper}{PID_I_previousTemps} short is $IBuffer, cnt is ". scalar @{$IBuffer}." (starting from 0)"); + Log3 ($hash, 4, "content of IBuffer is @{$IBuffer}"); + + # cut Buffer if it is too large + while (scalar @{$IBuffer} > $hash->{c_PID_DLookBackCnt}) { + my $v = shift @{$IBuffer}; +# Log3 ($hash, 3, "shift $v from IBuffer"); + } +# Log3 ($hash, 3, "IBuffer contains ".scalar @{$IBuffer}." elements"); + + ### D-Factor + + # initialize if not yet done + $hash->{helper}{PID_D_previousTemps} = [] unless defined (($hash->{helper}{PID_D_previousTemps})); + + # shorter reference to array + my $DBuffer = $hash->{helper}{PID_D_previousTemps}; + my $Dcnt = ( @{$DBuffer} ); # or scalar @{$DBuffer} + + # reference + #Log3 ($hash, 3, "org reference DBuffer is $hash->{helper}{PID_D_previousTemps} short is $DBuffer, cnt is ". scalar @{$DBuffer}." (starting from 0)"); + Log3 ($hash, 4, "content of DBuffer is @{$DBuffer}"); # for my $i ( 0 .. $cnt -1 ) { -# Log3 ($hash, 3, "value $i $TBuffer->[$i]"); +# Log3 ($hash, 3, "value $i $DBuffer->[$i]"); # } # -# push @{$TBuffer}, $TBuffer->[1] + $TBuffer->[0]; +# push @{$DBuffer}, $DBuffer->[1] + $DBuffer->[0]; # -# for my $i ( 0 .. @{$TBuffer} -1 ) { -# Log3 ($hash, 3, "value after push $i $TBuffer->[$i]"); +# for my $i ( 0 .. @{$DBuffer} -1 ) { +# Log3 ($hash, 3, "value after push $i $DBuffer->[$i]"); # } # -# shift @{$TBuffer}; +# shift @{$DBuffer}; # -# for my $i ( 0 .. @{$TBuffer} -1 ) { -# Log3 ($hash, 3, "value after shift $i $TBuffer->[$i]"); +# for my $i ( 0 .. @{$DBuffer} -1 ) { +# Log3 ($hash, 3, "value after shift $i $DBuffer->[$i]"); # } # cut Buffer if it is too large - while (scalar @{$TBuffer} > $hash->{c_PID_DLookBackCnt}) { - my $v = shift @{$TBuffer}; -# Log3 ($hash, 3, "shift $v from TBuffer"); + while (scalar @{$DBuffer} > $hash->{c_PID_DLookBackCnt}) { + my $v = shift @{$DBuffer}; +# Log3 ($hash, 3, "shift $v from DBuffer"); } -# Log3 ($hash, 3, "TBuffer contains ".scalar @{$TBuffer}." elements"); +# Log3 ($hash, 3, "DBuffer contains ".scalar @{$DBuffer}." elements"); } @@ -879,25 +910,49 @@ PWMR_ReadRoom(@) $PWMOnTime = "00:00"; } - # PID calculation - my $TBuffer = $room->{helper}{PID_previousTemps}; - push @{$TBuffer}, $temperaturV; + ### PID calculation + + my $DBuffer = $room->{helper}{PID_D_previousTemps}; + push @{$DBuffer}, $temperaturV; + + my $IBuffer = $room->{helper}{PID_I_previousTemps}; + push @{$IBuffer}, $temperaturV; + + # cut I-Buffer if it is too large + while (scalar @{$IBuffer} > $room->{c_PID_ILookBackCnt}) { + my $v = shift @{$IBuffer}; + #Log3 ($room, 3, "shift $v from IBuffer"); + } + #Log3 ($room, 3, "IBuffer contains ".scalar @{$IBuffer}." elements"); + + # cut D-Buffer if it is too large + while (scalar @{$DBuffer} > $room->{c_PID_DLookBackCnt}) { + my $v = shift @{$DBuffer}; + #Log3 ($room, 3, "shift $v from DBuffer"); + } + #Log3 ($room, 3, "DBuffer contains ".scalar @{$DBuffer}." elements"); + + $room->{h_PID_I_previousTemps} = join (" ", @{$IBuffer}); + $room->{h_PID_D_previousTemps} = join (" ", @{$DBuffer}); + my $deltaTempPID = $desiredTemp - $temperaturV; - $room->{h_deltaTemp} = sprintf ("%.1f", -1 * $deltaTempPID); - $room->{h_deltaTemp_prev} = sprintf ("%.1f", -1 * ($desiredTemp - $TBuffer->[0])); - - # cut Buffer if it is too large - while (scalar @{$TBuffer} > $room->{c_PID_DLookBackCnt}) { - my $v = shift @{$TBuffer}; - #Log3 ($room, 3, "shift $v from TBuffer"); - } - #Log3 ($room, 3, "TBuffer contains ".scalar @{$TBuffer}." elements"); - $room->{h_PID_previousTemps} = join (" ", @{$TBuffer}); + $room->{h_deltaTemp} = sprintf ("%.1f", -1 * $deltaTempPID); + $room->{h_deltaTemp_D} = sprintf ("%.1f", -1 * ($desiredTemp - $DBuffer->[0])); + my $ISum = 0; + foreach my $t (@{$IBuffer}) { + $ISum += ($desiredTemp - $t); + } + #$ISum = $ISum / scalar @{$IBuffer}; + $ISum = $ISum; + + #my $deltaTempPID = $desiredTemp - $temperaturV; + #my $IVal = $room->{c_PID_IFactor} * $deltaTempPID + $room->{h_pid_integrator}; + my $PVal = $room->{c_PID_PFactor} * $deltaTemp; - my $IVal = $room->{c_PID_IFactor} * $deltaTempPID + $room->{h_pid_integrator}; - my $DVal = $room->{c_PID_DFactor} * ($room->{h_deltaTemp_prev} - $room->{h_deltaTemp}); + my $IVal = $room->{c_PID_IFactor} * $ISum; + my $DVal = $room->{c_PID_DFactor} * ($room->{h_deltaTemp_D} - $room->{h_deltaTemp}); $PVal = minNum (1, sprintf ("%.2f", $PVal)); $IVal = minNum (1, sprintf ("%.2f", $IVal)); @@ -905,7 +960,7 @@ PWMR_ReadRoom(@) $IVal = maxNum (-1, $IVal); - $room->{h_pid_integrator} = $IVal; + #$room->{h_pid_integrator} = $IVal; my $newpulsePID = ($PVal + $IVal + $DVal); $newpulsePID = minNum ($MaxPulse, sprintf ("%.2f", $newpulsePID)); @@ -1277,7 +1332,7 @@ PWMR_Boost(@) Define
    - define <name> PWMR <IODev> <factor[,offset]> <tsensor[:reading:[t_regexp]]> <actor>[:<a_regexp_on>] [<window|dummy>[,<window>[:<w_regexp>]] [<usePID 0|1>:<PFactor>:<IFactor>:<DFactor>:<DLookBackCnt>]
    + define <name> PWMR <IODev> <factor[,offset]> <tsensor[:reading:[t_regexp]]> <actor>[:<a_regexp_on>] [<window|dummy>[,<window>[:<w_regexp>]] [<usePID 0|1>:<PFactor>:<IFactor>[,<ILookBackCnt>]:<DFactor>,[<DLookBackCnt>]

    Define a calculation object with the following parameters:
    @@ -1310,14 +1365,15 @@ PWMR_Boost(@) w_regexp defines a regular expression to be applied to the reading. Default is '.*Open.*'.
    -
  • <usePID 0|1>:<PFactor>:<IFactor>:<DFactor>:<DLookBackCnt>
    +
  • <usePID 0|1>:<PFactor>:<IFactor>[,<ILookBackCnt>]:<DFactor>[,<DLookBackCnt>]
    usePID 0|1: 0 .. calculate Pulse based on PID but do not use it. 1 .. calculate Pulse based on PID and use it.
    PFactor: Konstant for P.
    IFactor: Konstant for I.
    DFactor: Konstant for D.
    + ILookBackCnt: Buffer size to store previous temperatures. For I calculation all values will be used. Default is 3.
    DLookBackCnt: Buffer size to store previous temperatures. For D calculation actual and oldest temperature will be used. Default is 1.
    Internals c_PID_PFactor, c_PID_IFactor, c_PID_DFactor and c_PID_useit will reflect the above configuration values.
    - Internals h_deltaTemp h_deltaTemp_prev, h_pid_integrator will store the values needed for calculation of the next PID value.
    + Internals h_deltaTemp h_deltaTemp_D store the values needed for calculation of the next PID value.
    Readings PID_DVal, PID_IVal, PID_PVal, PID_PWMOnTime and PID_PWMPulse will reflect the actual calculated PID values and Pulse.