80_M232.pm 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. #
  2. #
  3. # 80_M232.pm
  4. # written by Dr. Boris Neubert 2007-11-26
  5. # e-mail: omega at online dot de
  6. #
  7. ##############################################
  8. # $Id: 80_M232.pm 16375 2018-03-10 15:40:02Z neubert $
  9. package main;
  10. use strict;
  11. use warnings;
  12. sub M232Write($$);
  13. sub M232GetData($$);
  14. sub Log($$);
  15. use vars qw {%attr %defs};
  16. #####################################
  17. sub
  18. M232_Initialize($)
  19. {
  20. my ($hash) = @_;
  21. # Provider
  22. $hash->{WriteFn} = "M232_Write";
  23. $hash->{Clients} = ":M232Counter:M232Voltage:";
  24. # Consumer
  25. $hash->{DefFn} = "M232_Define";
  26. $hash->{UndefFn} = "M232_Undef";
  27. $hash->{GetFn} = "M232_Get";
  28. $hash->{SetFn} = "M232_Set";
  29. $hash->{AttrList}= "model:m232";
  30. }
  31. #####################################
  32. sub
  33. M232_Define($$)
  34. {
  35. my ($hash, $def) = @_;
  36. my @a = split("[ \t][ \t]*", $def);
  37. $hash->{STATE} = "Initialized";
  38. my $dev = $a[2];
  39. if($dev eq "none") {
  40. Log3 $hash, 1, "M232 device is none, commands will be echoed only";
  41. return undef;
  42. }
  43. Log3 $hash, 3, "M232 opening device $dev";
  44. my $po;
  45. if ($^O eq 'MSWin32') {
  46. eval ("use Win32::SerialPort;");
  47. if ($@) {
  48. $hash->{STATE} = "error using Modul Win32::SerialPort";
  49. Log3 $hash, 1,"Error using Device::SerialPort";
  50. return "Can't use Win32::SerialPort $@\n";
  51. }
  52. $po = new Win32::SerialPort ($dev, 1);
  53. } else {
  54. eval ("use Device::SerialPort;");
  55. if ($@) {
  56. $hash->{STATE} = "error using Modul Device::SerialPort";
  57. Log3 $hash, 1,"Error using Device::SerialPort";
  58. return "Can't Device::SerialPort $@\n";
  59. }
  60. $po = new Device::SerialPort ($dev, 1);
  61. }
  62. if (!$po) {
  63. $hash->{STATE} = "error opening device";
  64. Log3 $hash, 1,"Error opening Serial Device $dev";
  65. return "Can't open Device $dev: $^E\n";
  66. }
  67. Log3 $hash, 3, "M232 opened device $dev";
  68. $po->close();
  69. $hash->{DeviceName} = $dev;
  70. return undef;
  71. }
  72. #####################################
  73. sub
  74. M232_Undef($$)
  75. {
  76. my ($hash, $arg) = @_;
  77. my $name = $hash->{NAME};
  78. foreach my $d (sort keys %defs) {
  79. if(defined($defs{$d}) &&
  80. defined($defs{$d}{IODev}) &&
  81. $defs{$d}{IODev} == $hash)
  82. {
  83. my $lev = ($reread_active ? 4 : 2);
  84. Log3 $hash, $lev, "deleting port for $d";
  85. delete $defs{$d}{IODev};
  86. }
  87. }
  88. return undef;
  89. }
  90. #####################################
  91. # M232_Ready
  92. # implement ReadyFn
  93. # only used for Win32
  94. #
  95. sub
  96. M232_Ready($$)
  97. {
  98. my ($hash, $dev) = @_;
  99. my $po=$dev||$hash->{po};
  100. return 0 if !$po;
  101. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags)=$po->status;
  102. return ($InBytes>0);
  103. }
  104. #####################################
  105. sub
  106. M232_Set($@)
  107. {
  108. my ($hash, @a) = @_;
  109. my $u1 = "Usage: set <name> auto <value>\n" .
  110. "set <name> stop\n" .
  111. "set <name> start\n" .
  112. "set <name> octet <value>\n" .
  113. "set <name> [io0..io7] 0|1\n";
  114. return $u1 if(int(@a) < 2);
  115. my $msg;
  116. my $reading= $a[1];
  117. my $value;
  118. my @legal;
  119. if($reading eq "auto") {
  120. return $u1 if(int(@a) !=3);
  121. $value= $a[2];
  122. @legal= (0..5,"none");
  123. if(!grep($value eq $_, @legal)) {
  124. return "Illegal value $value, possible values: @legal";
  125. }
  126. if($value eq "none") { $value= 0; } else { $value+=1; }
  127. $msg= "M" . $value;
  128. }
  129. elsif($reading eq "start") {
  130. return $u1 if(int(@a) !=2);
  131. $msg= "Z1";
  132. }
  133. elsif($reading eq "stop") {
  134. return $u1 if(int(@a) !=2);
  135. $msg= "Z0";
  136. }
  137. elsif($reading eq "octet") {
  138. return $u1 if(int(@a) !=3);
  139. $value= $a[2];
  140. @legal= (0..255);
  141. if(!grep($value eq $_, @legal)) {
  142. return "Illegal value $value, possible values: 0..255";
  143. }
  144. $msg= sprintf("W%02X", $value);
  145. }
  146. elsif($reading =~ /^io[0-7]$/) {
  147. return $u1 if(int(@a) !=3);
  148. $value= $a[2];
  149. return $u1 unless($value eq "0" || $value eq "1");
  150. $msg= "D" . substr($reading,2,1) . $value;
  151. }
  152. else { return $u1; }
  153. my $d = M232GetData($hash, $msg);
  154. return "Read error" if(!defined($d));
  155. return $d;
  156. }
  157. #####################################
  158. sub
  159. M232_Get($@)
  160. {
  161. my ($hash, @a) = @_;
  162. my $u1 = "Usage: get <name> [an0..an5]\n" .
  163. "get <name> [io0..io7]\n" .
  164. "get <name> octet\n" .
  165. "get <name> counter";
  166. return $u1 if(int(@a) != 2);
  167. my $name= $a[0];
  168. my $reading= $a[1];
  169. my $msg;
  170. my $retval;
  171. my ($count,$d,$state,$iscurrent,$voltage);
  172. if($reading eq "counter") {
  173. $msg= "z";
  174. $d = M232GetData($hash, $msg);
  175. return "Read error" if(!defined($d));
  176. $count= hex $d;
  177. $retval= $count;
  178. }
  179. elsif($reading =~ /^an[0-5]$/) {
  180. $msg= "a" . substr($reading,2,1);
  181. $d = M232GetData($hash, $msg);
  182. return "Read error" if(!defined($d));
  183. $voltage= (hex substr($d,0,3))*5.00/1024.0;
  184. $iscurrent= substr($d,3,1);
  185. $retval= $voltage; # . " " . $iscurrent;
  186. }
  187. elsif($reading =~ /^io[0-7]$/) {
  188. $msg= "d" . substr($reading,2,1);
  189. $d = M232GetData($hash, $msg);
  190. return "Read error" if(!defined($d));
  191. $state= hex $d;
  192. $retval= $state;
  193. }
  194. elsif($reading eq "octet") {
  195. $msg= "w";
  196. $d = M232GetData($hash, $msg);
  197. return "Read error" if(!defined($d));
  198. $state= hex $d;
  199. $retval= $state;
  200. }
  201. else { return $u1; }
  202. $hash->{READINGS}{$reading}{VAL}= $retval;
  203. $hash->{READINGS}{$reading}{TIME}= TimeNow();
  204. return "$name $reading => $retval";
  205. }
  206. #####################################
  207. sub
  208. M232_Write($$)
  209. {
  210. my ($hash,$msg) = @_;
  211. return M232GetData($hash, $msg);
  212. }
  213. #####################################
  214. sub
  215. M232GetData($$)
  216. {
  217. my ($hash, $data) = @_;
  218. my $dev=$hash->{DeviceName};
  219. my $MSGSTART= chr 1;
  220. my $MSGEND= chr 13;
  221. my $MSGACK= chr 6;
  222. my $MSGNACK= chr 21;
  223. my $serport;
  224. my $d = $MSGSTART . $data . $MSGEND;
  225. if ($^O eq 'MSWin32') {
  226. $serport=new Win32::SerialPort ($dev, 1);
  227. }else{
  228. $serport=new Device::SerialPort ($dev, 1);
  229. }
  230. if(!$serport) {
  231. Log3 $hash, 3, "M232: Can't open $dev: $!";
  232. return undef;
  233. }
  234. $serport->reset_error();
  235. $serport->baudrate(2400);
  236. $serport->databits(8);
  237. $serport->parity('none');
  238. $serport->stopbits(1);
  239. $serport->handshake('none');
  240. $serport->write_settings;
  241. $hash->{po}=$serport;
  242. Log3 $hash, 4, "M232: Sending $d";
  243. my $rm = "M232: ?";
  244. $serport->lookclear;
  245. $serport->write($d);
  246. my $retval = "";
  247. my $status = "";
  248. my $nfound=0;
  249. my $ret=undef;
  250. sleep(1);
  251. for(;;) {
  252. if ($^O eq 'MSWin32') {
  253. $nfound=M232_Ready($hash,undef);
  254. }else{
  255. my ($rout, $rin) = ('', '');
  256. vec($rin, $serport->FILENO, 1) = 1;
  257. $nfound = select($rin, undef, undef, 1.0); # 3 seconds timeout
  258. if($nfound < 0) {
  259. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  260. $rm="M232:Select error $nfound / $!";
  261. last;
  262. }
  263. }
  264. last if($nfound == 0);
  265. my $out = $serport->read(1);
  266. if(!defined($out) || length($out) == 0) {
  267. $rm = "M232 EOF on $dev";
  268. last;
  269. }
  270. if($out eq $MSGACK) {
  271. $rm= "M232: acknowledged";
  272. Log3 $hash, 4, "M232: return value \'" . $retval . "\'";
  273. $status= "ACK";
  274. } elsif($out eq $MSGNACK) {
  275. $rm= "M232: not acknowledged";
  276. $status= "NACK";
  277. $retval= undef;
  278. } else {
  279. $retval .= $out;
  280. }
  281. if($status) {
  282. $ret=$retval;
  283. last;
  284. }
  285. }
  286. DONE:
  287. $serport->close();
  288. undef $serport;
  289. delete $hash->{po} if exists($hash->{po});
  290. Log3 $hash, 4, $rm;
  291. return $ret;
  292. }
  293. 1;
  294. =pod
  295. =item summary connection to ELV M232 module
  296. =item summary_DE Anbindung eines ELV-M232-Moduls
  297. =begin html
  298. <a name="M232"></a>
  299. <h3>M232</h3>
  300. <ul>
  301. <br>
  302. <a name="M232define"></a>
  303. <b>Define</b>
  304. <ul>
  305. <code>define &lt;name&gt; M232 &lt;m232-device&gt;</code>
  306. <br><br>
  307. Define a M232 device. You can attach as many M232 devices as you like. A
  308. M232 device provides 6 analog inputs (voltage 0..5V with 10 bit resolution)
  309. and 8 bidirectional digital ports. The eighth digital port can be used as a
  310. 16 bit counter (maximum frequency 3kHz). The M232 device needs to be
  311. connected to a 25pin sub-d RS232 serial port. A USB-to-serial converter
  312. works fine if no serial port is available.<br><br>
  313. Examples:
  314. <ul>
  315. <code>define m232 M232 /dev/ttyUSB2</code><br>
  316. </ul>
  317. <br>
  318. </ul>
  319. <a name="M232set"></a>
  320. <b>Set </b>
  321. <ul>
  322. <code>set &lt;name&gt; stop</code>
  323. <br><br>
  324. Stops the counter.
  325. <br><br>
  326. <code>set &lt;name&gt; start</code>
  327. <br><br>
  328. Resets the counter to zero and starts it.
  329. <br><br>
  330. <code>set &lt;name&gt; octet <value></code>
  331. <br><br>
  332. Sets the state of all digital ports at once, value is 0..255.
  333. <br><br>
  334. <code>set &lt;name&gt; io0..io7 0|1</code>
  335. <br><br>
  336. Turns digital port 0..7 off or on.
  337. <br><br>
  338. </ul>
  339. <a name="M232get"></a>
  340. <b>Get</b>
  341. <ul>
  342. <code>get &lt;name&gt; [an0..an5]</code>
  343. <br><br>
  344. Gets the reading of analog input 0..5 in volts.
  345. <br><br>
  346. <code>get &lt;name&gt; [io0..io7]</code>
  347. <br><br>
  348. Gets the state of digital ports 0..7, result is 0 or 1.
  349. <br><br>
  350. <code>get &lt;name&gt; octet</code>
  351. <br><br>
  352. Gets the state of all digital ports at once, result is 0..255.
  353. <br><br>
  354. <code>get &lt;name&gt; counter</code>
  355. <br><br>
  356. Gets the number of ticks of the counter since the last reset. The counter
  357. wraps around from 65,535 to 0 and <i>then stops</i>.
  358. See <a href="#M232Counter">M232Counter</a> for how we care about this.
  359. <br><br>
  360. </ul>
  361. <a name="M232attr"></a>
  362. <b>Attributes</b>
  363. <ul>
  364. <li><a href="#model">model</a> (m232)</li>
  365. </ul>
  366. <br>
  367. </ul>
  368. =end html
  369. =cut