11_OWX_CCC.pm 16 KB

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