OWX_DS9097.pm 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. ########################################################################################
  2. #
  3. # OWX_DS2480.pm
  4. #
  5. # FHEM module providing hardware dependent functions for the DS9097 interface of OWX
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. # Norbert Truchsess
  9. #
  10. # $Id: OWX_DS9097.pm 10611 2016-01-24 11:15:07Z ntruchsess $
  11. #
  12. ########################################################################################
  13. #
  14. # Provides the following methods for OWX
  15. #
  16. # Alarms
  17. # Complex
  18. # Define
  19. # Discover
  20. # Init
  21. # Reset
  22. # Verify
  23. #
  24. ########################################################################################
  25. package OWX_DS9097;
  26. use strict;
  27. use warnings;
  28. use Time::HiRes qw( gettimeofday );
  29. use vars qw/@ISA/;
  30. @ISA='OWX_SER';
  31. use ProtoThreads;
  32. no warnings 'deprecated';
  33. sub new($) {
  34. my ($class,$serial) = @_;
  35. return bless $serial,$class;
  36. }
  37. ########################################################################################
  38. #
  39. # Complex - Send match ROM, data block and receive bytes as response
  40. #
  41. # Parameter hash = hash of bus master,
  42. # owx_dev = ROM ID of device
  43. # data = string to send
  44. # numread = number of bytes to receive
  45. #
  46. # Return response, if OK
  47. # 0 if not OK
  48. #
  49. ########################################################################################
  50. sub get_pt_execute($$$$) {
  51. my ($self, $reset, $dev, $writedata, $numread) = @_;
  52. my $pt_query;
  53. return PT_THREAD(sub {
  54. my ($thread) = @_;
  55. my $select;
  56. PT_BEGIN($thread);
  57. $self->reset() if ($reset);
  58. if (defined $writedata or $numread) {
  59. #-- has match ROM part
  60. if( $dev ) {
  61. #-- 8 byte 1-Wire device address
  62. my @rom_id;
  63. #-- from search string to byte id
  64. $dev=~s/\.//g;
  65. for(my $i=0;$i<8;$i++){
  66. $rom_id[$i]=hex(substr($dev,2*$i,2));
  67. }
  68. $select=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id);
  69. #-- has no match ROM part, issue skip ROM command (0xCC:)
  70. } else {
  71. $select="\xCC";
  72. }
  73. if (defined $writedata) {
  74. $select.=$writedata;
  75. }
  76. #-- for debugging
  77. if( $main::owx_async_debug > 1){
  78. main::Log3($self->{name},5,"OWX_DS9097::pt_execute: Sending out ".unpack ("H*",$select));
  79. }
  80. $self->block($select);
  81. }
  82. #-- has receive data part
  83. if( $numread ) {
  84. $select = "";
  85. #$numread += length($data);
  86. for( my $i=0;$i<$numread;$i++){
  87. $select .= "11111111";
  88. };
  89. $pt_query = $self->pt_query($select);
  90. PT_WAIT_THREAD($pt_query);
  91. die $pt_query->PT_CAUSE() if ($pt_query->PT_STATE() == PT_ERROR || $pt_query->PT_STATE() == PT_CANCELED);
  92. my $res = pack "b*",$pt_query->PT_RETVAL();
  93. main::Log3($self->{name},5,"OWX_DS9097::pt_execute: Receiving ".unpack ("H*",$res)) if( $main::owx_async_debug > 1);
  94. PT_EXIT($res);
  95. } else {
  96. PT_EXIT("");
  97. }
  98. PT_END;
  99. });
  100. }
  101. sub reset() {
  102. my ( $serial ) = @_;
  103. if (defined (my $hwdevice = $serial->{hash}->{USBDev})) {
  104. $hwdevice->baudrate(9600);
  105. $hwdevice->write_settings;
  106. main::Log3($serial->{name},5, "OWX_DS9097 9600 baud") if ( $main::owx_async_debug > 2 );
  107. $hwdevice->write("\xF0");
  108. main::Log3($serial->{name},5, "OWX_DS9097 reset") if ( $main::owx_async_debug > 1 );
  109. while ($serial->poll()) {};
  110. $hwdevice->baudrate(115200);
  111. $hwdevice->write_settings;
  112. main::Log3($serial->{name},5, "OWX_DS9097 115200 baud") if ( $main::owx_async_debug > 2 );
  113. }
  114. }
  115. sub block($) {
  116. my ( $serial, $block ) = @_;
  117. main::Log3($serial->{name},5, "OWX_DS9097 block: ".unpack "H*",$block) if ( $main::owx_async_debug > 1 );
  118. foreach my $bit (split //,unpack "b*",$block) {
  119. $serial->bit($bit);
  120. }
  121. }
  122. sub bit($) {
  123. my ( $serial, $bit ) = @_;
  124. if (defined (my $hwdevice = $serial->{hash}->{USBDev})) {
  125. my $sp1 = $bit == 1 ? "\xFF" : "\x00";
  126. main::Log3($serial->{name},5, sprintf("OWX_DS9097 bit: %02x",ord($sp1))) if ( $main::owx_async_debug > 2 );
  127. $hwdevice->write($sp1);
  128. } else {
  129. die "no USBDev";
  130. }
  131. }
  132. sub pt_query($) {
  133. my ( $serial, $query ) = @_;
  134. my @bitsout = split //,$query;
  135. my $numbits = @bitsout;
  136. my $bitsin = "";
  137. my $bit;
  138. return PT_THREAD(sub {
  139. my ( $thread ) = @_;
  140. PT_BEGIN($thread);
  141. main::Log3($serial->{name},5, "OWX_DS9097 pt_query out: ".$query) if( $main::owx_async_debug );
  142. while ($serial->poll()) {};
  143. $serial->{string_raw} = "";
  144. while (defined ($bit = shift @bitsout)) {
  145. $serial->bit($bit);
  146. };
  147. main::OWX_ASYNC_TaskTimeout($serial->{hash},gettimeofday+main::AttrVal($serial->{name},"timeout",1));
  148. PT_WAIT_UNTIL(length($serial->{string_raw}) >= $numbits);
  149. $bitsin = join "", map { ($_ == 0xFF) ? "1" : "0" } unpack "C*",$serial->{string_raw};
  150. main::Log3($serial->{name},5,"OWX_DS9097 pt_query in: ".$bitsin) if ( $main::owx_async_debug );
  151. PT_EXIT($bitsin);
  152. PT_END;
  153. });
  154. }
  155. sub read() {
  156. my ($serial) = @_;
  157. if (defined (my $hwdevice = $serial->{hash}->{USBDev})) {
  158. my $string_part = $hwdevice->input();
  159. if (defined $string_part and length($string_part) > 0) {
  160. $serial->{string_raw} .= $string_part;
  161. main::Log3($serial->{name},5, "OWX_DS9097 read: Loop no. $serial->{num_reads}, Receiving: ".unpack("H*",$string_part)) if( $main::owx_async_debug > 1 );
  162. return 1;
  163. } elsif ($main::owx_async_debug > 2) {
  164. main::Log3($serial->{name},5, "OWX_DS9097 read: Loop no. $serial->{num_reads}, no data read:");
  165. foreach my $i (0..6) {
  166. my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = caller($i);
  167. main::Log3($serial->{name},5, "$subroutine $filename $line");
  168. }
  169. }
  170. }
  171. return undef;
  172. }
  173. sub pt_next ($$) {
  174. my ($serial,$context,$mode)=@_;
  175. my $id_bit_number = 1;
  176. my ($pt_query,$search_direction,@search,$query,$result);
  177. return PT_THREAD(sub {
  178. my ( $thread ) = @_;
  179. PT_BEGIN($thread);
  180. $serial->reset();
  181. #-- issue the normal search command \xF0 or the alarm search command \xEC
  182. if( $mode ne "alarm" ){
  183. $serial->block("\xF0");
  184. } else {
  185. $serial->block("\xEC");
  186. }
  187. #-- Response search data parsing operates bitwise
  188. @search = split //, unpack "b64", pack "C8",@{$context->{ROM_ID}};
  189. #-- bits < LastDiscrepancy are allready known:
  190. $query = "";
  191. $result = "";
  192. if ($context->{LastDiscrepancy} > 0) {
  193. while ( $id_bit_number < $context->{LastDiscrepancy} ) {
  194. $query.= "11".$search[$id_bit_number-1];
  195. $id_bit_number++;
  196. }
  197. # $id_bit_number now is equal LastDiscrepancy
  198. if ($id_bit_number < 65) {
  199. $query.="111";
  200. $search[$id_bit_number-1] = 1;
  201. $id_bit_number++;
  202. }
  203. }
  204. $query.="11" if ($id_bit_number != 57 and $id_bit_number < 65);
  205. $pt_query = $serial->pt_query($query);
  206. while (1) {
  207. PT_WAIT_THREAD($pt_query);
  208. die $pt_query->PT_CAUSE() if ($pt_query->PT_STATE() == PT_ERROR || $pt_query->PT_STATE() == PT_CANCELED);
  209. my $ret = $pt_query->PT_RETVAL();
  210. die "unparsable return of query '$ret'" unless ($ret =~ /(1|0)(1|0)$/);
  211. $result.=$ret;
  212. last if ( $id_bit_number > 64 );
  213. if ( $id_bit_number < 65 ) {
  214. my $id_bit = $1;
  215. my $cmp_id_bit = $2;
  216. if( ($id_bit == 1) && ($cmp_id_bit == 1) ){
  217. main::Log3 ($serial->{name},5, "no devices present at id_bit_number=$id_bit_number");
  218. last;
  219. }
  220. if ( $id_bit != $cmp_id_bit ){
  221. $search_direction = $id_bit;
  222. } else {
  223. $search_direction = 0;
  224. }
  225. # set or clear the bit in the ROM byte rom_byte_number
  226. # with mask rom_byte_mask
  227. $search[$id_bit_number-1] = $search_direction;
  228. # serial number search direction write bit
  229. $serial->bit($search_direction);
  230. $result.="$search_direction";
  231. main::Log3 ($serial->{name},5,"id_bit_number: $id_bit_number, search_direction: $search_direction, ROM_ID: ".sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",unpack "C8",pack "b64", join "",@search)) if ($main::owx_async_debug);
  232. $id_bit_number++;
  233. last if ($id_bit_number > 64);
  234. $pt_query = $serial->pt_query("11") unless ( $id_bit_number == 57 );
  235. }
  236. #bits 57-64 are CRC and can be calculated from bits 1-56
  237. if ( $id_bit_number == 57 ) {
  238. my @crc = (0,unpack("C7", pack ("b64", join ("",@search[0..55]))));
  239. @search[56..63] = split //,unpack "b8",pack "C1",(main::OWX_CRC(\@crc));
  240. $query = "";
  241. while ($id_bit_number < 65) {
  242. $query.= "11".$search[$id_bit_number-1];
  243. $id_bit_number++;
  244. }
  245. $pt_query = $serial->pt_query($query);
  246. }
  247. }
  248. die "unexpected length of result: ".length($result).", expected: 192" if (length($result)!=192);
  249. my $bit_number = 0;
  250. my @bits = split //,$result;
  251. my $last_zero = 0;
  252. my @found = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
  253. if ($main::owx_async_debug>2) {
  254. my @results;
  255. for (my $i=0;$i<64;$i++) {
  256. push @results,substr($result,$i*3,3);
  257. }
  258. main::Log3 ($serial->{name},5,"result: ".join(":",@results));
  259. main::Log3 ($serial->{name},5,"bits: ".join(",",@bits));
  260. main::Log3 ($serial->{name},5,"search: ".join(",",@search));
  261. }
  262. while(@bits) {
  263. my $id_bit = shift @bits;
  264. my $cmp_id_bit = shift @bits;
  265. shift @bits;
  266. if ($id_bit == 1 and $cmp_id_bit == 1) {
  267. last;
  268. } elsif ($id_bit == 0 and $cmp_id_bit == 0) {
  269. if ($search[$bit_number] == 0) {
  270. $last_zero = $bit_number+1;
  271. }
  272. $found[$bit_number] = $search[$bit_number];
  273. } else {
  274. $found[$bit_number] = $id_bit;
  275. }
  276. $bit_number++;
  277. }
  278. @{$context->{ROM_ID}} = unpack "C8", pack "b64", join "",@found;
  279. $context->{LastDiscrepancy} = $last_zero;
  280. main::Log3 ($serial->{name},5,"bit_number: $bit_number, LastDiscrepancy: $last_zero, ROM_ID: ".sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$context->{ROM_ID}})) if ($main::owx_async_debug);
  281. PT_END;
  282. });
  283. }
  284. 1;