11_OWX_FRM.pm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. ########################################################################################
  2. #
  3. # OWX_FRM.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the FRM interface of OWX
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 11_OWX_FRM.pm 15392 2017-11-05 06:46:46Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # Provides the following methods for OWX
  14. #
  15. # Define
  16. # Detect
  17. # Alarms
  18. # Complex
  19. # Discover
  20. # Read
  21. # Ready
  22. # Verify
  23. # Write
  24. # observer
  25. # device_to_firmata
  26. # firmata_to_device
  27. #
  28. ########################################################################################
  29. #
  30. # $hash->{DeviceName} = <firmata-device>:<firmata-pin>
  31. # $hash->{INTERFACE} = "firmata";
  32. # $hash->{HWDEVICE} = <firmata-device>
  33. # $hash->{PIN} = <firmata-pin>
  34. # $hash->{TYPE} = "OWX";
  35. #
  36. ########################################################################################
  37. package OWX_FRM;
  38. use strict;
  39. use warnings;
  40. use Device::Firmata::Constants qw/ :all /;
  41. ########################################################################################
  42. #
  43. # Constructor
  44. #
  45. ########################################################################################
  46. sub new($) {
  47. my ($class,$hash) = @_;
  48. return bless {
  49. hash => $hash
  50. }, $class;
  51. }
  52. ########################################################################################
  53. #
  54. # Define - Implements Define method
  55. #
  56. # Parameter def = definition string
  57. #
  58. # Return undef if ok, otherwise error message
  59. #
  60. ########################################################################################
  61. sub Define($) {
  62. my ($self,$def) = @_;
  63. my $hash = $self->{hash};
  64. if (!defined($main::modules{FRM})) {
  65. my $ret = "OWX_FRM::Define module FRM not yet loaded, please define an FRM device first.";
  66. main::Log3 $hash->{NAME},1,$ret;
  67. return $ret;
  68. }
  69. my @a = split( "[ \t][ \t]*", $def );
  70. my $u = "wrong syntax: define <name> OWX <firmata-device>:<firmata-pin>";
  71. return $u unless int(@a) > 0;
  72. my($fdev,$pin) = split(':',$a[2]);
  73. $self->{pin} = $pin;
  74. $self->{id} = 0;
  75. $self->{name} = $hash->{NAME};
  76. $self->{hash} = $hash;
  77. #-- when the specified device name contains @<digits>, remove these.
  78. #my $dev =~ s/\@\d*//;
  79. #main::AssignIoPort($hash);
  80. #-- store with OWX device
  81. $hash->{DeviceName} = $a[2];
  82. $hash->{INTERFACE} = "firmata";
  83. $hash->{HWDEVICE} = $fdev;
  84. $hash->{PIN} = $pin;
  85. $hash->{ASYNCHRONOUS} = 0;
  86. #-- module version
  87. $hash->{version} = "7.05";
  88. main::Log3 $hash->{NAME},1,"OWX_FRM::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
  89. if( $hash->{version} ne $main::owx_version);
  90. #-- call low level init function for the device
  91. main::InternalTimer(time()+55, "OWX_FRM::Init", $self,0);
  92. return undef;
  93. }
  94. ########################################################################################
  95. #
  96. # Detect - Find out if we have the proper interface
  97. #
  98. # Return 1 if ok, otherwise 0
  99. #
  100. ########################################################################################
  101. sub Detect () {
  102. my ($self) = @_;
  103. my $hash = $self->{hash};
  104. my $ret;
  105. my $name = $hash->{NAME};
  106. my $ress = "OWX: 1-Wire bus $name: interface ";
  107. my $iodev = $hash->{IODev};
  108. if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
  109. $ret=1;
  110. $ress .= "Firmata detected in $iodev->{NAME}";
  111. } else {
  112. $ret=0;
  113. $ress .= defined $iodev ? "$iodev->{NAME} is not connected to Firmata" : "not associated to any FRM device";
  114. }
  115. main::Log(1, $ress);
  116. return $ret;
  117. }
  118. ########################################################################################
  119. #
  120. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  121. #
  122. # Return number of alarmed devices
  123. #
  124. ########################################################################################
  125. sub Alarms() {
  126. my ($self) = @_;
  127. my $hash = $self->{hash};
  128. #-- get the interface
  129. my $frm = $hash->{IODev};
  130. return 0 unless defined $frm;
  131. my $firmata = $frm->{FirmataDevice};
  132. my $pin = $hash->{PIN};
  133. return 0 unless ( defined $firmata and defined $pin );
  134. $hash->{ALARMDEVS} = undef;
  135. $firmata->onewire_search_alarms($hash->{PIN});
  136. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  137. for (my $i=0;$i<$times;$i++) {
  138. if (main::FRM_poll($hash->{IODev})) {
  139. if (defined $hash->{ALARMDEVS}) {
  140. return 1;
  141. }
  142. } else {
  143. select (undef,undef,undef,0.05);
  144. }
  145. }
  146. $hash->{ALARMDEVS} = [];
  147. return 1;
  148. }
  149. ########################################################################################
  150. #
  151. # Init - Initialize the 1-wire device
  152. #
  153. # Parameter hash = hash of bus master
  154. #
  155. # Return 1 : OK
  156. # 0 : not OK
  157. #
  158. ########################################################################################
  159. sub Init() {
  160. my ($self) = @_;
  161. my $hash = $self->{hash};
  162. my $dev = $hash->{DeviceName};
  163. my $name = $hash->{NAME};
  164. my $pin = $hash->{PIN};
  165. my $msg;
  166. #main::Log 1,"==================> STARTING INIT of 11_OWX_FRM";
  167. select(undef,undef,undef,0.01);
  168. my @args = ($pin);
  169. $hash->{IODev} = $main::defs{$hash->{HWDEVICE}};
  170. my $ret = main::FRM_Init_Pin_Client($hash,\@args,PIN_ONEWIRE);
  171. if (defined $ret){
  172. $msg = "Error ".$ret;
  173. main::Log3 $name,1,"OWX_FRM::Init ".$msg;
  174. return $msg;
  175. }
  176. my $firmata = main::FRM_Client_FirmataDevice($hash);
  177. $hash->{FRM_OWX_CORRELATIONID} = 0;
  178. $firmata->observe_onewire($pin,\&observer,$hash);
  179. $hash->{FRM_OWX_REPLIES} = {};
  180. $hash->{DEVS} = [];
  181. if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
  182. $firmata->onewire_config($pin,1);
  183. }
  184. $hash->{STATE}="Initialized";
  185. main::InternalTimer(main::gettimeofday()+10, "OWX_Discover", $hash,0);
  186. return undef;
  187. }
  188. ########################################################################################
  189. #
  190. # Complex - Send match ROM, data block and receive bytes as response
  191. #
  192. # Parameter hash = hash of bus master,
  193. # owx_dev = ROM ID of device
  194. # data = string to send
  195. # numread = number of bytes to receive
  196. #
  197. # Return response, if OK
  198. # 0 if not OK
  199. #
  200. ########################################################################################
  201. sub Complex ($$$$) {
  202. my ($self,$owx_dev,$data,$numread) =@_;
  203. my $hash = $self->{hash};
  204. my $name = $hash->{NAME};
  205. my $res = "";
  206. #-- get the interface
  207. my $frm = $hash->{IODev};
  208. return 0 unless defined $frm;
  209. my $firmata = $frm->{FirmataDevice};
  210. my $pin = $hash->{PIN};
  211. return 0 unless ( defined $firmata and defined $pin );
  212. my $ow_command = {};
  213. #-- has match ROM part
  214. if ($owx_dev) {
  215. $ow_command->{"select"} = device_to_firmata($owx_dev);
  216. #-- padding first 9 bytes into result string, since we have this
  217. # in the serial interfaces as well
  218. $res .= "000000000";
  219. }
  220. #-- has data part
  221. if ($data) {
  222. my @data = unpack "C*", $data;
  223. $ow_command->{"write"} = \@data;
  224. $res.=$data;
  225. }
  226. #-- has receive part
  227. if ( $numread > 0 ) {
  228. $ow_command->{"read"} = $numread;
  229. #Firmata sends 0-address on read after skip
  230. $owx_dev = '00.000000000000.00' unless defined $owx_dev;
  231. my $id = $hash->{FRM_OWX_CORRELATIONID};
  232. $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
  233. $hash->{FRM_OWX_REQUESTS}->{$id} = {
  234. command => $ow_command,
  235. device => $owx_dev
  236. };
  237. delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  238. $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
  239. }
  240. $firmata->onewire_command_series( $pin, $ow_command );
  241. if ($numread) {
  242. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  243. for (my $i=0;$i<$times;$i++) {
  244. if (main::FRM_poll($hash->{IODev})) {
  245. if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) {
  246. $res .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  247. main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i ",$res);
  248. return $res;
  249. }
  250. } else {
  251. select (undef,undef,undef,0.05);
  252. }
  253. }
  254. }
  255. main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop ",$res);
  256. return $res;
  257. }
  258. ########################################################################################
  259. #
  260. # Discover - Discover devices on the 1-Wire bus via internal firmware
  261. #
  262. # Parameter hash = hash of bus master
  263. #
  264. # Return 0 : error
  265. # 1 : OK
  266. #
  267. ########################################################################################
  268. sub Discover ($) {
  269. my ($self) = @_;
  270. my $hash = $self->{hash};
  271. #main::Log 1,"======================> FRM Discover called";
  272. #-- get the interface
  273. my $frm = $hash->{IODev};
  274. return 0 unless defined $frm;
  275. my $firmata = $frm->{FirmataDevice};
  276. my $pin = $hash->{PIN};
  277. return 0 unless ( defined $firmata and defined $pin );
  278. my $old_devices = $hash->{DEVS};
  279. $hash->{DEVS} = undef;
  280. my $res = $firmata->onewire_search($hash->{PIN});
  281. #main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev};
  282. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  283. #main::Log 1,"===========> olddevices = $old_devices, tries =$times";
  284. for (my $i=0;$i<$times;$i++) {
  285. if (main::FRM_poll($hash->{IODev})) {
  286. if (defined $hash->{DEVS}) {
  287. return 1;
  288. }
  289. } else {
  290. select (undef,undef,undef,0.05);
  291. }
  292. }
  293. #main::Log 1,"===========> olddevices restored";
  294. $hash->{DEVS} = $old_devices;
  295. return 1;
  296. }
  297. #######################################################################################
  298. #
  299. # Read - Implement the Read function
  300. #
  301. # Parameter numexp = expected number of bytes
  302. #
  303. #######################################################################################
  304. sub Read(@) {
  305. my ($self,$numexp) = @_;
  306. my $hash = $self->{hash};
  307. my $name = $hash->{NAME};
  308. my $buffer = $hash->{PREBUFFER};
  309. my $owx_dev = $hash->{FRM_OWX_CURRDEV};
  310. my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  311. #-- first read
  312. $buffer .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  313. main::OWX_WDBGL($name,5,"OWX_FRM::Read receiving in first read ",$buffer);
  314. return $buffer;
  315. }
  316. ########################################################################################
  317. #
  318. # Ready - Implement the Ready function
  319. #
  320. # Return 1 : OK
  321. # 0 : not OK
  322. #
  323. ########################################################################################
  324. sub Ready () {
  325. my ($self) = @_;
  326. my $hash = $self->{hash};
  327. my $name = $hash->{NAME};
  328. my $success= 0;
  329. main::Log3 $name,1,"OWX_FRM::Ready function called for bus $name. STATE=".$hash->{STATE};
  330. return $success;
  331. }
  332. ########################################################################################
  333. #
  334. # Reset - Reset the 1-Wire bus
  335. #
  336. # Parameter hash = hash of bus master
  337. #
  338. # Return 1 : OK
  339. # 0 : not OK
  340. #
  341. ########################################################################################
  342. sub Reset() {
  343. my ($self) = @_;
  344. my $hash = $self->{hash};
  345. #-- get the interface
  346. my $frm = $hash->{IODev};
  347. return undef unless defined $frm;
  348. my $firmata = $frm->{FirmataDevice};
  349. my $pin = $hash->{PIN};
  350. return undef unless ( defined $firmata and defined $pin );
  351. $firmata->onewire_reset($pin);
  352. return 1;
  353. }
  354. ########################################################################################
  355. #
  356. # Verify - Verify a particular device on the 1-Wire bus
  357. #
  358. # Parameter hash = hash of bus master, dev = 8 Byte ROM ID of device to be tested
  359. #
  360. # Return 1 : device found
  361. # 0 : device not
  362. #
  363. ########################################################################################
  364. sub Verify($) {
  365. my ($self,$dev) = @_;
  366. my $hash = $self->{hash};
  367. foreach my $found ($hash->{DEVS}) {
  368. if ($dev eq $found) {
  369. return 1;
  370. }
  371. }
  372. return 0;
  373. }
  374. #######################################################################################
  375. #
  376. # Write - Implement the write function
  377. #
  378. #
  379. # Parameter cmd = string to be sent
  380. # reset = 1 if initial bus reset has to be done
  381. #
  382. ########################################################################################
  383. sub Write(@) {
  384. my ($self,$cmd, $reset) = @_;
  385. my $hash = $self->{hash};
  386. my $name = $hash->{NAME};
  387. my $res = "";
  388. #-- get the interface
  389. my $frm = $hash->{IODev};
  390. unless(defined $frm){
  391. main::Log3 $name,1,"OWX_FRM::Write attempted to undefined device $name";
  392. return 0
  393. }
  394. my $firmata = $frm->{FirmataDevice};
  395. my $pin = $hash->{PIN};
  396. unless ( defined $firmata and defined $pin ){
  397. main::Log3 $name,1,"OWX_FRM::Write attempted to ill-defined device $name";
  398. return 0
  399. }
  400. #-- if necessary, perform a reset operation
  401. $self->Reset()
  402. if( $reset );
  403. main::OWX_WDBGL($name,5,"OWX_FRM::Write Sending out ",$cmd);
  404. my $cmd2 = $cmd;
  405. my $owx_dev ="";
  406. my $ow_command = {};
  407. #-- take away trailing 0xFF
  408. my $takeoff=0;
  409. my $tchar;
  410. for( my $i=length($cmd)-1; $i>=0; $i--){
  411. $tchar = substr($cmd,$i,1);
  412. if( ord($tchar) == 0xff ){
  413. $takeoff++;
  414. }else{
  415. last;
  416. }
  417. }
  418. $cmd2 = substr($cmd,0,length($cmd)-$takeoff);
  419. #-- has match ROM part - need to extract this
  420. $tchar = substr($cmd2,0,1);
  421. if( ord($tchar) == 0x55 ){
  422. #-- ID of the device. Careful, because hash is the hash of busmaster
  423. for(my $i=0;$i<8;$i++){
  424. my $j=int(ord(substr($cmd2,$i+1,1))/16);
  425. my $k=ord(substr($cmd,$i+1,1))%16;
  426. $owx_dev.=sprintf "%1x%1x",$j,$k;
  427. $owx_dev.="."
  428. if($i==0 || $i==6);
  429. }
  430. $owx_dev=uc($owx_dev);
  431. $cmd2 = substr($cmd2,9);
  432. $ow_command->{"select"} = device_to_firmata($owx_dev);
  433. #-- padding first 9 bytes into result string, since we have this
  434. # in the serial interfaces as well
  435. $res .= "000000000";
  436. }
  437. #-- has data part
  438. if ($cmd2) {
  439. my @data = unpack "C*", $cmd2;
  440. $ow_command->{"write"} = \@data;
  441. $res.=$cmd2;
  442. }
  443. #-- pre-content of result buffer
  444. $hash->{PREBUFFER} = $res;
  445. #-- always receive part ??
  446. # if ( $numread > 0 ) {
  447. $ow_command->{"read"} = length($cmd);
  448. #Firmata sends 0-address on read after skip
  449. $owx_dev = '00.000000000000.00' unless defined $owx_dev;
  450. my $id = $hash->{FRM_OWX_CORRELATIONID};
  451. $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
  452. $hash->{FRM_OWX_REQUESTS}->{$id} = {
  453. command => $ow_command,
  454. device => $owx_dev
  455. };
  456. delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
  457. $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
  458. #}
  459. $firmata->onewire_command_series( $pin, $ow_command );
  460. }
  461. #######################################################################################
  462. #
  463. # observer function for listening to the FRM device
  464. #
  465. #######################################################################################
  466. sub observer
  467. {
  468. my ( $data,$hash ) = @_;
  469. my $command = $data->{command};
  470. COMMAND_HANDLER: {
  471. $command eq "READ_REPLY" and do {
  472. my $id = $data->{id};
  473. my $request = (defined $id) ? $hash->{FRM_OWX_REQUESTS}->{$id} : undef;
  474. unless (defined $request) {
  475. return unless (defined $data->{device});
  476. my $owx_device = firmata_to_device($data->{device});
  477. my %requests = %{$hash->{FRM_OWX_REQUESTS}};
  478. foreach my $key (keys %requests) {
  479. if ($requests{$key}->{device} eq $owx_device) {
  480. $request = $requests{$key};
  481. $id = $key;
  482. last;
  483. };
  484. };
  485. };
  486. return unless (defined $request);
  487. my $owx_data = pack "C*",@{$data->{data}};
  488. my $owx_device = $request->{device};
  489. $hash->{FRM_OWX_REPLIES}->{$owx_device} = $owx_data;
  490. ##
  491. $hash->{FRM_OWX_CURRDEV} = $owx_device;
  492. delete $hash->{FRM_OWX_REQUESTS}->{$id};
  493. return main::OWX_Read($hash);
  494. };
  495. ($command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY") and do {
  496. my @owx_devices = ();
  497. foreach my $device (@{$data->{devices}}) {
  498. push @owx_devices, firmata_to_device($device);
  499. }
  500. if ($command eq "SEARCH_REPLY") {
  501. $hash->{DEVS} = \@owx_devices;
  502. #$main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices;
  503. } else {
  504. $hash->{ALARMDEVS} = \@owx_devices;
  505. }
  506. last;
  507. };
  508. }
  509. }
  510. #######################################################################################
  511. #
  512. # translation of strings
  513. #
  514. #######################################################################################
  515. sub device_to_firmata
  516. {
  517. my @device;
  518. foreach my $hbyte ( unpack "A2xA2A2A2A2A2A2xA2", shift ) {
  519. push @device, hex $hbyte;
  520. }
  521. return {
  522. family => shift @device,
  523. crc => pop @device,
  524. identity => \@device,
  525. }
  526. }
  527. sub firmata_to_device
  528. {
  529. my $device = shift;
  530. return sprintf( "%02X.%02X%02X%02X%02X%02X%02X.%02X", $device->{family}, @{ $device->{identity} }, $device->{crc} );
  531. }
  532. 1;
  533. =pod
  534. =item device
  535. =item summary to address an OWX interface device via Arduino Firmata
  536. =item summary_DE zur Adressierung eines OWX Interface Device mit Arduino Firmata
  537. =begin html
  538. <a name="OWX_FRM"></a>
  539. <h3>OWX_FRM</h3>
  540. See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  541. =end html
  542. =begin html_DE
  543. <a name="OWX_FRM"></a>
  544. <h3>OWX_FRM</h3>
  545. <a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  546. =end html_DE