11_OWX_CCC.pm 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  1. ########################################################################################
  2. #
  3. # OWX_CCC.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 11_OWX_CCC.pm 16362 2018-03-09 17:18:43Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # Provides the following methods for OWX
  14. #
  15. # Define
  16. # Detect
  17. # Alarms
  18. # Complex
  19. # Discover
  20. # Open
  21. # Close
  22. # Reopen
  23. # Init
  24. # Read
  25. # ReadLow
  26. # Ready
  27. # Reset
  28. # Verify
  29. # Write
  30. #
  31. ########################################################################################
  32. #
  33. # $hash->{DeviceName} =
  34. # $hash->{INTERFACE} = "COC/CUNO";
  35. # $hash->{HWDEVICE} =
  36. # $hash->{TYPE} = "OWX";
  37. #
  38. ########################################################################################
  39. package OWX_CCC;
  40. use strict;
  41. use warnings;
  42. ########################################################################################
  43. #
  44. # Constructor
  45. #
  46. ########################################################################################
  47. sub new($) {
  48. my ($class,$hash) = @_;
  49. return bless {
  50. hash => $hash,
  51. #-- module version
  52. version => "7.08"
  53. }, $class;
  54. }
  55. ########################################################################################
  56. #
  57. # Define - Implements Define method
  58. #
  59. # Parameter def = definition string
  60. #
  61. # Return undef if ok, otherwise error message
  62. #
  63. ########################################################################################
  64. sub Define($) {
  65. my ($self,$def) = @_;
  66. my $hash = $self->{hash};
  67. my @a = split("[ \t][ \t]*", $def);
  68. #-- check syntax
  69. if(int(@a) < 3){
  70. return "OWX_CCC::Define Syntax error - must be define <name> OWX <cuno/coc-device>"
  71. }
  72. my $name = $a[0];
  73. $hash->{NAME} = $name;
  74. my $dev = $a[2];
  75. $hash->{DeviceName} = $dev;
  76. #-- Second step in case of CUNO: See if we can open it
  77. my $msg = "OWX_CCC::Define COC/CUNO device $dev";
  78. #-- hash des COC/CUNO
  79. my $hwdevice = $main::defs{$dev};
  80. if(!$hwdevice){
  81. main::Log3 $name,1, $msg." not defined";
  82. return $msg." not defined";
  83. }
  84. main::Log(1,$msg." defined");
  85. #-- store with OWX device
  86. $hash->{DeviceName} = $dev;
  87. $hash->{ASYNCHRONOUS} = 0;
  88. $hash->{INTERFACE} = "COC/CUNO";
  89. $hash->{HWDEVICE} = $hwdevice;
  90. #-- loop for some time until the state is "Initialized"
  91. for(my $i=0;$i<6;$i++){
  92. last if( $hwdevice->{STATE} eq "Initialized");
  93. main::Log(1,"OWX_CCC::Define Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE});
  94. select(undef,undef,undef,3);
  95. }
  96. main::Log(1, "OWX_CCC::Define Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized");
  97. #-- reset the 1-Wire system in COC/CUNO
  98. main::CUL_SimpleWrite($hwdevice, "Oi");
  99. main::Log3 $name,1,"OWX_CCC::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
  100. if( $hash->{version} ne $main::owx_version);
  101. #-- call low level init function for the device
  102. $self->Init();
  103. return undef;
  104. }
  105. ########################################################################################
  106. #
  107. # Detect - Find out if we have the proper interface
  108. #
  109. # Return 1 if ok, otherwise 0
  110. #
  111. ########################################################################################
  112. sub Detect () {
  113. my ($self) = @_;
  114. my $hash = $self->{hash};
  115. my ($ret,$ress);
  116. my $name = $hash->{NAME};
  117. my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface ";
  118. $ress = $ress0;
  119. #-- get the interface
  120. my $interface;
  121. my $hwdevice = $hash->{HWDEVICE};
  122. select(undef,undef,undef,2);
  123. #-- type of interface
  124. main::CUL_SimpleWrite($hwdevice, "V");
  125. select(undef,undef,undef,0.01);
  126. my ($err,$ob) = ReadLow($hwdevice);
  127. #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V"));
  128. #-- process result for detection
  129. if( !defined($ob)){
  130. $ob="";
  131. $ret=0;
  132. #-- COC
  133. }elsif( $ob =~ m/.*CSM.*/){
  134. $interface="COC";
  135. $ress .= "DS2482 / COC detected in $hwdevice->{NAME}";
  136. $ret=1;
  137. #-- CUNO
  138. }elsif( $ob =~ m/.*CUNO.*/){
  139. $interface="CUNO";
  140. $ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}";
  141. $ret=1;
  142. #-- something else
  143. } else {
  144. $ret=0;
  145. }
  146. #-- treat the failure cases
  147. if( $ret == 0 ){
  148. $interface=undef;
  149. $ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob";
  150. }
  151. #-- store with OWX device
  152. $hash->{INTERFACE} = $interface;
  153. main::Log(1, $ress);
  154. return $ret;
  155. }
  156. ########################################################################################
  157. #
  158. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  159. #
  160. # Return 0 because not implemented here.
  161. #
  162. ########################################################################################
  163. sub Alarms () {
  164. my ($self) = @_;
  165. return 0;
  166. }
  167. ########################################################################################
  168. #
  169. # Complex - Send match ROM, data block and receive bytes as response
  170. #
  171. # Parameter dev = ROM ID of device
  172. # data = string to send
  173. # numread = number of bytes to receive
  174. #
  175. # Return response, if OK
  176. # 0 if not OK
  177. #
  178. ########################################################################################
  179. sub Complex ($$$) {
  180. my ($self,$dev,$data,$numread) =@_;
  181. my $hash = $self->{hash};
  182. my $select;
  183. my $res = "";
  184. #-- get the interface
  185. my $hwdevice = $hash->{HWDEVICE};
  186. my $name = $hash->{NAME};
  187. #-- has match ROM part
  188. if( $dev ){
  189. #-- ID of the device
  190. my $owx_rnf = substr($dev,3,12);
  191. my $owx_f = substr($dev,0,2);
  192. #-- 8 byte 1-Wire device address
  193. my @rom_id =(0,0,0,0 ,0,0,0,0);
  194. #-- from search string to reverse string id
  195. $dev=~s/\.//g;
  196. for(my $i=0;$i<8;$i++){
  197. $rom_id[7-$i]=substr($dev,2*$i,2);
  198. }
  199. $select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id);
  200. main::Log3 $name,5,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select;
  201. #--
  202. main::CUL_SimpleWrite($hwdevice, $select);
  203. my ($err,$ob) = ReadLow($hwdevice);
  204. #-- padding first 9 bytes into result string, since we have this
  205. # in the serial interfaces as well
  206. $res .= "000000000";
  207. }
  208. #-- has data part
  209. if ( $data ){
  210. $self->Write($data,0);
  211. $res .= $data;
  212. }
  213. #-- has receive part
  214. if( $numread > 0 ){
  215. #$numread += length($data);
  216. main::Log3 $name,5,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes";
  217. $res.=$self->Read($numread);
  218. }
  219. return $res;
  220. }
  221. ########################################################################################
  222. #
  223. # Discover - Discover devices on the 1-Wire bus via internal firmware
  224. #
  225. # Return 0 : error
  226. # 1 : OK
  227. #
  228. ########################################################################################
  229. sub Discover () {
  230. my ($self) = @_;
  231. my $hash = $self->{hash};
  232. my $res;
  233. #-- get the interface
  234. my $hwdevice = $hash->{HWDEVICE};
  235. my $name = $hash->{NAME};
  236. #-- zero the array
  237. @{$hash->{DEVS}}=();
  238. #-- reset the busmaster
  239. $self->Init();
  240. #-- get the devices
  241. main::CUL_SimpleWrite($hwdevice, "Oc");
  242. select(undef,undef,undef,0.5);
  243. my ($err,$ob) = ReadLow($hwdevice);
  244. if( $ob ){
  245. # main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob);
  246. foreach my $dx (split(/\n/,$ob)){
  247. next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
  248. $dx =~ s/\d+\://;
  249. my $ddx = substr($dx,14,2).".";
  250. #-- reverse data from culfw
  251. for( my $i=1;$i<7;$i++){
  252. $ddx .= substr($dx,14-2*$i,2);
  253. }
  254. $ddx .= ".".substr($dx,0,2);
  255. push (@{$hash->{DEVS}},$ddx);
  256. }
  257. return 1;
  258. } else {
  259. main::Log3 $name,1, "OWX_CCC::Discover No answer to device search";
  260. return 0;
  261. }
  262. }
  263. ########################################################################################
  264. #
  265. # Open - Open Device
  266. #
  267. ########################################################################################
  268. sub Open () {
  269. my ($self) = @_;
  270. my $hash = $self->{hash};
  271. }
  272. ########################################################################################
  273. #
  274. # Close - Close Device
  275. #
  276. ########################################################################################
  277. sub Close () {
  278. my ($self) = @_;
  279. my $hash = $self->{hash};
  280. }
  281. ########################################################################################
  282. #
  283. # Reopen - Reopen Device
  284. #
  285. ########################################################################################
  286. sub Reopen () {
  287. main::Log 1,"[OWX_CCC] Warning: ->Reopen currently not defined
  288. }
  289. ########################################################################################
  290. #
  291. # Init - Low Level Init of the 1-wire device
  292. #
  293. # Return 1 : OK
  294. # 0 : not OK
  295. #
  296. ########################################################################################
  297. sub Init () {
  298. my ($self) = @_;
  299. my $hash = $self->{hash};
  300. my $dev = $hash->{DeviceName};
  301. my $name = $hash->{NAME};
  302. #main::Log3 $name,1,"OWX_CCC::Init called on device $dev for bus $name, state is ".$hash->{STATE};
  303. #-- get the interface
  304. my $hwdevice = $hash->{HWDEVICE};
  305. my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm"));
  306. #--
  307. # main::CUL_SimpleWrite($hwdevice, "ORm");
  308. # select(undef,undef,undef,0.01);
  309. # my ($err,$ob) = ReadLow($hwdevice);
  310. #main::Log3 $name,1,"OWX_CCC::Init gives ob=$ob ";
  311. if( !defined($ob) ){
  312. #main::Log 1,"empty ORm";
  313. return "empty ORm";
  314. }elsif( length($ob) < 13){
  315. #main::Log 1,"short ORm of length ".length($ob);
  316. return "short ORm of length ".length($ob);
  317. }elsif( substr($ob,9,4) eq "OK" ){
  318. #main::Log 1,"=====> OK";
  319. $hash->{STATE} = "opened";
  320. return undef;
  321. }else{
  322. #main::Log 1,"=====> ORm empty -> still OK ???";
  323. $hash->{STATE} = "opened";
  324. return undef;
  325. }
  326. }
  327. #######################################################################################
  328. #
  329. # Read - Implement the Read function
  330. #
  331. # Parameter numexp = expected number of bytes
  332. #
  333. #######################################################################################
  334. sub Read(@) {
  335. my ($self,$numexp) = @_;
  336. my $hash = $self->{hash};
  337. my $name = $hash->{NAME};
  338. my $state = $hash->{STATE};
  339. my $buffer = "";
  340. my $numget = 0;
  341. my ($err,$ob);
  342. my $try;
  343. my $maxtry = $numexp;
  344. #-- get the interface
  345. my $hwdevice = $hash->{HWDEVICE};
  346. for($try=0;$try<$maxtry;$try++){
  347. #main::Log(1, "Sending $hwdevice->{NAME}: OrB";
  348. #my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB"));
  349. main::CUL_SimpleWrite($hwdevice, "OrB");
  350. ($err,$ob) = main::CUL_ReadAnswer($hwdevice,$name,0,undef);
  351. #select(undef,undef,undef,0.01);
  352. #($err,$ob) = ReadLow($hwdevice);
  353. #-- process results
  354. if( !(defined($ob)) ){
  355. return "";
  356. #-- four bytes received makes one byte of result
  357. }elsif( length($ob) == 4 ){
  358. $buffer .= sprintf("%c",hex(substr($ob,0,2)));
  359. $numget++;
  360. #-- 11 bytes received makes one byte of result
  361. }elsif( length($ob) == 11 ){
  362. $buffer .= sprintf("%c",hex(substr($ob,9,2)));
  363. $numget++;
  364. #-- 18 bytes received from CUNO
  365. }elsif( length($ob) == 18 ){
  366. main::OWX_WDBGL($name,4,"OWX_CCC::Read 18 bytes from CUNO: ",$ob);
  367. #-- 20 bytes received = leftover from match
  368. }elsif( length($ob) == 20 ){
  369. $maxtry++;
  370. }else{
  371. main::Log3 $name,1,"OWX_CCC::Read unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{NAME};
  372. }
  373. }
  374. if( $numget >= $numexp){
  375. main::OWX_WDBGL($name,1,"OWX_CCC::Read from CUNO with error=$err: ",$buffer);
  376. return $buffer
  377. #-- ultimate failure
  378. }else{
  379. main::Log3 $name, 1,"OWX_CCC::Read $name: $numget of $numexp bytes with error=$err at state $state, this is an unrecoverable error";
  380. #main::DevIo_Disconnected($hwdevice);
  381. return "";
  382. }
  383. }
  384. ########################################################################################
  385. #
  386. # ReadLow - Replacement for CUL_ReadAnswer for better control
  387. #
  388. # Parameter: hash = hash of bus master
  389. #
  390. # Return: string received
  391. #
  392. ########################################################################################
  393. sub ReadLow($)
  394. {
  395. my ($hwdevice) = @_;
  396. my $type = $hwdevice->{TYPE};
  397. my $name = $hwdevice->{NAME};
  398. my $arg ="";
  399. my $anydata=0;
  400. my $regexp =undef;
  401. my ($mculdata, $rin) = ("", '');
  402. my $buf;
  403. my $to = 3; # 3 seconds timeout
  404. $to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less
  405. for(;;) {
  406. return ("Device lost when reading answer for get $arg", undef)
  407. if(!$hwdevice->{FD});
  408. vec($rin, $hwdevice->{FD}, 1) = 1;
  409. my $nfound = select($rin, undef, undef, $to);
  410. if($nfound < 0) {
  411. next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
  412. my $err = $!;
  413. #main::DevIo_Disconnected($hwdevice);
  414. main::Log 1,"============================> DISCOINNECTING";
  415. return("ReadLow $arg: $err", undef);
  416. }
  417. return ("Timeout reading answer for get $arg", undef)
  418. if($nfound == 0);
  419. $buf = main::DevIo_SimpleRead($hwdevice);
  420. return ("No data", undef) if(!defined($buf));
  421. if($buf) {
  422. main::Log3 $name,5, "OWX_CCC::ReadLow $buf";
  423. $mculdata .= $buf;
  424. }
  425. # \n\n is socat special
  426. if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) {
  427. if($regexp && $mculdata !~ m/$regexp/) {
  428. main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString});
  429. } else {
  430. return (undef, $mculdata)
  431. }
  432. }
  433. }
  434. }
  435. ########################################################################################
  436. #
  437. # Ready - Implement the Ready function
  438. #
  439. # Return 1 : OK
  440. # 0 : not OK
  441. #
  442. ########################################################################################
  443. sub Ready () {
  444. my ($self) = @_;
  445. my $hash = $self->{hash};
  446. my $name = $hash->{NAME};
  447. my $success;
  448. $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init")
  449. if($hash->{STATE} eq "disconnected");
  450. return $success;
  451. }
  452. ########################################################################################
  453. #
  454. # Reset - Reset the 1-Wire bus
  455. #
  456. # Return 1 : OK
  457. # 0 : not OK
  458. #
  459. ########################################################################################
  460. sub Reset () {
  461. my ($self) = @_;
  462. my $hash = $self->{hash};
  463. #-- get the interface
  464. my $hwdevice = $hash->{HWDEVICE};
  465. my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb"));
  466. if( substr($ob,9,4) eq "OK:1" ){
  467. return 1;
  468. }else{
  469. return 0
  470. }
  471. }
  472. ########################################################################################
  473. #
  474. # Verify - Verify a particular device on the 1-Wire bus
  475. #
  476. # Parameter dev = 8 Byte ROM ID of device to be tested
  477. #
  478. # Return 1 : device found
  479. # 0 : device not
  480. #
  481. ########################################################################################
  482. sub Verify ($) {
  483. my ($self,$dev) = @_;
  484. my $hash = $self->{hash};
  485. my $i;
  486. #-- get the interface
  487. my $hwdevice = $hash->{HWDEVICE};
  488. #-- Ask the COC/CUNO
  489. main::CUL_SimpleWrite($hwdevice, "OCf");
  490. #-- sleeping for some time
  491. select(undef,undef,undef,3);
  492. main::CUL_SimpleWrite($hwdevice, "Oc");
  493. select(undef,undef,undef,0.5);
  494. my ($err,$ob) = $self->($hwdevice);
  495. if( $ob ){
  496. foreach my $dx (split(/\n/,$ob)){
  497. next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
  498. $dx =~ s/\d+\://;
  499. my $ddx = substr($dx,14,2).".";
  500. #-- reverse data from culfw
  501. for( my $i=1;$i<7;$i++){
  502. $ddx .= substr($dx,14-2*$i,2);
  503. }
  504. $ddx .= ".".substr($dx,0,2);
  505. return 1 if( $dev eq $ddx);
  506. }
  507. }
  508. return 0;
  509. }
  510. #######################################################################################
  511. #
  512. # Write - Implement the write function
  513. #
  514. # Parameter cmd = string to be sent
  515. # reset = 1 if initial bus reset has to be done
  516. #
  517. ########################################################################################
  518. sub Write(@) {
  519. my ($self,$cmd, $reset) = @_;
  520. my $hash = $self->{hash};
  521. my $name = $hash->{NAME};
  522. my $count_out;
  523. if($hash->{STATE} eq "disconnected"){
  524. main::Log3 $name,4,"OWX_CCC::Write attempted to disconnected device $name";
  525. return undef;
  526. }
  527. #-- if necessary, perform a reset operation
  528. $self->Reset()
  529. if( $reset );
  530. my ($i,$j,$k);
  531. my $res = "";
  532. #-- get the interface
  533. my $hwdevice = $hash->{HWDEVICE};
  534. for( $i=0;$i<length($cmd);$i++){
  535. $j = int(ord(substr($cmd,$i,1))/16);
  536. $k = ord(substr($cmd,$i,1))%16;
  537. $res = sprintf "OwB%1x%1x ",$j,$k;
  538. main::CUL_SimpleWrite($hwdevice, $res);
  539. }
  540. main::OWX_WDBGL($name,5,"OWX_CCC::Write Sending out ",$cmd);
  541. }
  542. 1;
  543. =pod
  544. =item device
  545. =item summary to address an OWX interface device via COC/CUNO
  546. =item summary_DE zur Adressierung eines OWX Interface Device mit COC/CUNO
  547. =begin html
  548. <a name="OWX_CCC"></a>
  549. <h3>OWX_CCC</h3>
  550. <ul>
  551. See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  552. </ul>
  553. =end html
  554. =begin html_DE
  555. <a name="OWX_CCC"></a>
  556. <h3>OWX_CCC</h3>
  557. <ul>
  558. <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>
  559. </ul>
  560. =end html_DE