20_FRM_SERVO.pm 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. ##############################################
  2. # $Id: 20_FRM_SERVO.pm 5927 2014-05-21 21:56:37Z ntruchsess $
  3. ##############################################
  4. package main;
  5. use strict;
  6. use warnings;
  7. #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
  8. BEGIN {
  9. if (!grep(/FHEM\/lib$/,@INC)) {
  10. foreach my $inc (grep(/FHEM$/,@INC)) {
  11. push @INC,$inc."/lib";
  12. };
  13. };
  14. };
  15. use Device::Firmata::Constants qw/ :all /;
  16. #####################################
  17. my %sets = (
  18. "angle" => "",
  19. );
  20. sub
  21. FRM_SERVO_Initialize($)
  22. {
  23. my ($hash) = @_;
  24. $hash->{SetFn} = "FRM_SERVO_Set";
  25. $hash->{DefFn} = "FRM_Client_Define";
  26. $hash->{InitFn} = "FRM_SERVO_Init";
  27. $hash->{UndefFn} = "FRM_Client_Undef";
  28. $hash->{AttrFn} = "FRM_SERVO_Attr";
  29. $hash->{AttrList} = "min-pulse max-pulse IODev $main::readingFnAttributes";
  30. main::LoadModule("FRM");
  31. }
  32. sub
  33. FRM_SERVO_Init($$)
  34. {
  35. my ($hash,$args) = @_;
  36. my $ret = FRM_Init_Pin_Client($hash,$args,PIN_SERVO);
  37. return $ret if (defined $ret);
  38. eval {
  39. my $firmata = FRM_Client_FirmataDevice($hash);
  40. $hash->{resolution}=$firmata->{metadata}{servo_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{servo_resolutions});
  41. FRM_SERVO_apply_attribute($hash,"max-pulse"); #sets min-pulse as well
  42. };
  43. return FRM_Catch($@) if $@;
  44. main::readingsSingleUpdate($hash,"state","Initialized",1);
  45. return undef;
  46. }
  47. sub
  48. FRM_SERVO_Attr($$$$) {
  49. my ($command,$name,$attribute,$value) = @_;
  50. my $hash = $main::defs{$name};
  51. eval {
  52. if ($command eq "set") {
  53. ARGUMENT_HANDLER: {
  54. $attribute eq "IODev" and do {
  55. if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $value)) {
  56. FRM_Client_AssignIOPort($hash,$value);
  57. FRM_Init_Client($hash) if (defined ($hash->{IODev}));
  58. }
  59. last;
  60. };
  61. ($attribute eq "min-pulse" || $attribute eq "max-pulse") and do {
  62. if ($main::init_done) {
  63. $main::attr{$name}{$attribute}=$value;
  64. FRM_SERVO_apply_attribute($hash,$attribute);
  65. }
  66. last;
  67. };
  68. }
  69. }
  70. };
  71. my $ret = FRM_Catch($@) if $@;
  72. if ($ret) {
  73. $hash->{STATE} = "error setting $attribute to $value: ".$ret;
  74. return "cannot $command attribute $attribute to $value for $name: ".$ret;
  75. }
  76. return undef;
  77. }
  78. sub FRM_SERVO_apply_attribute {
  79. my ($hash,$attribute) = @_;
  80. if ( $attribute eq "min-pulse" || $attribute eq "max-pulse" ) {
  81. my $name = $hash->{NAME};
  82. # defaults are taken from: http://arduino.cc/en/Reference/ServoAttach
  83. FRM_Client_FirmataDevice($hash)->servo_config($hash->{PIN},{min_pulse => main::AttrVal($name,"min-pulse",544), max_pulse => main::AttrVal($name,"max-pulse",2400)});
  84. }
  85. }
  86. sub
  87. FRM_SERVO_Set($@)
  88. {
  89. my ($hash, @a) = @_;
  90. return "Need at least one parameters" if(@a < 2);
  91. return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
  92. if(!defined($sets{$a[1]}));
  93. my $command = $a[1];
  94. my $value = $a[2];
  95. eval {
  96. FRM_Client_FirmataDevice($hash)->servo_write($hash->{PIN},$value);
  97. main::readingsSingleUpdate($hash,"state",$value, 1);
  98. };
  99. return $@;
  100. }
  101. 1;
  102. =pod
  103. =begin html
  104. <a name="FRM_SERVO"></a>
  105. <h3>FRM_SERVO</h3>
  106. <ul>
  107. represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
  108. configured to drive a pwm-controlled servo-motor.<br>
  109. The value set will be drive the shaft of the servo to the specified angle. see <a href="http://arduino.cc/en/Reference/ServoWrite">Servo.write</a> for values and range<br>
  110. Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
  111. <a name="FRM_SERVOdefine"></a>
  112. <b>Define</b>
  113. <ul>
  114. <code>define &lt;name&gt; FRM_SERVO &lt;pin&gt;</code> <br>
  115. Defines the FRM_SERVO device. &lt;pin&gt> is the arduino-pin to use.
  116. </ul>
  117. <br>
  118. <a name="FRM_SERVOset"></a>
  119. <b>Set</b><br>
  120. <ul>
  121. <code>set &lt;name&gt; angle &lt;value&gt;</code><br>sets the angle of the servo-motors shaft to the value specified (in degrees).<br>
  122. </ul>
  123. <a name="FRM_SERVOget"></a>
  124. <b>Get</b><br>
  125. <ul>
  126. N/A
  127. </ul><br>
  128. <a name="FRM_SERVOattr"></a>
  129. <b>Attributes</b><br>
  130. <ul>
  131. <li><a href="#IODev">IODev</a><br>
  132. Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
  133. than one FRM-device defined.)
  134. </li>
  135. <li>min-pulse<br>
  136. sets the minimum puls-width to use. Defaults to 544. For most servos this translates into a rotation of 180° counterclockwise.</li>
  137. <li>max-pulse<br>
  138. sets the maximum puls-width to use. Defaults to 2400. For most servos this translates into a rotation of 180° clockwise</li>
  139. <li><a href="#eventMap">eventMap</a><br></li>
  140. <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
  141. </ul>
  142. </ul>
  143. <br>
  144. =end html
  145. =cut