11_OWX_SER.pm 26 KB

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