20_FRM_ROTENC.pm 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. ##############################################
  2. # $Id: 20_FRM_ROTENC.pm 7111 2014-12-01 14:13:26Z 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. "reset" => "noArg",
  19. "offset"=> "",
  20. );
  21. my %gets = (
  22. "position" => "noArg",
  23. "offset" => "noArg",
  24. "value" => "noArg",
  25. );
  26. sub
  27. FRM_ROTENC_Initialize($)
  28. {
  29. my ($hash) = @_;
  30. $hash->{SetFn} = "FRM_ROTENC_Set";
  31. $hash->{GetFn} = "FRM_ROTENC_Get";
  32. $hash->{AttrFn} = "FRM_ROTENC_Attr";
  33. $hash->{DefFn} = "FRM_Client_Define";
  34. $hash->{InitFn} = "FRM_ROTENC_Init";
  35. $hash->{UndefFn} = "FRM_ROTENC_Undef";
  36. $hash->{StateFn} = "FRM_ROTENC_State";
  37. $hash->{AttrList} = "IODev $main::readingFnAttributes";
  38. main::LoadModule("FRM");
  39. }
  40. sub
  41. FRM_ROTENC_Init($$)
  42. {
  43. my ($hash,$args) = @_;
  44. my $u = "wrong syntax: define <name> FRM_ROTENC pinA pinB [id]";
  45. return $u unless defined $args and int(@$args) > 1;
  46. my $pinA = @$args[0];
  47. my $pinB = @$args[1];
  48. my $encoder = defined @$args[2] ? @$args[2] : 0;
  49. my $name = $hash->{NAME};
  50. $hash->{PINA} = $pinA;
  51. $hash->{PINB} = $pinB;
  52. $hash->{ENCODERNUM} = $encoder;
  53. eval {
  54. FRM_Client_AssignIOPort($hash);
  55. my $firmata = FRM_Client_FirmataDevice($hash);
  56. $firmata->encoder_attach($encoder,$pinA,$pinB);
  57. $firmata->observe_encoder($encoder, \&FRM_ROTENC_observer, $hash );
  58. };
  59. if ($@) {
  60. $@ =~ /^(.*)( at.*FHEM.*)$/;
  61. $hash->{STATE} = "error initializing: ".$1;
  62. return "error initializing '$name': $1";
  63. }
  64. if (! (defined AttrVal($name,"stateFormat",undef))) {
  65. $main::attr{$name}{"stateFormat"} = "position";
  66. }
  67. $hash->{offset} = ReadingsVal($name,"position",0);
  68. main::readingsSingleUpdate($hash,"state","Initialized",1);
  69. return undef;
  70. }
  71. sub
  72. FRM_ROTENC_observer
  73. {
  74. my ( $encoder, $value, $hash ) = @_;
  75. my $name = $hash->{NAME};
  76. Log3 ($name,5,"onEncoderMessage for pins ".$hash->{PINA}.",".$hash->{PINB}." encoder: ".$encoder." position: ".$value."\n");
  77. main::readingsBeginUpdate($hash);
  78. main::readingsBulkUpdate($hash,"position",$value+$hash->{offset}, 1);
  79. main::readingsBulkUpdate($hash,"value",$value, 1);
  80. main::readingsEndUpdate($hash,1);
  81. }
  82. sub
  83. FRM_ROTENC_Set
  84. {
  85. my ($hash, @a) = @_;
  86. return "Need at least one parameters" if(@a < 2);
  87. my $command = $a[1];
  88. my $value = $a[2];
  89. if(!defined($sets{$command})) {
  90. my @commands = ();
  91. foreach my $key (sort keys %sets) {
  92. push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key;
  93. }
  94. return "Unknown argument $a[1], choose one of " . join(" ", @commands);
  95. }
  96. COMMAND_HANDLER: {
  97. $command eq "reset" and do {
  98. eval {
  99. FRM_Client_FirmataDevice($hash)->encoder_reset_position($hash->{ENCODERNUM});
  100. };
  101. main::readingsBeginUpdate($hash);
  102. main::readingsBulkUpdate($hash,"position",$hash->{offset},1);
  103. main::readingsBulkUpdate($hash,"value",0,1);
  104. main::readingsEndUpdate($hash,1);
  105. last;
  106. };
  107. $command eq "offset" and do {
  108. $hash->{offset} = $value;
  109. readingsSingleUpdate($hash,"position",ReadingsVal($hash->{NAME},"value",0)+$value,1);
  110. last;
  111. };
  112. }
  113. }
  114. sub
  115. FRM_ROTENC_Get($)
  116. {
  117. my ($hash, @a) = @_;
  118. return "Need at least one parameters" if(@a < 2);
  119. my $command = $a[1];
  120. my $value = $a[2];
  121. if(!defined($gets{$command})) {
  122. my @commands = ();
  123. foreach my $key (sort keys %gets) {
  124. push @commands, $gets{$key} ? $key.":".join(",",$gets{$key}) : $key;
  125. }
  126. return "Unknown argument $a[1], choose one of " . join(" ", @commands);
  127. }
  128. my $name = shift @a;
  129. my $cmd = shift @a;
  130. ARGUMENT_HANDLER: {
  131. $cmd eq "position" and do {
  132. return ReadingsVal($hash->{NAME},"position","0");
  133. };
  134. $cmd eq "offset" and do {
  135. return $hash->{offset};
  136. };
  137. $cmd eq "value" and do {
  138. return ReadingsVal($hash->{NAME},"value","0");
  139. };
  140. }
  141. return undef;
  142. }
  143. sub
  144. FRM_ROTENC_Attr($$$$) {
  145. my ($command,$name,$attribute,$value) = @_;
  146. my $hash = $main::defs{$name};
  147. eval {
  148. if ($command eq "set") {
  149. ARGUMENT_HANDLER: {
  150. $attribute eq "IODev" and do {
  151. if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $value)) {
  152. FRM_Client_AssignIOPort($hash,$value);
  153. FRM_Init_Client($hash) if (defined ($hash->{IODev}));
  154. }
  155. last;
  156. };
  157. }
  158. }
  159. };
  160. if ($@) {
  161. $@ =~ /^(.*)( at.*FHEM.*)$/;
  162. $hash->{STATE} = "error setting $attribute to $value: ".$1;
  163. return "cannot $command attribute $attribute to $value for $name: ".$1;
  164. }
  165. }
  166. sub
  167. FRM_ROTENC_Undef($$)
  168. {
  169. my ($hash, $name) = @_;
  170. my $pinA = $hash->{PINA};
  171. my $pinB = $hash->{PINB};
  172. eval {
  173. my $firmata = FRM_Client_FirmataDevice($hash);
  174. $firmata->encoder_detach($hash->{ENCODERNUM});
  175. $firmata->pin_mode($pinA,PIN_ANALOG);
  176. $firmata->pin_mode($pinB,PIN_ANALOG);
  177. };
  178. if ($@) {
  179. eval {
  180. my $firmata = FRM_Client_FirmataDevice($hash);
  181. $firmata->pin_mode($pinA,PIN_INPUT);
  182. $firmata->digital_write($pinA,0);
  183. $firmata->pin_mode($pinB,PIN_INPUT);
  184. $firmata->digital_write($pinB,0);
  185. };
  186. }
  187. return undef;
  188. }
  189. sub
  190. FRM_ROTENC_State($$$$)
  191. {
  192. my ($hash, $tim, $sname, $sval) = @_;
  193. if ($sname eq "position") {
  194. $hash->{offset} = $sval;
  195. }
  196. return undef;
  197. }
  198. 1;
  199. =pod
  200. =begin html
  201. <a name="FRM_ROTENC"></a>
  202. <h3>FRM_ROTENC</h3>
  203. <ul>
  204. represents a rotary-encoder attached to two pins of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a><br>
  205. Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
  206. <a name="FRM_ROTENCdefine"></a>
  207. <b>Define</b>
  208. <ul>
  209. <code>define &lt;name&gt; FRM_ROTENC &lt;pinA&gt; &lt;pinB&gt; [id]</code> <br>
  210. Defines the FRM_ROTENC device. &lt;pinA&gt> and &lt;pinA&gt> are the arduino-pins to use.<br>
  211. [id] is the instance-id of the encoder. Must be a unique number per FRM-device (rages from 0-4 depending on Firmata being used, optional if a single encoder is attached to the arduino).<br>
  212. </ul>
  213. <br>
  214. <a name="FRM_ROTENCset"></a>
  215. <b>Set</b><br>
  216. <li>reset<br>
  217. resets to value of 'position' to 0<br></li>
  218. <li>offset &lt;value&gt;<br>
  219. set offset value of 'position'<br></li>
  220. <a name="FRM_ROTENCget"></a>
  221. <b>Get</b>
  222. <ul>
  223. <li>position<br>
  224. returns the position of the rotary-encoder attached to pinA and pinB of the arduino<br>
  225. the 'position' is the sum of 'value' and 'offset'<br></li>
  226. <li>offset<br>
  227. returns the offset value<br>
  228. on shutdown of fhem the latest position-value is saved as new offset.<br></li>
  229. <li>value<br>
  230. returns the raw position value as it's reported by the rotary-encoder attached to pinA and pinB of the arduino<br>
  231. this value is reset to 0 whenever Arduino restarts or Firmata is reinitialized<br></li>
  232. </ul><br>
  233. <a name="FRM_ROTENCattr"></a>
  234. <b>Attributes</b><br>
  235. <ul>
  236. <li><a href="#IODev">IODev</a><br>
  237. Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
  238. than one FRM-device defined.)
  239. </li>
  240. <li><a href="#eventMap">eventMap</a><br></li>
  241. <li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
  242. </ul>
  243. </ul>
  244. <br>
  245. =end html
  246. =cut