| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- ########################################################################################
- #
- # OWX_DS2480.pm
- #
- # FHEM module providing hardware dependent functions for the DS2480 interface of OWX
- #
- # Prof. Dr. Peter A. Henning
- # Norbert Truchsess
- #
- # $Id: OWX_DS2480.pm 6398 2014-08-12 21:22:18Z ntruchsess $
- #
- ########################################################################################
- #
- # Provides the following methods for OWX
- #
- # Alarms
- # Complex
- # Define
- # Discover
- # Init
- # Reset
- # Verify
- #
- ########################################################################################
- package OWX_DS2480;
- use strict;
- use warnings;
- use Time::HiRes qw( gettimeofday tv_interval usleep );
- use ProtoThreads;
- no warnings 'deprecated';
- use vars qw/@ISA/;
- @ISA='OWX_SER';
- sub new($) {
- my ($class,$serial) = @_;
-
- return bless $serial,$class;
- }
- ########################################################################################
- #
- # Complex - Send match ROM, data block and receive bytes as response
- #
- # Parameter hash = hash of bus master,
- # owx_dev = ROM ID of device
- # data = string to send
- # numread = number of bytes to receive
- #
- # Return response, if OK
- # 0 if not OK
- #
- ########################################################################################
- sub get_pt_execute($$$$) {
- my ($self, $reset, $dev, $writedata, $numread) = @_;
- return PT_THREAD(sub {
- my ($thread) = @_;
-
- PT_BEGIN($thread);
-
- $self->reset() if ($reset);
-
- if (defined $writedata or $numread) {
-
- my $select;
-
- #-- has match ROM part
- if( $dev ) {
-
- #-- ID of the device
- my $owx_rnf = substr($dev,3,12);
- my $owx_f = substr($dev,0,2);
-
- #-- 8 byte 1-Wire device address
- my @rom_id =(0,0,0,0 ,0,0,0,0);
- #-- from search string to byte id
- $dev=~s/\.//g;
- for(my $i=0;$i<8;$i++){
- $rom_id[$i]=hex(substr($dev,2*$i,2));
- }
- $select=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id);
- #-- has no match ROM part, issue skip ROM command (0xCC:)
- } else {
- $select="\xCC";
- }
- if (defined $writedata) {
- $select.=$writedata;
- }
- #-- has receive data part
- if( $numread ) {
- #$numread += length($data);
- for( my $i=0;$i<$numread;$i++){
- $select .= "\xFF";
- };
- }
-
- #-- for debugging
- if( $main::owx_async_debug > 1){
- main::Log3($self->{name},5,"OWX_SER::Execute: Sending out ".unpack ("H*",$select));
- }
- $self->block($select);
- }
-
- main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
- PT_WAIT_UNTIL($self->response_ready());
-
- if ($reset and !$self->reset_response()) {
- die "reset failure";
- }
-
- my $res = $self->{string_in};
- #-- for debugging
- if( $main::owx_async_debug > 1){
- main::Log3($self->{name},5,"OWX_SER::Execute: Receiving ".unpack ("H*",$res));
- }
-
- if (defined $res) {
- my $writelen = defined $writedata ? split (//,$writedata) : 0;
- my @result = split (//, $res);
- my $readdata = 9+$writelen < @result ? substr($res,9+$writelen) : "";
- PT_EXIT($readdata);
- }
- PT_END;
- });
- }
- ########################################################################################
- #
- # The following subroutines in alphabetical order are only for a DS2480 bus interface
- #
- #########################################################################################
- #
- # Block_2480 - Send data block (Fig. 6 of Maxim AN192)
- #
- # Parameter hash = hash of bus master, data = string to send
- #
- # Return response, if OK
- # 0 if not OK
- #
- ########################################################################################
- sub block ($) {
- my ($serial,$data) =@_;
- my $data2="";
-
- my $len = length($data);
-
- #-- if necessary, prepend E1 character for data mode
- if( substr($data,0,1) ne '\xE1') {
- $data2 = "\xE1";
- }
- #-- all E3 characters have to be duplicated
- for(my $i=0;$i<$len;$i++){
- my $newchar = substr($data,$i,1);
- $data2=$data2.$newchar;
- if( $newchar eq '\xE3'){
- $data2=$data2.$newchar;
- }
- }
- #-- write 1-Wire bus as a single string
- $serial->query($data2,$len);
- }
- ########################################################################################
- #
- # query - Write to the 1-Wire bus
- #
- # Parameter: cmd = string to send to the 1-Wire bus, retlen = expected len of the response
- #
- # Return: 1 when write was successful. undef otherwise
- #
- ########################################################################################
- sub query ($$$) {
- my ($serial,$cmd,$retlen) = @_;
- main::DevIo_SimpleWrite($serial->{hash},$cmd,0);
- $serial->{retlen} += $retlen;
- }
- ########################################################################################
- #
- # read - read response from the 1-Wire bus
- # to be called from OWX ReadFn.
- #
- # Parameter: -
- #
- # Return: 1 when at least 1 byte was read. undef otherwise
- #
- ########################################################################################
- sub read() {
- my ($serial) = @_;
- #-- read the data
- my $string_part = main::DevIo_DoSimpleRead($serial->{hash});
- $serial->{num_reads}++;
- #return undef unless defined $string_part;
- if (defined $string_part) {
- my $count_in = length ($string_part);
- $serial->{string_in} .= $string_part;
- $serial->{retcount} += $count_in;
- main::Log3($serial->{name},5, "OWX_DS2480 read: Loop no. $serial->{num_reads}, Receiving: ".unpack("H*",$string_part)) if( $main::owx_async_debug > 1 );
- return $count_in > 0 ? 1 : undef;
- } elsif ($main::owx_async_debug > 2) {
- main::Log3($serial->{name},5, "OWX_DS2480 read: Loop no. $serial->{num_reads}, no data read:");
- foreach my $i (0..6) {
- my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = caller($i);
- main::Log3($serial->{name},5, "$subroutine $filename $line");
- }
- }
- return undef;
- }
- sub response_ready() {
- my ($serial) = @_;
- if ($serial->{retcount} >= $serial->{retlen}) {
- main::Log3($serial->{name},5, "OWX_DS2480 read: After loop no. $serial->{num_reads} received: ".unpack("H*",$serial->{string_in}));
- return 1;
- }
- return 0;
- }
- sub start_query() {
- my ($serial) = @_;
- #read and discard any outstanding data from previous commands:
- while($serial->poll()) {};
- $serial->{string_in} = "";
- $serial->{num_reads} = 0;
- $serial->{retlen} = 0;
- $serial->{retcount} = 0;
- }
- ########################################################################################
- #
- # reset - Reset the 1-Wire bus (Fig. 4 of Maxim AN192)
- #
- # Parameter hash = hash of bus master
- #
- # Return 1 : OK
- # 0 : not OK
- #
- ########################################################################################
- sub reset() {
- my ($serial) = @_;
- my ($res,$r1,$r2);
- my $name = $serial->{name};
- $serial->start_query();
- #-- if necessary, prepend \xE3 character for command mode
- #-- Reset command \xC5
- #-- write 1-Wire bus
- $serial->query("\xE3\xC5",1);
- #-- sleeping for some time (value of 0.07 taken from original OWX_Query_DS2480)
- select(undef,undef,undef,0.07);
- }
- sub reset_response() {
- my ($serial) = @_;
- my $res = ord(substr($serial->{string_in},0,1));
- my $name = $serial->{name};
-
- if( !($res & 192) ) {
- main::Log3($name,4, "OWX_DS2480 reset failure on bus $name");
- return 0;
- }
-
- if( ($res & 3) == 2 ) {
- main::Log3($name,4, "OWX_DS2480 reset Alarm presence detected on bus $name");
- $serial->{ALARMED} = "yes";
- } else {
- $serial->{ALARMED} = "no";
- }
- $serial->{string_in} = substr($serial->{string_in},1);
- return 1;
- }
- ########################################################################################
- #
- # Search_2480 - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
- # search state.
- #
- # Parameter hash = hash of bus master, mode=alarm,discover or verify
- #
- # Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
- # or only in owx_ROM_ID (LastDeviceFlag=1)
- # 0 : device not found, or ot searched at all
- #
- ########################################################################################
- sub pt_next ($$) {
- my ($serial,$context,$mode)=@_;
- my $id_bit_number = 1;
- my $rom_byte_number = 0;
- my $rom_byte_mask = 1;
- my $last_zero = 0;
- my ($sp1,$sp2,$pt_query,$search_direction);
- return PT_THREAD(sub {
- my ( $thread ) = @_;
- PT_BEGIN($thread);
- #-- clear 16 byte of search data
- $context->{search} = [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0];
- #-- Output search data construction (Fig. 9 of Maxim AN192)
- # operates on a 16 byte search response = 64 pairs of two bits
- while ( $id_bit_number <= 64) {
- #-- address single bits in a 16 byte search string
- my $newcpos = int(($id_bit_number-1)/4);
- my $newimsk = ($id_bit_number-1)%4;
- #-- address single bits in a 8 byte id string
- my $newcpos2 = int(($id_bit_number-1)/8);
- my $newimsk2 = ($id_bit_number-1)%8;
-
- if( $id_bit_number <= $context->{LastDiscrepancy}){
- #-- first use the ROM ID bit to set the search direction
- if( $id_bit_number < $context->{LastDiscrepancy} ) {
- $search_direction = ($context->{ROM_ID}->[$newcpos2]>>$newimsk2) & 1;
- #-- at the last discrepancy search into 1 direction anyhow
- } else {
- $search_direction = 1;
- }
- #-- fill into search data;
- $context->{search}->[$newcpos]+=$search_direction<<(2*$newimsk+1);
- }
- #--increment number
- $id_bit_number++;
- }
- #-- issue data mode \xE1, the normal search command \xF0 or the alarm search command \xEC
- # and the command mode \xE3 / start accelerator \xB5
- if( $mode ne "alarm" ){
- $sp1 = "\xE1\xF0\xE3\xB5";
- } else {
- $sp1 = "\xE1\xEC\xE3\xB5";
- }
- #-- issue data mode \xE1, device ID, command mode \xE3 / end accelerator \xA5
- $sp2=sprintf("\xE1%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\xE3\xA5",@{$context->{search}});
- $serial->reset();
- $serial->query($sp1,1);
- $serial->query($sp2,16);
- main::OWX_ASYNC_TaskTimeout($serial->{hash},gettimeofday+main::AttrVal($serial->{name},"timeout",2));
- PT_WAIT_UNTIL($serial->response_ready());
- die "reset failed" unless $serial->reset_response();
- my $response = substr($serial->{string_in},1);
- #-- interpret the return data
- if( length($response)!=16 ) {
- die "OWX_DS2480: Search 2nd return has wrong parameter with length = ".(length($response)."");
- }
- #-- Response search data parsing (Fig. 11 of Maxim AN192)
- # operates on a 16 byte search response = 64 pairs of two bits
- $id_bit_number = 1;
- #-- clear 8 byte of device id for current search
- $context->{ROM_ID} = [0,0,0,0 ,0,0,0,0];
- while ( $id_bit_number <= 64) {
- #-- adress single bits in a 16 byte string
- my $newcpos = int(($id_bit_number-1)/4);
- my $newimsk = ($id_bit_number-1)%4;
- #-- retrieve the new ROM_ID bit
- my $newchar = substr($response,$newcpos,1);
- #-- these are the new bits
- my $newibit = (( ord($newchar) >> (2*$newimsk) ) & 2) / 2;
- my $newdbit = ( ord($newchar) >> (2*$newimsk) ) & 1;
- #-- discrepancy=1 and ROM_ID=0
- if( ($newdbit==1) and ($newibit==0) ){
- $context->{LastDiscrepancy}=$id_bit_number;
- if( $id_bit_number < 9 ){
- $context->{LastFamilyDiscrepancy}=$id_bit_number;
- }
- }
- #-- fill into device data; one char per 8 bits
- $context->{ROM_ID}->[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8);
- #-- increment number
- $id_bit_number++;
- main::Log3 ($serial->{name},5,"id_bit_number: $id_bit_number, LastDiscrepancy: $context->{LastDiscrepancy} ROM_ID: ".sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$context->{ROM_ID}})) if ($main::owx_async_debug > 2);
- }
- PT_END;
- });
- }
- 1;
|