99_PID.pm 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. ##############################################
  2. # This is primary intended to use S300TH/S555TH in conjunction with FHT8v
  3. # to control room temperature
  4. # for this I defined the following:
  5. # define conf_set_value dummy
  6. # define conf_set_value notify config:pid_set_value { pid_set_value(%) }
  7. # { pid_create("bz",0.0,255.0) }
  8. # { pid_set_factors("bz",65.0,7.8,15.0) }
  9. # define control_bz notify th_sensor_bz {my @@d=split(" ","%");;fhem("set CUL raw T16270126" . sprintf("%%02X", pid("bz",$d[1])))}
  10. # trigger config:pid_set_value "bz",21.0
  11. #
  12. # Alexander Tietzel (Perl newby)
  13. #
  14. # TODO:
  15. # want to have references to the second hash inside %data. Some like
  16. # %ctrl = ${$data{$name}}...
  17. # Or better write this as a class and instantiate each controller
  18. # but I did not discover how to have persistent objects in FHEM
  19. # so I helped myself with the hash to have multiple instances.
  20. ###############################################
  21. package main;
  22. use strict;
  23. use warnings;
  24. use Math::Trig;
  25. sub pid($$);
  26. sub pid_create($$$);
  27. sub pid_set_value($$);
  28. sub pid_set_factors($$$$);
  29. sub PID_Initialize($);
  30. # See perldoc DateTime::Event::Sunrise for details
  31. my %data;
  32. sub
  33. PID_Initialize($)
  34. {
  35. my ($hash) = @_;
  36. }
  37. ##########################
  38. sub pid_create($$$) {
  39. my $name = shift;
  40. my $min = shift;
  41. my $max = shift;
  42. ${$data{$name}}{'last_time'} = 0.0;
  43. ${$data{$name}}{'p_factor'} = 0.0;
  44. ${$data{$name}}{'i_factor'} = 0.0;
  45. ${$data{$name}}{'d_factor'} = 0.0;
  46. ${$data{$name}}{'error'} = 0.0;
  47. ${$data{$name}}{'actuation'} = 0.0;
  48. ${$data{$name}}{'integrator'} = 0.0;
  49. ${$data{$name}}{'set_value'} = 0.0;
  50. ${$data{$name}}{'sat_min'} = $min;
  51. ${$data{$name}}{'sat_max'} = $max;
  52. return undef;
  53. }
  54. sub pid_set_factors($$$$) {
  55. my $name = shift;
  56. my $p_factor = shift;
  57. my $i_factor = shift;
  58. my $d_factor = shift;
  59. ${$data{$name}}{'p_factor'} = $p_factor;
  60. ${$data{$name}}{'i_factor'} = $i_factor;
  61. ${$data{$name}}{'d_factor'} = $d_factor;
  62. return undef;
  63. }
  64. sub pid_set_value($$) {
  65. my $name = shift;
  66. my $set_value = shift;
  67. ${$data{$name}}{'set_value'} = $set_value;
  68. return undef;
  69. }
  70. sub saturate($$) {
  71. my $name = shift;
  72. my $v = shift;
  73. if ( $v > ${$data{$name}}{'sat_max'} ) {
  74. return ${$data{$name}}{'sat_max'};
  75. }
  76. if ( $v < ${$data{$name}}{'sat_min'} ) {
  77. return ${$data{$name}}{'sat_min'};
  78. }
  79. return $v;
  80. }
  81. sub pid($$) {
  82. my $name = shift;
  83. my $in = shift;
  84. # Log 1, "PID (" . $name . "): kp: " . ${$data{$name}}{'p_factor'} . " ki: " . ${$data{$name}}{'i_factor'} . " kd: " .${$data{$name}}{'d_factor'};
  85. my $error = ${$data{$name}}{'set_value'} - $in;
  86. my $p = $error * ${$data{$name}}{'p_factor'};
  87. my $i = ${$data{$name}}{'integrator'}+$error*${$data{$name}}{'i_factor'};
  88. ${$data{$name}}{'integrator'} = saturate($name, $i);
  89. my $d = ($error - ${$data{$name}}{'error'}) * ${$data{$name}}{'d_factor'};
  90. ${$data{$name}}{'error_value'} = $error;
  91. my $a = $p + ${$data{$name}}{'integrator'} + $d;
  92. ${$data{$name}}{'actuation'} = saturate($name, $a);
  93. Log 4, sprintf("PID (%s): p: %.2f i: %.2f d: %.2f", $name, $p, ${$data{$name}}{'integrator'}, $d);
  94. return ${$data{$name}}{'actuation'};
  95. }
  96. 1;