11_OWX_TCP.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  1. ########################################################################################
  2. #
  3. # OWX_TCP.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the
  6. # TCP/IP-UART interfaces of OWX connected to a DS2480
  7. #
  8. # Prof. Dr. Peter A. Henning
  9. #
  10. # $Id: 11_OWX_TCP.pm 16671 2018-04-29 05:06:35Z phenning $
  11. #
  12. ########################################################################################
  13. #
  14. # Provides the following methods for OWX
  15. #
  16. # Define
  17. # Detect
  18. # Alarms
  19. # Complex
  20. # Discover
  21. # Open
  22. # Close
  23. # Reopen
  24. # Init
  25. # Read
  26. # Ready
  27. # Reset
  28. # Verify
  29. # Write
  30. # Query
  31. # First
  32. # Next
  33. # Search
  34. # SearchLow
  35. #
  36. ########################################################################################
  37. #
  38. # $hash->{DeviceName} = <ip-addresse>:port
  39. # $hash->{INTERFACE} = "DS2480";
  40. # $hash->{TYPE} = "OWX";
  41. #
  42. ########################################################################################
  43. package OWX_TCP;
  44. use strict;
  45. use warnings;
  46. use DevIo;
  47. ########################################################################################
  48. #
  49. # Constructor
  50. #
  51. ########################################################################################
  52. sub new($) {
  53. my ($class,$hash) = @_;
  54. return bless {
  55. #-- OWX device
  56. hash => $hash,
  57. #-- module version
  58. version => "7.11",
  59. #-- 16 byte search string
  60. search => [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0],
  61. ROM_ID => [0,0,0,0 ,0,0,0,0],
  62. #-- search state for 1-Wire bus search
  63. LastDiscrepancy => 0,
  64. LastFamilyDiscrepancy => 0,
  65. LastDeviceFlag => 0,
  66. }, $class;
  67. }
  68. ########################################################################################
  69. #
  70. # Define - Implements Define method
  71. #
  72. # Parameter def = definition string
  73. #
  74. # Return undef if ok, otherwise error message
  75. #
  76. ########################################################################################
  77. sub Define ($) {
  78. my ($self,$def) = @_;
  79. my $hash = $self->{hash};
  80. my @a = split("[ \t][ \t]*", $def);
  81. my $dev = $a[2];
  82. my @tcp = split(':',$dev);
  83. my $ip = $tcp[0];
  84. my $port= ( $tcp[1] )?$tcp[1]:23;
  85. #-- store with OWX device
  86. $hash->{DeviceName} = $ip.":".$port;
  87. $hash->{ASYNCHRONOUS} = 0;
  88. main::Log3 $hash->{NAME},1,"OWX_TCP::Define warning: version ".$self->{version}." not identical to OWX version "..$main::owx_version
  89. if( $self->{version} ne $main::owx_version );
  90. #-- call low level init function for the device
  91. $self->Init();
  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 ($i,$j,$k,$l,$res,$ret,$ress);
  105. my $name = $hash->{NAME};
  106. my $ress0 = "OWX_TCP::Detect 1-Wire bus $name: interface ";
  107. $ress = $ress0;
  108. my $interface;
  109. #-- timing byte for DS2480
  110. $self->Query("\xC1",1);
  111. #-- Max 4 tries to detect an interface
  112. for($l=0;$l<4;$l++) {
  113. #-- write 1-Wire bus (Fig. 2 of Maxim AN192)
  114. $res = $self->Query("\x17\x45\x5B\x0F\x91",5);
  115. #-- process 4/5-byte string for detection
  116. if( !defined($res)){
  117. $res="";
  118. $ret=0;
  119. }elsif( ($res eq "\x16\x44\x5A\x00") || ($res eq "\x16\x44\x5A\x00\x90") || ($res eq "\x16\x44\x5A\x00\x93")){
  120. $ress .= "master DS2480 detected for the first time";
  121. $interface="DS2480";
  122. $ret=1;
  123. } elsif( ($res eq "\x17\x45\x5B\x0F\x91") || ($res eq "\x17\x45\x1B\x0F\x91")){
  124. $ress .= "master DS2480 re-detected";
  125. $interface="DS2480";
  126. $ret=1;
  127. } else {
  128. $ret=0;
  129. }
  130. last
  131. if( $ret==1 );
  132. main::OWX_WDBGL($name,1,$ress."not found, answer was ",$res);
  133. $ress = $ress0;
  134. }
  135. if( $ret == 0 ){
  136. $interface=undef;
  137. main::OWX_WDBGL($name,1,$ress."not detected, answer was ",$res);
  138. } else {
  139. main::OWX_WDBGL($name,1,$ress,undef);
  140. }
  141. $hash->{INTERFACE} = $interface;
  142. return $ret;
  143. }
  144. ########################################################################################
  145. #
  146. # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
  147. #
  148. # Return number of alarmed devices
  149. #
  150. ########################################################################################
  151. sub Alarms () {
  152. my ($self) = @_;
  153. my $hash = $self->{hash};
  154. my $name = $hash->{NAME};
  155. #-- Discover all alarmed devices on the 1-Wire bus
  156. #main::Log3 $name, 1, "We ARE ASKING FOR ALARMS - WHY NOT ASK THE LAST RESET THING ? ";
  157. my $res = $self->First("alarm");
  158. while( $self->{LastDeviceFlag}==0 && $res != 0){
  159. $res = $res & $self->SER_Next("alarm");
  160. }
  161. if( $hash->{ALARMDEVS} ) {
  162. main::Log3 $name, 1, " Alarms = ".join(' ',@{$hash->{ALARMDEVS}});
  163. return( int(@{$hash->{ALARMDEVS}}) );
  164. } else {
  165. return 0;
  166. }
  167. }
  168. ########################################################################################
  169. #
  170. # Complex - Send match ROM, data block and receive bytes as response
  171. #
  172. # Parameter hash = hash of bus master,
  173. # owx_dev = ROM ID of device
  174. # data = string to send
  175. # numread = number of bytes to receive
  176. #
  177. # Return response, if OK
  178. # 0 if not OK
  179. #
  180. ########################################################################################
  181. sub Complex ($$$$) {
  182. my ($self,$dev,$data,$numread) =@_;
  183. my $hash = $self->{hash};
  184. my $name = $hash->{NAME};
  185. my $select;
  186. my $res;
  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 byte id
  195. $dev=~s/\.//g;
  196. for(my $i=0;$i<8;$i++){
  197. $rom_id[$i]=hex(substr($dev,2*$i,2));
  198. }
  199. $select=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id).$data;
  200. #-- has no match ROM part
  201. } else {
  202. $select=$data;
  203. }
  204. #-- has receive data part
  205. if( $numread >0 ){
  206. #$numread += length($data);
  207. for( my $i=0;$i<$numread;$i++){
  208. $select .= "\xFF";
  209. };
  210. }
  211. main::OWX_WDBGL($name,5,"OWX_TCP::Complex sending ",$select);
  212. #-- send data block (Fig. 6 of Maxim AN192)
  213. my $data2="";
  214. my $retlen = length($select);
  215. #-- if necessary, prepend E1 character for data mode
  216. if( substr($select,0,1) ne '\xE1') {
  217. $data2 = "\xE1";
  218. }
  219. #-- all E3 characters have to be duplicated
  220. for(my $i=0;$i<length($select);$i++){
  221. my $newchar = substr($select,$i,1);
  222. $data2=$data2.$newchar;
  223. if( $newchar eq '\xE3'){
  224. $data2=$data2.$newchar;
  225. }
  226. }
  227. #-- write 1-Wire bus as a single string
  228. $res =$self->Query($data2,$retlen);
  229. main::OWX_WDBGL($name,5,"OWX_TCP::Complex receiving ",$res);
  230. return $res
  231. }
  232. ########################################################################################
  233. #
  234. # Discover - Find devices on the 1-Wire bus
  235. #
  236. # Return 1, if alarmed devices found, 0 otherwise.
  237. #
  238. ########################################################################################
  239. sub Discover () {
  240. my ($self) = @_;
  241. my $hash = $self->{hash};
  242. #-- zero the array
  243. @{$hash->{DEVS}}=();
  244. #-- Discover all devices on the 1-Wire bus
  245. my $res = $self->First("discover");
  246. while( $self->{LastDeviceFlag}==0 && $res!=0 ){
  247. $res = $res & $self->Next("discover");
  248. }
  249. return( @{$hash->{DEVS}} == 0);
  250. }
  251. ########################################################################################
  252. #
  253. # Open - Open Device
  254. #
  255. ########################################################################################
  256. sub Open () {
  257. my ($self) = @_;
  258. my $hash = $self->{hash};
  259. return main::DevIo_OpenDev($hash,0,undef);
  260. }
  261. ########################################################################################
  262. #
  263. # Close - Close Device
  264. #
  265. ########################################################################################
  266. sub Close () {
  267. my ($self) = @_;
  268. my $hash = $self->{hash};
  269. return main::DevIo_CloseDev($hash);
  270. }
  271. ########################################################################################
  272. #
  273. # Reopen - Reopen Device
  274. #
  275. ########################################################################################
  276. sub Reopen () {
  277. my ($self) = @_;
  278. my $hash = $self->{hash};
  279. main::DevIo_CloseDev($hash);
  280. return main::DevIo_OpenDev($hash,0,undef);
  281. }
  282. ########################################################################################
  283. #
  284. # Init - Implement the Init function. Only reopens the device
  285. #
  286. # Return undef if ok
  287. #
  288. ########################################################################################
  289. sub Init() {
  290. my ($self) = @_;
  291. my $hash = $self->{hash};
  292. my $dev = $hash->{DeviceName};
  293. my $name = $hash->{NAME};
  294. main::Log3 $name, 5,"OWX_TCP::Init called on device $dev for bus $name, state is ".$hash->{STATE};
  295. main::DevIo_OpenDev($hash,0,undef);
  296. return undef;
  297. }
  298. #######################################################################################
  299. #
  300. # Read - Implement the Read function
  301. #
  302. # Parameter numexp = expected number of bytes
  303. #
  304. #######################################################################################
  305. sub Read(@) {
  306. my ($self,$numexp) = @_;
  307. my $hash = $self->{hash};
  308. my $name = $hash->{NAME};
  309. my $buffer = "";
  310. #-- first try to read things
  311. $buffer = main::DevIo_SimpleRead($hash);
  312. return $buffer;
  313. }
  314. ########################################################################################
  315. #
  316. # Ready - Implement the Ready function
  317. #
  318. # Return 1 : OK
  319. # 0 : not OK
  320. #
  321. ########################################################################################
  322. sub Ready () {
  323. my ($self) = @_;
  324. my $hash = $self->{hash};
  325. my $name = $hash->{NAME};
  326. my $success= 0;
  327. main::Log3 $name,5,"OWX_TCP: Ready function called for bus $name. STATE=".$hash->{STATE};
  328. if($hash->{STATE} eq "disconnected"){
  329. $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init");
  330. #main::Log3 $name,5," ===============> success = $success";
  331. if( !$success ){
  332. main::DevIo_CloseDev($hash);
  333. $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init");
  334. #main::Log3 $name,5," 2nd try ===============> success = $success";
  335. }
  336. }
  337. return $success;
  338. }
  339. ########################################################################################
  340. #
  341. # Reset - Reset the 1-Wire bus (Fig. 4 of Maxim AN192)
  342. #
  343. # Return 1 : OK
  344. # 0 : not OK
  345. #
  346. ########################################################################################
  347. sub Reset () {
  348. my ($self)=@_;
  349. my $hash = $self->{hash};
  350. my $name = $hash->{NAME};
  351. my $interface = $hash->{TYPE};
  352. my $asynchronous = $hash->{ASYNCHRONOUS};
  353. return 0
  354. if( $hash->{STATE} eq "disconnected" );
  355. my ($res,$r1,$r2);
  356. #-- Reset command
  357. my $cmd = "\xE3\xC5";
  358. #-- OWX interface
  359. if( $interface eq "OWX" ){
  360. $res = $self->Query($cmd,1);
  361. $res = "" if( !$res );
  362. #-- OWX_ASYNC
  363. }elsif( $interface eq "OWX_ASYNC"){
  364. $res = $self->Query($cmd,1);
  365. $res = "" if( !$res );
  366. }
  367. #-- process result
  368. $r1 = ord(substr($res,0,1)) & 192;
  369. if( $r1 != 192){
  370. main::OWX_WDBGL($name,4,"OWX_TCP::Reset_TCP failure on bus $name ",$res);
  371. return 0;
  372. }
  373. $hash->{ALARMED} = "no";
  374. $r2 = ord(substr($res,0,1)) & 3;
  375. if( $r2 == 3 ){
  376. main::Log3($name,4, "OWX_TCP: No presence detected on bus $name");
  377. return 1;
  378. }elsif( $r2 ==2 ){
  379. main::Log3($name,1, "OWX_TCP: Reset_TCP Alarm presence detected on bus $name");
  380. $hash->{ALARMED} = "yes";
  381. }
  382. return 1;
  383. }
  384. ########################################################################################
  385. #
  386. # Query - Sychronously write to and read from the 1-Wire bus
  387. #
  388. # Parameter: cmd = string to send to the 1-Wire bus
  389. #
  390. # Return: string received from the 1-Wire bus
  391. #
  392. ########################################################################################
  393. sub Query ($$) {
  394. my ($self,$cmd,$numexp) = @_;
  395. my $hash = $self->{hash};
  396. my $name = $hash->{NAME};
  397. my $state = $hash->{STATE};
  398. my $timeout = $hash->{timeout};
  399. my $timedout = 0;
  400. my $try = 0;
  401. my $buffer = "";
  402. my $numget = 0;
  403. my $numgeto = 0;
  404. if($state ne "opened") {
  405. main::Log3 $name, 1, "OWX_TCP::Query attempted to write to $state device $name";
  406. return undef;
  407. }
  408. #-- write operation
  409. main::OWX_WDBGL($name,5,"OWX_TCP::Query $name: Sending out",$cmd);
  410. return undef unless defined(main::DevIo_SimpleWrite($hash, $cmd, 0));
  411. #-- sleeping for some time
  412. select(undef,undef,undef,0.04);
  413. #-- first try to read things
  414. eval {
  415. local $SIG{ALRM} = sub { die "alarm\n" };
  416. alarm 1;
  417. $buffer = main::DevIo_DoSimpleRead($hash);
  418. alarm 0;
  419. };
  420. if ($@) {
  421. if( $numexp<=1 ){
  422. main::Log3 $name, 4, "OWX_TCP::Query timed out in first attempt - maybe one byte missing";
  423. return "";
  424. }
  425. main::Log3 $name, 4, "OWX_TCP::Query timed out in first attempt";
  426. $timedout = 1;
  427. }
  428. $numget = (defined($buffer))?length($buffer):0;
  429. $numgeto = $numget;
  430. #-- first try ok
  431. if( $numget >= $numexp){
  432. main::Log3 $name, 5, "OWX_TCP::Query $name: Received $numget of $numexp bytes in first attempt and state $state";
  433. return $buffer;
  434. #-- several tries to read from slow device
  435. }elsif( $numget<$numexp ){
  436. for($try=0;$try<3;$try++) {
  437. eval {
  438. local $SIG{ALRM} = sub { die "alarm\n" };
  439. alarm $timeout;
  440. $buffer .= main::DevIo_DoSimpleRead($hash);
  441. alarm 0;
  442. };
  443. if ($@) {
  444. main::Log3 $name, 4, "OWX_TCP::Query timed out in loop attempt no. $try";
  445. }
  446. $numget = length($buffer);
  447. last
  448. if( $numget>=$numexp );
  449. select(undef,undef,undef,0.01);
  450. }
  451. main::Log3 $name, 5, "OWX_TCP::Query $name: $numget of $numexp bytes in loop $try and state $state"
  452. if( $numget < $numexp );
  453. return $buffer
  454. if( $numget >= $numexp);
  455. }
  456. #-- something wrong with the 1-Wire device
  457. if( ($numget == $numgeto) && ($timedout==0) ){
  458. main::Log3 $name, 1,"OWX_TCP::Query $name: $numget of $numexp bytes - something wrong with the 1-Wire device ???";
  459. return $buffer;
  460. #-- ultimate failure
  461. }else{
  462. main::OWX_WDBGL($name,5,"OWX_TCP::Query $name: $numget of $numexp bytes in last attempt and state $state. ",$buffer);
  463. main::DevIo_Disconnected($hash);
  464. main::InternalTimer(main::gettimeofday()+$timeout, "main::OWX_Ready", $hash,0);
  465. return $buffer;
  466. }
  467. }
  468. ########################################################################################
  469. #
  470. # Verify - Verify a particular device on the 1-Wire bus
  471. #
  472. # Parameter dev = 8 Byte ROM ID of device to be tested
  473. #
  474. # Return 1 : device found
  475. # 0 : device not
  476. #
  477. ########################################################################################
  478. sub Verify ($) {
  479. my ($self,$dev) = @_;
  480. my $hash = $self->{hash};
  481. my @rom_id;
  482. my $i;
  483. #-- from search string to byte id
  484. my $devs=$dev;
  485. $devs=~s/\.//g;
  486. for($i=0;$i<8;$i++){
  487. $rom_id[$i]=hex(substr($devs,2*$i,2));
  488. }
  489. @{$self->{ROM_ID}}=@rom_id;
  490. #-- reset the search state
  491. $self->{LastDiscrepancy} = 64;
  492. $self->{LastDeviceFlag} = 0;
  493. #-- now do the search
  494. my $res=$self->Search("verify");
  495. my $dev2=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}});
  496. #-- reset the search state
  497. $self->{LastDiscrepancy} = 0;
  498. $self->{LastDeviceFlag} = 0;
  499. #-- check result
  500. if ($dev eq $dev2){
  501. return 1;
  502. }else{
  503. return 0;
  504. }
  505. }
  506. #######################################################################################
  507. #
  508. # Write - Implement the write function
  509. #
  510. #
  511. # Parameter cmd = string to be sent
  512. # reset = 1 if initial bus reset has to be done
  513. #
  514. ########################################################################################
  515. sub Write(@) {
  516. my ($self,$cmd, $reset) = @_;
  517. my $hash = $self->{hash};
  518. my $name = $hash->{NAME};
  519. if($hash->{STATE} eq "disconnected"){
  520. main::Log3 $name,1,"OWX_TCP::Write attempted to disconnected device $name";
  521. return undef;
  522. }
  523. #-- if necessary, perform a reset operation
  524. $self->Reset()
  525. if( $reset );
  526. #-- if necessary, prepend E1 character for data mode of DS2480
  527. my $cmd2 = ( substr($cmd,0,1) ne '\xE1')?"\xE1":"";
  528. #-- all E3 characters have to be duplicated in DS2480
  529. for(my $i=0;$i<length($cmd);$i++){
  530. my $newchar = substr($cmd,$i,1);
  531. $cmd2=$cmd2.$newchar;
  532. if( $newchar eq '\xE3'){
  533. $cmd2=$cmd2.$newchar;
  534. }
  535. }
  536. main::OWX_WDBGL($name,5,"OWX_TCP::Write Sending out ",$cmd2);
  537. main::DevIo_SimpleWrite($hash, $cmd2, 0);
  538. return;
  539. }
  540. #######################################################################################
  541. #
  542. # First - Find the 'first' devices on the 1-Wire bus
  543. #
  544. # Parameter mode
  545. #
  546. # Return 1 : device found, ROM number pushed to list
  547. # 0 : no device present
  548. #
  549. ########################################################################################
  550. sub First ($) {
  551. my ($self,$mode) = @_;
  552. #-- clear 16 byte of search data
  553. @{$self->{search}} = (0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0);
  554. #-- reset the search state
  555. $self->{LastDiscrepancy} = 0;
  556. $self->{LastDeviceFlag} = 0;
  557. $self->{LastFamilyDiscrepancy} = 0;
  558. #-- now do the search
  559. return $self->Search($mode);
  560. }
  561. ########################################################################################
  562. #
  563. # Next - Find the 'next' devices on the 1-Wire bus
  564. #
  565. # Parameter hash = hash of bus master, mode
  566. #
  567. # Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
  568. # or only in owx_ROM_ID (LastDeviceFlag=1)
  569. # 0 : device not found, or ot searched at all
  570. #
  571. ########################################################################################
  572. sub Next ($) {
  573. my ($self,$mode) = @_;
  574. return $self->Search($mode);
  575. }
  576. #######################################################################################
  577. #
  578. # Search - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
  579. # search state.
  580. #
  581. # Parameter mode=alarm,discover or verify
  582. #
  583. # Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
  584. # or only in owx_ROM_ID (LastDeviceFlag=1)
  585. # 0 : device not found, or ot searched at all
  586. #
  587. ########################################################################################
  588. sub Search ($) {
  589. my ($self,$mode)=@_;
  590. my $hash = $self->{hash};
  591. my $name = $hash->{NAME};
  592. my @owx_fams=();
  593. #TODO: What if undefined ?
  594. #-- if the last call was the last one, no search
  595. if ($self->{LastDeviceFlag}==1){
  596. return 0;
  597. }
  598. #-- 1-Wire reset
  599. if ($self->Reset()==0){
  600. #-- reset the search
  601. main::Log(1, "OWX_TCP::Search reset failed on bus $name");
  602. $self->{LastDiscrepancy} = 0;
  603. $self->{LastDeviceFlag} = 0;
  604. $self->{LastFamilyDiscrepancy} = 0;
  605. return 0;
  606. }
  607. #-- here we call the device dependent part
  608. $self->SearchLow($mode);
  609. #--check if we really found a device
  610. if( main::OWX_CRC($self->{ROM_ID})!= 0){
  611. #-- reset the search
  612. main::Log(1, "OWX_TCP::Search CRC failed on bus $name");
  613. $self->{LastDiscrepancy} = 0;
  614. $self->{LastDeviceFlag} = 0;
  615. $self->{LastFamilyDiscrepancy} = 0;
  616. return 0;
  617. }
  618. #-- character version of device ROM_ID, first byte = family
  619. my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}});
  620. #-- for some reason this does not work - replaced by another test, see below
  621. #if( $self->{LastDiscrepancy}==0 ){
  622. # $self->{LastDeviceFlag}=1;
  623. #}
  624. #--
  625. if( $self->{LastDiscrepancy}==$self->{LastFamilyDiscrepancy} ){
  626. $self->{LastFamilyDiscrepancy}=0;
  627. }
  628. #-- mode was to verify presence of a device
  629. if ($mode eq "verify") {
  630. main::Log(5, "OWX_TCP::Search: device verified $dev on bus $name");
  631. return 1;
  632. #-- mode was to discover devices
  633. } elsif( $mode eq "discover" ){
  634. #-- check families
  635. my $famfnd=0;
  636. foreach (@owx_fams){
  637. if( substr($dev,0,2) eq $_ ){
  638. #-- if present, set the fam found flag
  639. $famfnd=1;
  640. last;
  641. }
  642. }
  643. push(@owx_fams,substr($dev,0,2)) if( !$famfnd );
  644. foreach (@{$hash->{DEVS}}){
  645. if( $dev eq $_ ){
  646. #-- if present, set the last device found flag
  647. $self->{LastDeviceFlag}=1;
  648. last;
  649. }
  650. }
  651. if( $self->{LastDeviceFlag}!=1 ){
  652. #-- push to list
  653. push(@{$hash->{DEVS}},$dev);
  654. main::Log(5, "OWX_TCP::Search: new device found $dev on bus $name");
  655. }
  656. return 1;
  657. #-- mode was to discover alarm devices
  658. } elsif( $hash->{ALARMDEVS} ) {
  659. for(my $i=0;$i<@{$hash->{ALARMDEVS}};$i++){
  660. if( $dev eq ${$hash->{ALARMDEVS}}[$i] ){
  661. #-- if present, set the last device found flag
  662. $self->{LastDeviceFlag}=1;
  663. last;
  664. }
  665. }
  666. if( $self->{LastDeviceFlag}!=1 ){
  667. #--push to list
  668. push(@{$hash->{ALARMDEVS}},$dev);
  669. main::Log(5, "OWX_TCP::Search: new alarm device found $dev on bus $name");
  670. }
  671. return 1;
  672. }
  673. }
  674. ########################################################################################
  675. #
  676. # SearchLow - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
  677. # search state.
  678. #
  679. # Parameter mode=alarm,discover or verify
  680. #
  681. # Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
  682. # or only in owx_ROM_ID (LastDeviceFlag=1)
  683. # 0 : device not found, or ot searched at all
  684. #
  685. ########################################################################################
  686. sub SearchLow ($) {
  687. my ($self,$mode)=@_;
  688. my $hash = $self->{hash};
  689. my $name = $hash->{NAME};
  690. my ($sp1,$sp2,$response,$search_direction,$id_bit_number);
  691. #-- Response search data parsing operates bytewise
  692. $id_bit_number = 1;
  693. select(undef,undef,undef,0.05);
  694. #-- clear 16 byte of search data
  695. @{$self->{search}}=(0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0);
  696. #-- Output search data construction (Fig. 9 of Maxim AN192)
  697. # operates on a 16 byte search response = 64 pairs of two bits
  698. while ( $id_bit_number <= 64) {
  699. #-- address single bits in a 16 byte search string
  700. my $newcpos = int(($id_bit_number-1)/4);
  701. my $newimsk = ($id_bit_number-1)%4;
  702. #-- address single bits in a 8 byte id string
  703. my $newcpos2 = int(($id_bit_number-1)/8);
  704. my $newimsk2 = ($id_bit_number-1)%8;
  705. if( $id_bit_number <= $self->{LastDiscrepancy}){
  706. #-- first use the ROM ID bit to set the search direction
  707. if( $id_bit_number < $self->{LastDiscrepancy} ) {
  708. $search_direction = (@{$self->{ROM_ID}}[$newcpos2]>>$newimsk2) & 1;
  709. #-- at the last discrepancy search into 1 direction anyhow
  710. } else {
  711. $search_direction = 1;
  712. }
  713. #-- fill into search data;
  714. @{$self->{search}}[$newcpos]+=$search_direction<<(2*$newimsk+1);
  715. }
  716. #--increment number
  717. $id_bit_number++;
  718. }
  719. #-- issue data mode \xE1, the normal search command \xF0 or the alarm search command \xEC
  720. # and the command mode \xE3 / start accelerator \xB5
  721. if( $mode ne "alarm" ){
  722. $sp1 = "\xE1\xF0\xE3\xB5";
  723. } else {
  724. $sp1 = "\xE1\xEC\xE3\xB5";
  725. }
  726. #-- issue data mode \xE1, device ID, command mode \xE3 / end accelerator \xA5
  727. $sp2=sprintf("\xE1%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\xE3\xA5",@{$self->{search}});
  728. $response = $self->Query($sp1,1);
  729. $response = $self->Query($sp2,16);
  730. #-- interpret the return data
  731. if( length($response)!=16 ) {
  732. main::Log(3, "OWX_TCP::Search_TCP on bus $name 2nd return has wrong parameter with length = ".length($response)."");
  733. return 0;
  734. }
  735. #-- Response search data parsing (Fig. 11 of Maxim AN192)
  736. # operates on a 16 byte search response = 64 pairs of two bits
  737. $id_bit_number = 1;
  738. #-- clear 8 byte of device id for current search
  739. @{$self->{ROM_ID}} =(0,0,0,0 ,0,0,0,0);
  740. while ( $id_bit_number <= 64) {
  741. #-- adress single bits in a 16 byte string
  742. my $newcpos = int(($id_bit_number-1)/4);
  743. my $newimsk = ($id_bit_number-1)%4;
  744. #-- retrieve the new ROM_ID bit
  745. my $newchar = substr($response,$newcpos,1);
  746. #-- these are the new bits
  747. my $newibit = (( ord($newchar) >> (2*$newimsk) ) & 2) / 2;
  748. my $newdbit = ( ord($newchar) >> (2*$newimsk) ) & 1;
  749. #-- output for test purpose
  750. #print "id_bit_number=$id_bit_number => newcpos=$newcpos, newchar=0x".int(ord($newchar)/16).
  751. # ".".int(ord($newchar)%16)." r$id_bit_number=$newibit d$id_bit_number=$newdbit\n";
  752. #-- discrepancy=1 and ROM_ID=0
  753. if( ($newdbit==1) and ($newibit==0) ){
  754. $self->{LastDiscrepancy}=$id_bit_number;
  755. if( $id_bit_number < 9 ){
  756. $self->{LastFamilyDiscrepancy}=$id_bit_number;
  757. }
  758. }
  759. #-- fill into device data; one char per 8 bits
  760. @{$self->{ROM_ID}}[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8);
  761. #-- increment number
  762. $id_bit_number++;
  763. }
  764. return 1;
  765. }
  766. 1;
  767. =pod
  768. =item device
  769. =item summary to address an OWX interface device via TCP/IP
  770. =item summary_DE zur Adressierung eines OWX Interface Device mit TCP/IP
  771. =begin html
  772. <a name="OWX_TCP"></a>
  773. <h3>OWX_TCP</h3>
  774. <ul>
  775. See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
  776. </ul>
  777. =end html
  778. =begin html_DE
  779. <a name="OWX_TCP"></a>
  780. <h3>OWX_TCP</h3>
  781. <ul>
  782. <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>
  783. </ul>
  784. =end html_DE
  785. =cut