| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441 |
- ########################################################################################
- #
- # OWX_SER.pm
- #
- # FHEM module providing hardware dependent functions for the serial (USB) interface of OWX
- #
- # Prof. Dr. Peter A. Henning
- # Norbert Truchsess
- #
- # $Id: OWX_SER.pm 6398 2014-08-12 21:22:18Z ntruchsess $
- #
- ########################################################################################
- #
- # Provides the following methods for OWX
- #
- # Alarms
- # Complex
- # Define
- # Discover
- # Init
- # Reset
- # Verify
- #
- ########################################################################################
- package OWX_SER;
- use strict;
- use warnings;
- use vars qw/@ISA/;
- require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
- use Time::HiRes qw( gettimeofday );
- use ProtoThreads;
- no warnings 'deprecated';
- ########################################################################################
- #
- # Constructor
- #
- ########################################################################################
- sub new() {
- my $class = shift;
- my $self = {
- interface => "serial",
- #-- module version
- version => 5.2,
- alarmdevs => [],
- devs => [],
- fams => [],
- };
- return bless $self,$class;
- }
- sub poll() {
- my ( $self ) = @_;
- my $hash = $self->{hash};
- if(defined($hash->{FD})) {
- my ($rout, $rin) = ('', '');
- vec($rin, $hash->{FD}, 1) = 1;
- select($rout=$rin, undef, undef, 0.1);
- my $mfound = vec($rout, $hash->{FD}, 1);
- if ($mfound) {
- if ($self->read()) {
- return 1;
- } else {
- main::OWX_ASYNC_Disconnect($hash);
- }
- }
- }
- return undef;
- }
- ########################################################################################
- #
- # Public methods
- #
- ########################################################################################
- #
- # Define - Implements Define method
- #
- # Parameter def = definition string
- #
- # Return undef if ok, otherwise error message
- #
- ########################################################################################
- sub Define ($$) {
- my ($self,$hash,$def) = @_;
- my @a = split("[ \t][ \t]*", $def);
- $self->{name} = $hash->{NAME};
- #-- check syntax
- if(int(@a) < 3){
- return "OWX_SER: Syntax error - must be define <name> OWX <serial-device>"
- }
- my $dev = $a[2];
- my $device;
- #-- network attached serial:
- if ( $dev =~ m/^(.+):([0-9]+)$/ ) {
- $hash->{DeviceName} = $dev;
- $device = $dev;
- } else {
- #-- when the specified device name contains @<digits> already, use it as supplied
- if ( $dev !~ m/\@\d*/ ){
- $hash->{DeviceName} = $dev."\@9600";
- }
- my $baudrate;
- ($device,$baudrate) = split('@',$dev);
- $self->{baud} = $baudrate ? $baudrate : 9600;
- }
- #-- let fhem.pl MAIN call OWX_Ready when setup is done.
- $main::readyfnlist{"$hash->{NAME}.$device"} = $hash;
- $self->{hash} = $hash;
- return undef;
- }
- ########################################################################################
- #
- # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
- #
- # Return number of alarmed devices
- #
- ########################################################################################
- sub get_pt_alarms() {
- my ($self) = @_;
- my $pt_next;
- return PT_THREAD(sub {
- my ($thread) = @_;
- PT_BEGIN($thread);
- $thread->{alarmdevs} = [];
- #-- Discover all alarmed devices on the 1-Wire bus
- $self->first($thread);
- do {
- $pt_next = $self->pt_next($thread,"alarms");
- PT_WAIT_THREAD($pt_next);
- die $pt_next->PT_CAUSE() if ($pt_next->PT_STATE() == PT_ERROR || $pt_next->PT_STATE() == PT_CANCELED);
- $self->next_response($thread,"alarms");
- } while( $thread->{LastDeviceFlag}==0 );
- main::Log3($self->{name},5, " Alarms = ".join(' ',@{$thread->{alarmdevs}}));
- PT_EXIT($thread->{alarmdevs});
- PT_END;
- });
- }
- ########################################################################################
- #
- # Discover - Find devices on the 1-Wire bus
- #
- # Parameter hash = hash of bus master
- #
- # Return 1, if alarmed devices found, 0 otherwise.
- #
- ########################################################################################
- sub get_pt_discover() {
- my ($self) = @_;
- my $pt_next;
- return PT_THREAD(sub {
- my ($thread) = @_;
- PT_BEGIN($thread);
- #-- Discover all alarmed devices on the 1-Wire bus
- $self->first($thread);
- do {
- $pt_next = $self->pt_next($thread,"discover");
- PT_WAIT_THREAD($pt_next);
- die $pt_next->PT_CAUSE() if ($pt_next->PT_STATE() == PT_ERROR || $pt_next->PT_STATE() == PT_CANCELED);
- $self->next_response($thread,"discover");
- } while( $thread->{LastDeviceFlag}==0 );
- PT_EXIT($thread->{devs});
- PT_END;
- });
- }
- ########################################################################################
- #
- # Init - Initialize the 1-wire device
- #
- # Parameter hash = hash of bus master
- #
- # Return 1 or Errormessage : not OK
- # 0 or undef : OK
- #
- ########################################################################################
- sub initialize() {
- my ($self) = @_;
- my ($i,$j,$k,$l,$res,$ret,$ress);
- #-- Second step in case of serial device: open the serial device to test it
- my $hash = $self->{hash};
- my $msg = "OWX_SER: Serial device $hash->{DeviceName}";
- main::DevIo_OpenDev($hash,$self->{reopen},undef);
- return undef unless $hash->{STATE} eq "opened";
- #-- Third step detect busmaster on serial interface
- my $name = $self->{name};
- my $ress0 = "OWX_SER::Detect 1-Wire bus $name: interface ";
- $ress = $ress0;
- my $interface;
-
- require "$main::attr{global}{modpath}/FHEM/OWX_DS2480.pm";
- my $ds2480 = OWX_DS2480->new($self);
- if (defined (my $hwdevice = $hash->{USBDev})) {
- #force master reset in DS2480
- $hwdevice->reset_error();
- $hwdevice->purge_all;
- $hwdevice->baudrate(4800);
- $hwdevice->write_settings;
- $hwdevice->write("\x00");
- select(undef,undef,undef,0.5);
- #-- timing byte for DS2480
- $ds2480->start_query();
- $hwdevice->baudrate(9600);
- $hwdevice->write_settings;
- $ds2480->query("\xC1",0);
- $hwdevice->baudrate($self->{baud});
- $hwdevice->write_settings;
- } else {
- #-- for serial over network we cannot reset but just send the timing byte
- $ds2480->start_query();
- $ds2480->query("\xC1",0);
- }
- #-- Max 4 tries to detect an interface
- for($l=0;$l<100;$l++) {
- #-- write 1-Wire bus (Fig. 2 of Maxim AN192)
- $ds2480->start_query();
- $ds2480->query("\x17\x45\x5B\x0F\x91",5);
- my $until = gettimeofday + main::AttrVal($hash->{NAME},"timeout",1);
- eval {
- do {
- $ds2480->poll();
- } until ($ds2480->response_ready() or gettimeofday >= $until);
- };
- $res = $ds2480->{string_in};
- #-- process 4/5-byte string for detection
- if( !defined($res)){
- $res="";
- $ret=1;
- }elsif( ($res eq "\x16\x44\x5A\x00\x90") || ($res eq "\x16\x44\x5A\x00\x93")){
- $ress .= "master DS2480 detected for the first time";
- $interface="DS2480";
- $ret=0;
- } elsif( $res eq "\x17\x45\x5B\x0F\x91"){
- $ress .= "master DS2480 re-detected";
- $interface="DS2480";
- $ret=0;
- } elsif( ($res eq "\x17\x0A\x5B\x0F\x02") || ($res eq "\x00\x17\x0A\x5B\x0F\x02") || ($res eq "\x30\xf8\x00") || ($res eq "\x06\x00\x09\x07\x80") || ($res eq "\x17\x41\xAB\x20\xFC")){
- $ress .= "passive DS9097 detected";
- $interface="DS9097";
- $ret=0;
- } else {
- $ret=1;
- }
- last
- if( $ret==0 );
- $ress .= "not found, answer was ";
- for($i=0;$i<length($res);$i++){
- $j=int(ord(substr($res,$i,1))/16);
- $k=ord(substr($res,$i,1))%16;
- $ress.=sprintf "0x%1x%1x ",$j,$k;
- }
- main::Log3($hash->{NAME},4, $ress);
- $ress = $ress0;
- #-- sleeping for some time
- select(undef,undef,undef,0.5);
- }
- if( $ret == 1 ){
- $interface=undef;
- $ress .= "not detected, answer was ";
- for($i=0;$i<length($res);$i++){
- $j=int(ord(substr($res,$i,1))/16);
- $k=ord(substr($res,$i,1))%16;
- $ress.=sprintf "0x%1x%1x ",$j,$k;
- }
- }
- $self->{interface} = $interface;
- main::Log3($hash->{NAME},3, $ress);
- if ($interface eq "DS2480") {
- return $ds2480;
- } elsif ($interface eq "DS9097") {
- require "$main::attr{global}{modpath}/FHEM/OWX_DS9097.pm";
- return OWX_DS9097->new($self);
- } else {
- die $ress;
- }
- }
- sub exit($) {
- my ($self) = @_;
- main::DevIo_Disconnected($self->{hash});
- $self->{interface} = "serial";
- $self->{reopen} = 1;
- }
- ########################################################################################
- #
- # Verify - Verify a particular device on the 1-Wire bus
- #
- # Parameter hash = hash of bus master, dev = 8 Byte ROM ID of device to be tested
- #
- # Return 1 : device found
- # 0 : device not
- #
- ########################################################################################
- sub get_pt_verify($) {
- my ($self,$dev) = @_;
- my $pt_next;
- return PT_THREAD(sub {
- my ($thread) = @_;
- my $i;
- PT_BEGIN($thread);
- #-- from search string to byte id
- my $devs=$dev;
- $devs=~s/\.//g;
- for($i=0;$i<8;$i++){
- $thread->{ROM_ID}->[$i]=hex(substr($devs,2*$i,2));
- }
- #-- reset the search state
- $thread->{LastDiscrepancy} = 65;
- $thread->{LastDeviceFlag} = 0;
- $thread->{LastFamilyDiscrepancy} = 0;
-
- #-- now do the search
- $pt_next = $self->pt_next($thread,"verify");
- PT_WAIT_THREAD($pt_next);
- die $pt_next->PT_CAUSE() if ($pt_next->PT_STATE() == PT_ERROR || $pt_next->PT_STATE() == PT_CANCELED);
- $self->next_response($thread,"verify");
- my $dev2=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$thread->{ROM_ID}});
- #-- reset the search state
- $thread->{LastDiscrepancy} = 0;
- $thread->{LastDeviceFlag} = 0;
- $thread->{LastFamilyDiscrepancy} = 0;
- #-- check result
- if ($dev eq $dev2){
- PT_EXIT(1);
- }else{
- PT_EXIT(0);
- }
- PT_END;
- });
- };
- #######################################################################################
- #
- # First - Find the 'first' devices on the 1-Wire bus
- #
- # Parameter hash = hash of bus master, mode
- #
- # Return 1 : device found, ROM number pushed to list
- # 0 : no device present
- #
- ########################################################################################
- sub first($) {
- my ($self,$thread) = @_;
- #-- reset the search state
- $thread->{LastDiscrepancy} = 0;
- $thread->{LastDeviceFlag} = 0;
- $thread->{LastFamilyDiscrepancy} = 0;
- $thread->{ROM_ID} = [0,0,0,0,0,0,0,0];
- }
- sub next_response($) {
- my ($self,$thread,$mode) = @_;
- #-- character version of device ROM_ID, first byte = family
- my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$thread->{ROM_ID}});
- #-- mode was to verify presence of a device
- if ($mode eq "verify") {
- main::Log3($self->{name},5, "OWX_SER::Search: device verified $dev");
- #--check if we really found a device
- } elsif( main::OWX_CRC($thread->{ROM_ID})!= 0) {
- #-- reset the search
- main::Log3($self->{name},1, "OWX_SER::Search CRC failed : $dev");
- $thread->{LastDiscrepancy} = 0;
- $thread->{LastDeviceFlag} = 0;
- $thread->{LastFamilyDiscrepancy} = 0;
- die "OWX_SER::Search CRC failed : $dev";
- }
- #--
- if( $thread->{LastDiscrepancy}==$thread->{LastFamilyDiscrepancy} ){
- $thread->{LastFamilyDiscrepancy}=0;
- }
- #-- mode was to discover devices
- if( $mode eq "discover" ) {
- #-- check families
- my $famfnd=0;
- foreach (@{$self->{fams}}){
- if( substr($dev,0,2) eq $_ ){
- #-- if present, set the fam found flag
- $famfnd=1;
- last;
- }
- }
- push(@{$self->{fams}},substr($dev,0,2)) if( !$famfnd );
- foreach (@{$thread->{devs}}){
- if( $dev eq $_ ){
- #-- if present, set the last device found flag
- $thread->{LastDeviceFlag}=1;
- last;
- }
- }
- if( $thread->{LastDeviceFlag}!=1 ){
- #-- push to list
- push(@{$thread->{devs}},$dev);
- main::Log3($self->{name},5, "OWX_SER::Search: new device found $dev");
- }
- #-- mode was to discover alarm devices
- } elsif ( $mode eq "alarms" ) {
- for(my $i=0;$i<@{$thread->{alarmdevs}};$i++){
- if( $dev eq ${$thread->{alarmdevs}}[$i] ){
- #-- if present, set the last device found flag
- $thread->{LastDeviceFlag}=1;
- last;
- }
- }
- if( $thread->{LastDeviceFlag}!=1 ){
- #--push to list
- push(@{$thread->{alarmdevs}},$dev);
- main::Log3($self->{name},5, "OWX_SER::Search: new alarm device found $dev");
- }
- }
- return 1;
- }
- 1;
|