11_OWX_SER.pm 26 KB

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