diff --git a/fhem/FHEM/11_OWX_CCC.pm b/fhem/FHEM/11_OWX_CCC.pm new file mode 100644 index 000000000..b51b5e279 --- /dev/null +++ b/fhem/FHEM/11_OWX_CCC.pm @@ -0,0 +1,601 @@ +######################################################################################## +# +# OWX_CCC.pm +# +# FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX +# +# Prof. Dr. Peter A. Henning +# +# $Id$ +# +######################################################################################## +# +# Provides the following methods for OWX +# +# Define +# Detect +# Alarms +# Complex +# Discover +# Init +# Read +# ReadLow +# Ready +# Reset +# Verify +# Write +# +######################################################################################## + +package OWX_CCC; + +use strict; +use warnings; + +######################################################################################## +# +# Constructor +# +######################################################################################## + +sub new($) { + my ($class,$hash) = @_; + + return bless { + hash => $hash, + #-- module version + version => "7.01" + }, $class; +} + +######################################################################################## +# +# Define - Implements Define method +# +# Parameter def = definition string +# +# Return undef if ok, otherwise error message +# +######################################################################################## + +sub Define($) { + my ($self,$def) = @_; + my $hash = $self->{hash}; + + my @a = split("[ \t][ \t]*", $def); + + #-- check syntax + if(int(@a) < 3){ + return "OWX_CCC::Define Syntax error - must be define OWX " + } + + my $name = $a[0]; + $hash->{NAME} = $name; + my $dev = $a[2]; + $hash->{DeviceName} = $dev; + + + #-- Second step in case of CUNO: See if we can open it + my $msg = "OWX_CCC::Define COC/CUNO device $dev"; + #-- hash des COC/CUNO + my $hwdevice = $main::defs{$dev}; + if(!$hwdevice){ + main::Log3 $name,1, $msg." not defined"; + return $msg." not defined"; + } + + main::Log(1,$msg." defined"); + + #-- store with OWX device + $hash->{DeviceName} = $dev; + $hash->{ASYNCHRONOUS} = 0; + $hash->{INTERFACE} = "COC/CUNO"; + $hash->{HWDEVICE} = $hwdevice; + + #-- loop for some time until the state is "Initialized" + for(my $i=0;$i<6;$i++){ + last if( $hwdevice->{STATE} eq "Initialized"); + main::Log(1,"OWX_CCC::Define Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE}); + select(undef,undef,undef,3); + } + main::Log(1, "OWX_CCC::Define Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized"); + #-- reset the 1-Wire system in COC/CUNO + main::CUL_SimpleWrite($hwdevice, "Oi"); + + #-- module version + $hash->{version} = "7.0beta2"; + main::Log3 $name,1,"OWX_CCC::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version + if( $hash->{version} ne $main::owx_version); + + #-- call low level init function for the device + $self->Init(); + return undef; +} + +######################################################################################## +# +# Detect - Find out if we have the proper interface +# +# Return 1 if ok, otherwise 0 +# +######################################################################################## + +sub Detect () { + my ($self) = @_; + my $hash = $self->{hash}; + + my ($ret,$ress); + my $name = $hash->{NAME}; + my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface "; + $ress = $ress0; + + #-- get the interface + my $interface; + my $hwdevice = $hash->{HWDEVICE}; + + select(undef,undef,undef,2); + #-- type of interface + main::CUL_SimpleWrite($hwdevice, "V"); + select(undef,undef,undef,0.01); + my ($err,$ob) = ReadLow($hwdevice); + #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V")); + #-- process result for detection + if( !defined($ob)){ + $ob=""; + $ret=0; + #-- COC + }elsif( $ob =~ m/.*CSM.*/){ + $interface="COC"; + $ress .= "DS2482 / COC detected in $hwdevice->{NAME}"; + $ret=1; + #-- CUNO + }elsif( $ob =~ m/.*CUNO.*/){ + $interface="CUNO"; + $ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}"; + $ret=1; + #-- something else + } else { + $ret=0; + } + #-- treat the failure cases + if( $ret == 0 ){ + $interface=undef; + $ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob"; + } + #-- store with OWX device + $hash->{INTERFACE} = $interface; + main::Log(1, $ress); + return $ret; +} + +######################################################################################## +# +# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set +# +# Return 0 because not implemented here. +# +######################################################################################## + +sub Alarms () { + my ($self) = @_; + + return 0; +} + +######################################################################################## +# +# Complex - Send match ROM, data block and receive bytes as response +# +# Parameter dev = ROM ID of device +# data = string to send +# numread = number of bytes to receive +# +# Return response, if OK +# 0 if not OK +# +######################################################################################## + +sub Complex ($$$) { + my ($self,$dev,$data,$numread) =@_; + my $hash = $self->{hash}; + + my $select; + my $res = ""; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + my $name = $hash->{NAME}; + + #-- 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 reverse string id + $dev=~s/\.//g; + for(my $i=0;$i<8;$i++){ + $rom_id[7-$i]=substr($dev,2*$i,2); + } + $select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id); + main::Log3 $name,5,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select; + #-- + main::CUL_SimpleWrite($hwdevice, $select); + my ($err,$ob) = ReadLow($hwdevice); + #-- padding first 9 bytes into result string, since we have this + # in the serial interfaces as well + $res .= "000000000"; + } + #-- has data part + if ( $data ){ + $self->Write($data,0); + $res .= $data; + } + #-- has receive part + if( $numread > 0 ){ + #$numread += length($data); + main::Log3 $name,5,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes"; + $res.=$self->Read($numread); + } + return $res; +} + +######################################################################################## +# +# Discover - Discover devices on the 1-Wire bus via internal firmware +# +# Return 0 : error +# 1 : OK +# +######################################################################################## + +sub Discover () { + my ($self) = @_; + my $hash = $self->{hash}; + + my $res; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + my $name = $hash->{NAME}; + + #-- zero the array + @{$hash->{DEVS}}=(); + #-- reset the busmaster + $self->Init(); + #-- get the devices + main::CUL_SimpleWrite($hwdevice, "Oc"); + select(undef,undef,undef,0.5); + my ($err,$ob) = ReadLow($hwdevice); + if( $ob ){ + # main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob); + foreach my $dx (split(/\n/,$ob)){ + next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/); + $dx =~ s/\d+\://; + my $ddx = substr($dx,14,2)."."; + #-- reverse data from culfw + for( my $i=1;$i<7;$i++){ + $ddx .= substr($dx,14-2*$i,2); + } + $ddx .= ".".substr($dx,0,2); + push (@{$hash->{DEVS}},$ddx); + } + return 1; + } else { + main::Log3 $name,1, "OWX_CCC::Discover No answer to device search"; + return 0; + } +} + +######################################################################################## +# +# Init - Low Level Init of the 1-wire device +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Init () { + my ($self) = @_; + my $hash = $self->{hash}; + my $dev = $hash->{DeviceName}; + my $name = $hash->{NAME}; + + #main::Log3 $name,1,"OWX_CCC::Init called on device $dev for bus $name, state is ".$hash->{STATE}; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + + my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm")); + #-- + # main::CUL_SimpleWrite($hwdevice, "ORm"); + # select(undef,undef,undef,0.01); + # my ($err,$ob) = ReadLow($hwdevice); + #main::Log3 $name,1,"OWX_CCC::Init gives ob=$ob "; + if( !defined($ob) ){ + #main::Log 1,"empty ORm"; + return "empty ORm"; + }elsif( length($ob) < 13){ + #main::Log 1,"short ORm of length ".length($ob); + return "short ORm of length ".length($ob); + }elsif( substr($ob,9,4) eq "OK" ){ + #main::Log 1,"=====> OK"; + $hash->{STATE} = "opened"; + return undef; + }else{ + #main::Log 1,"=====> ORm empty -> still OK ???"; + $hash->{STATE} = "opened"; + return undef; + } +} + +####################################################################################### +# +# Read - Implement the Read function +# +# Parameter numexp = expected number of bytes +# +####################################################################################### + +sub Read(@) { + my ($self,$numexp) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $state = $hash->{STATE}; + my $buffer = ""; + my $numget = 0; + my ($err,$ob); + my $try; + my $maxtry = $numexp; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + + for($try=0;$try<$maxtry;$try++){ + #main::Log(1, "Sending $hwdevice->{NAME}: OrB"; + #my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB")); + main::CUL_SimpleWrite($hwdevice, "OrB"); + ($err,$ob) = main::CUL_ReadAnswer($hwdevice,$name,0,undef); + #select(undef,undef,undef,0.01); + #($err,$ob) = ReadLow($hwdevice); + + #-- process results + if( !(defined($ob)) ){ + return ""; + #-- four bytes received makes one byte of result + }elsif( length($ob) == 4 ){ + $buffer .= sprintf("%c",hex(substr($ob,0,2))); + $numget++; + #-- 11 bytes received makes one byte of result + }elsif( length($ob) == 11 ){ + $buffer .= sprintf("%c",hex(substr($ob,9,2))); + $numget++; + #-- 18 bytes received from CUNO + }elsif( length($ob) == 18 ){ + main::OWX_WDBGL($name,4,"OWX_CCC::Read 18 bytes from CUNO: ",$ob); + #-- 20 bytes received = leftover from match + }elsif( length($ob) == 20 ){ + $maxtry++; + }else{ + main::Log3 $name,1,"OWX_CCC::Read unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{NAME}; + } + } + if( $numget >= $numexp){ + main::OWX_WDBGL($name,1,"OWX_CCC::Read from CUNO with error=$err: ",$buffer); + return $buffer + #-- ultimate failure + }else{ + main::Log3 $name, 1,"OWX_CCC::Read $name: $numget of $numexp bytes with error=$err at state $state, this is an unrecoverable error"; + #main::DevIo_Disconnected($hwdevice); + return ""; + } +} + +######################################################################################## +# +# ReadLow - Replacement for CUL_ReadAnswer for better control +# +# Parameter: hash = hash of bus master +# +# Return: string received +# +######################################################################################## + +sub ReadLow($) +{ + my ($hwdevice) = @_; + + my $type = $hwdevice->{TYPE}; + my $name = $hwdevice->{NAME}; + + my $arg =""; + my $anydata=0; + my $regexp =undef; + + my ($mculdata, $rin) = ("", ''); + my $buf; + my $to = 3; # 3 seconds timeout + $to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less + for(;;) { + return ("Device lost when reading answer for get $arg", undef) + if(!$hwdevice->{FD}); + + vec($rin, $hwdevice->{FD}, 1) = 1; + my $nfound = select($rin, undef, undef, $to); + if($nfound < 0) { + next if ($! == EAGAIN() || $! == EINTR() || $! == 0); + my $err = $!; + #main::DevIo_Disconnected($hwdevice); + main::Log 1,"============================> DISCOINNECTING"; + return("ReadLow $arg: $err", undef); + } + return ("Timeout reading answer for get $arg", undef) + if($nfound == 0); + $buf = main::DevIo_SimpleRead($hwdevice); + return ("No data", undef) if(!defined($buf)); + + if($buf) { + main::Log3 $name,5, "OWX_CCC::ReadLow $buf"; + $mculdata .= $buf; + } + + # \n\n is socat special + if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) { + if($regexp && $mculdata !~ m/$regexp/) { + main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString}); + } else { + return (undef, $mculdata) + } + } + } +} + +######################################################################################## +# +# Ready - Implement the Ready function +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Ready () { + my ($self) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $success; + + $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init") + if($hash->{STATE} eq "disconnected"); + + return $success; +} + +######################################################################################## +# +# Reset - Reset the 1-Wire bus +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Reset () { + my ($self) = @_; + my $hash = $self->{hash}; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + + my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb")); + + if( substr($ob,9,4) eq "OK:1" ){ + return 1; + }else{ + return 0 + } +} + +######################################################################################## +# +# Verify - Verify a particular device on the 1-Wire bus +# +# Parameter dev = 8 Byte ROM ID of device to be tested +# +# Return 1 : device found +# 0 : device not +# +######################################################################################## + +sub Verify ($) { + my ($self,$dev) = @_; + my $hash = $self->{hash}; + + my $i; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + + #-- Ask the COC/CUNO + main::CUL_SimpleWrite($hwdevice, "OCf"); + #-- sleeping for some time + select(undef,undef,undef,3); + main::CUL_SimpleWrite($hwdevice, "Oc"); + select(undef,undef,undef,0.5); + my ($err,$ob) = $self->($hwdevice); + if( $ob ){ + foreach my $dx (split(/\n/,$ob)){ + next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/); + $dx =~ s/\d+\://; + my $ddx = substr($dx,14,2)."."; + #-- reverse data from culfw + for( my $i=1;$i<7;$i++){ + $ddx .= substr($dx,14-2*$i,2); + } + $ddx .= ".".substr($dx,0,2); + return 1 if( $dev eq $ddx); + } + } + return 0; +} + +####################################################################################### +# +# Write - Implement the write function +# +# Parameter cmd = string to be sent +# reset = 1 if initial bus reset has to be done +# +######################################################################################## + +sub Write(@) { + my ($self,$cmd, $reset) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $count_out; + + if($hash->{STATE} eq "disconnected"){ + main::Log3 $name,4,"OWX_CCC::Write attempted to disconnected device $name"; + return undef; + } + + #-- if necessary, perform a reset operation + $self->Reset() + if( $reset ); + + my ($i,$j,$k); + my $res = ""; + + #-- get the interface + my $hwdevice = $hash->{HWDEVICE}; + + for( $i=0;$i +

OWX_CCC

+See OWX +end html +=begin html_DE + + +

OWX_CCC

+Deutsche Dokumentation im Wiki vorhanden, die englische Version gibt es hier: OWX +=end html_DE \ No newline at end of file diff --git a/fhem/FHEM/11_OWX_FRM.pm b/fhem/FHEM/11_OWX_FRM.pm new file mode 100644 index 000000000..e90a5177f --- /dev/null +++ b/fhem/FHEM/11_OWX_FRM.pm @@ -0,0 +1,635 @@ +######################################################################################## +# +# OWX_FRM.pm +# +# FHEM module providing hardware dependent functions for the FRM interface of OWX +# +# Prof. Dr. Peter A. Henning +# +# $Id$ +# +######################################################################################## +# +# Provides the following methods for OWX +# +# Define +# Detect +# Alarms +# Complex +# Discover +# Read +# Ready +# Verify +# Write +# observer +# device_to_firmata +# firmata_to_device +# +######################################################################################## + +package OWX_FRM; + +use strict; +use warnings; + +use Data::Dumper; + +use Device::Firmata::Constants qw/ :all /; + +######################################################################################## +# +# Constructor +# +######################################################################################## + +sub new($) { + my ($class,$hash) = @_; + + return bless { + hash => $hash + }, $class; +} + +######################################################################################## +# +# Define - Implements Define method +# +# Parameter def = definition string +# +# Return undef if ok, otherwise error message +# +######################################################################################## + +sub Define($) { + my ($self,$def) = @_; + my $hash = $self->{hash}; + + if (!defined($main::modules{FRM})) { + my $ret = "OWX_FRM::Define module FRM not yet loaded, please define an FRM device first."; + main::Log3 $hash->{NAME},1,$ret; + return $ret; + } + + my @a = split( "[ \t][ \t]*", $def ); + my $u = "wrong syntax: define FRM_XXX pin"; + return $u unless int(@a) > 0; + + $self->{pin} = $a[2]; + $self->{id} = 0; + $self->{name} = $hash->{NAME}; + $self->{hash} = $hash; + + #-- when the specified device name contains @, remove these. + #my $dev =~ s/\@\d*//; + #main::AssignIoPort($hash); + + #-- store with OWX device + #$hash->{DeviceName} = $dev; + $hash->{INTERFACE} = "firmata"; + $hash->{HWDEVICE} = $a[2]; + $hash->{ASYNCHRONOUS} = 0; + + #-- module version + $hash->{version} = "7.01"; + main::Log3 $hash->{NAME},1,"OWX_FRM::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version + if( $hash->{version} ne $main::owx_version); + + #-- call low level init function for the device + main::InternalTimer(time()+55, "OWX_FRM::Init", $self,0); + return undef; + +} + +######################################################################################## +# +# Detect - Find out if we have the proper interface +# +# Return 1 if ok, otherwise 0 +# +######################################################################################## + +sub Detect () { + my ($self) = @_; + my $hash = $self->{hash}; + + my $ret; + my $name = $hash->{NAME}; + my $ress = "OWX: 1-Wire bus $name: interface "; + + my $iodev = $hash->{IODev}; + if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) { + $ret=1; + $ress .= "Firmata detected in $iodev->{NAME}"; + } else { + $ret=0; + $ress .= defined $iodev ? "$iodev->{NAME} is not connected to Firmata" : "not associated to any FRM device"; + } + main::Log(1, $ress); + return $ret; +} + +######################################################################################## +# +# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set +# +# Return number of alarmed devices +# +######################################################################################## + +sub Alarms() { + my ($self) = @_; + my $hash = $self->{hash}; + + #-- get the interface + my $frm = $hash->{IODev}; + return 0 unless defined $frm; + my $firmata = $frm->{FirmataDevice}; + my $pin = $hash->{PIN}; + return 0 unless ( defined $firmata and defined $pin ); + $hash->{ALARMDEVS} = undef; + $firmata->onewire_search_alarms($hash->{PIN}); + my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec + for (my $i=0;$i<$times;$i++) { + if (main::FRM_poll($hash->{IODev})) { + if (defined $hash->{ALARMDEVS}) { + return 1; + } + } else { + select (undef,undef,undef,0.05); + } + } + $hash->{ALARMDEVS} = []; + return 1; +} + +######################################################################################## +# +# Init - Initialize the 1-wire device +# +# Parameter hash = hash of bus master +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Init() { + my ($self) = @_; + my $hash = $self->{hash}; + my $dev = $hash->{DeviceName}; + my $name = $hash->{NAME}; + my $msg; + + #FRM_OWX_Init($hash,$pin); + + main::Log 1,"==================> STARTING INIT of 11_OWX_FRM"; + + my @args = (9); + $hash->{PIN} = 9; + + my $ret = main::FRM_Init_Pin_Client($hash,\@args,PIN_ONEWIRE); + if (defined $ret){ + $msg = "Error ".$ret; + main::Log3 $name,1,"OWX_FRM::Init ".$msg; + return $msg; + } + + my $firmata = main::FRM_Client_FirmataDevice($hash); + + my $pin = $hash->{PIN}; + $hash->{FRM_OWX_CORRELATIONID} = 0; + $firmata->observe_onewire($pin,\&observer,$hash); + + $hash->{FRM_OWX_REPLIES} = {}; + $hash->{DEVS} = []; + if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) { + $firmata->onewire_config($pin,1); + } + + $hash->{STATE}="Initialized"; + main::InternalTimer(main::gettimeofday()+10, "OWX_Discover", $hash,0); + return undef; +} + +######################################################################################## +# +# 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 Complex ($$$$) { + my ($self,$owx_dev,$data,$numread) =@_; + + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + + my $res = ""; + + #-- get the interface + my $frm = $hash->{IODev}; + return 0 unless defined $frm; + my $firmata = $frm->{FirmataDevice}; + my $pin = $hash->{PIN}; + return 0 unless ( defined $firmata and defined $pin ); + + my $ow_command = {}; + + #-- has match ROM part + if ($owx_dev) { + $ow_command->{"select"} = device_to_firmata($owx_dev); + + #-- padding first 9 bytes into result string, since we have this + # in the serial interfaces as well + $res .= "000000000"; + } + + #-- has data part + if ($data) { + my @data = unpack "C*", $data; + $ow_command->{"write"} = \@data; + $res.=$data; + } + + #-- has receive part + if ( $numread > 0 ) { + $ow_command->{"read"} = $numread; + #Firmata sends 0-address on read after skip + $owx_dev = '00.000000000000.00' unless defined $owx_dev; + my $id = $hash->{FRM_OWX_CORRELATIONID}; + $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID}; + $hash->{FRM_OWX_REQUESTS}->{$id} = { + command => $ow_command, + device => $owx_dev + }; + delete $hash->{FRM_OWX_REPLIES}->{$owx_dev}; + $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF; + } + + $firmata->onewire_command_series( $pin, $ow_command ); + + if ($numread) { + my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec + for (my $i=0;$i<$times;$i++) { + if (main::FRM_poll($hash->{IODev})) { + if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) { + $res .= $hash->{FRM_OWX_REPLIES}->{$owx_dev}; + main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i ",$res); + return $res; + } + } else { + select (undef,undef,undef,0.05); + } + } + } + + main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop ",$res); + return $res; +} + +######################################################################################## +# +# Discover - Discover devices on the 1-Wire bus via internal firmware +# +# Parameter hash = hash of bus master +# +# Return 0 : error +# 1 : OK +# +######################################################################################## + +sub Discover ($) { + + my ($self) = @_; + my $hash = $self->{hash}; + + #main::Log 1,"======================> FRM Discover called"; + + #-- get the interface + my $frm = $hash->{IODev}; + return 0 unless defined $frm; + my $firmata = $frm->{FirmataDevice}; + my $pin = $hash->{PIN}; + return 0 unless ( defined $firmata and defined $pin ); + my $old_devices = $hash->{DEVS}; + $hash->{DEVS} = undef; + my $res = $firmata->onewire_search($hash->{PIN}); + #main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev}; + my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec + #main::Log 1,"===========> olddevices = $old_devices, tries =$times"; + for (my $i=0;$i<$times;$i++) { + if (main::FRM_poll($hash->{IODev})) { + if (defined $hash->{DEVS}) { + return 1; + } + } else { + select (undef,undef,undef,0.05); + } + } + #main::Log 1,"===========> olddevices restored"; + $hash->{DEVS} = $old_devices; + return 1; +} + +####################################################################################### +# +# Read - Implement the Read function +# +# Parameter numexp = expected number of bytes +# +####################################################################################### + +sub Read(@) { + my ($self,$numexp) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $buffer = $hash->{PREBUFFER}; + + my $owx_dev = $hash->{FRM_OWX_CURRDEV}; + + my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec + + #-- first read + $buffer .= $hash->{FRM_OWX_REPLIES}->{$owx_dev}; + main::OWX_WDBGL($name,5,"OWX_FRM::Read receiving in first read ",$buffer); + return $buffer; + +} + +######################################################################################## +# +# Ready - Implement the Ready function +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Ready () { + my ($self) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $success= 0; + + main::Log3 $name,1,"OWX_FRM::Ready function called for bus $name. STATE=".$hash->{STATE}; + + return $success; +} + +######################################################################################## +# +# Reset - Reset the 1-Wire bus +# +# Parameter hash = hash of bus master +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Reset() { + my ($self) = @_; + my $hash = $self->{hash}; + + #-- get the interface + my $frm = $hash->{IODev}; + return undef unless defined $frm; + my $firmata = $frm->{FirmataDevice}; + my $pin = $hash->{PIN}; + return undef unless ( defined $firmata and defined $pin ); + + $firmata->onewire_reset($pin); + + return 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 Verify($) { + my ($self,$dev) = @_; + my $hash = $self->{hash}; + foreach my $found ($hash->{DEVS}) { + if ($dev eq $found) { + return 1; + } + } + return 0; +} + +####################################################################################### +# +# Write - Implement the write function +# +# +# Parameter cmd = string to be sent +# reset = 1 if initial bus reset has to be done +# +######################################################################################## + +sub Write(@) { + my ($self,$cmd, $reset) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + + my $res = ""; + + #-- get the interface + my $frm = $hash->{IODev}; + unless(defined $frm){ + main::Log3 $name,1,"OWX_FRM::Write attempted to undefined device $name"; + return 0 + } + + my $firmata = $frm->{FirmataDevice}; + my $pin = $hash->{PIN}; + unless ( defined $firmata and defined $pin ){ + main::Log3 $name,1,"OWX_FRM::Write attempted to ill-defined device $name"; + return 0 + } + + #-- if necessary, perform a reset operation + $self->Reset() + if( $reset ); + + main::OWX_WDBGL($name,5,"OWX_FRM::Write Sending out ",$cmd); + + my $cmd2 = $cmd; + my $owx_dev =""; + my $ow_command = {}; + + #-- take away trailing 0xFF + my $takeoff=0; + my $tchar; + for( my $i=length($cmd)-1; $i>=0; $i--){ + $tchar = substr($cmd,$i,1); + if( ord($tchar) == 0xff ){ + $takeoff++; + }else{ + last; + } + } + + $cmd2 = substr($cmd,0,length($cmd)-$takeoff); + + #-- has match ROM part - need to extract this + $tchar = substr($cmd2,0,1); + if( ord($tchar) == 0x55 ){ + #-- ID of the device. Careful, because hash is the hash of busmaster + for(my $i=0;$i<8;$i++){ + my $j=int(ord(substr($cmd2,$i+1,1))/16); + my $k=ord(substr($cmd,$i+1,1))%16; + $owx_dev.=sprintf "%1x%1x",$j,$k; + $owx_dev.="." + if($i==0 || $i==6); + } + $owx_dev=uc($owx_dev); + $cmd2 = substr($cmd2,9); + $ow_command->{"select"} = device_to_firmata($owx_dev); + + #-- padding first 9 bytes into result string, since we have this + # in the serial interfaces as well + $res .= "000000000"; + } + + #-- has data part + if ($cmd2) { + my @data = unpack "C*", $cmd2; + $ow_command->{"write"} = \@data; + $res.=$cmd2; + } + + #-- pre-content of result buffer + $hash->{PREBUFFER} = $res; + + #-- always receive part ?? + # if ( $numread > 0 ) { + $ow_command->{"read"} = length($cmd); + #Firmata sends 0-address on read after skip + $owx_dev = '00.000000000000.00' unless defined $owx_dev; + my $id = $hash->{FRM_OWX_CORRELATIONID}; + $ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID}; + $hash->{FRM_OWX_REQUESTS}->{$id} = { + command => $ow_command, + device => $owx_dev + }; + delete $hash->{FRM_OWX_REPLIES}->{$owx_dev}; + $hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF; + #} + + $firmata->onewire_command_series( $pin, $ow_command ); + +} + +####################################################################################### +# +# observer function for listening to the FRM device +# +####################################################################################### + +sub observer +{ + my ( $data,$hash ) = @_; + my $command = $data->{command}; + COMMAND_HANDLER: { + $command eq "READ_REPLY" and do { + my $id = $data->{id}; + my $request = (defined $id) ? $hash->{FRM_OWX_REQUESTS}->{$id} : undef; + unless (defined $request) { + return unless (defined $data->{device}); + my $owx_device = firmata_to_device($data->{device}); + my %requests = %{$hash->{FRM_OWX_REQUESTS}}; + foreach my $key (keys %requests) { + if ($requests{$key}->{device} eq $owx_device) { + $request = $requests{$key}; + $id = $key; + last; + }; + }; + }; + return unless (defined $request); + my $owx_data = pack "C*",@{$data->{data}}; + my $owx_device = $request->{device}; + $hash->{FRM_OWX_REPLIES}->{$owx_device} = $owx_data; + ## + $hash->{FRM_OWX_CURRDEV} = $owx_device; + delete $hash->{FRM_OWX_REQUESTS}->{$id}; + + return main::OWX_Read($hash); + + }; + ($command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY") and do { + my @owx_devices = (); + foreach my $device (@{$data->{devices}}) { + push @owx_devices, firmata_to_device($device); + } + if ($command eq "SEARCH_REPLY") { + $hash->{DEVS} = \@owx_devices; + #$main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices; + } else { + $hash->{ALARMDEVS} = \@owx_devices; + } + last; + }; + } +} + +####################################################################################### +# +# translation of strings +# +####################################################################################### + +sub device_to_firmata +{ + my @device; + foreach my $hbyte ( unpack "A2xA2A2A2A2A2A2xA2", shift ) { + push @device, hex $hbyte; + } + return { + family => shift @device, + crc => pop @device, + identity => \@device, + } +} + +sub firmata_to_device +{ + my $device = shift; + return sprintf( "%02X.%02X%02X%02X%02X%02X%02X.%02X", $device->{family}, @{ $device->{identity} }, $device->{crc} ); +} + +1; + + +=pod +=item helper +=item summary to address an OWX interface device via Arduino Firmata +=item summary_DE zur Adressierung eines OWX Interface Device mit Arduino Firmata +=begin html + + +

OWX_FRM

+See OWX +end html +=begin html_DE + + +

OWX_FRM

+Deutsche Dokumentation im Wiki vorhanden, die englische Version gibt es hier: OWX +=end html_DE \ No newline at end of file diff --git a/fhem/FHEM/11_OWX_SER.pm b/fhem/FHEM/11_OWX_SER.pm new file mode 100644 index 000000000..749ebbb3e --- /dev/null +++ b/fhem/FHEM/11_OWX_SER.pm @@ -0,0 +1,881 @@ +######################################################################################## +# +# OWX_SER.pm +# +# FHEM module providing hardware dependent functions for the serial (USB) interface of OWX +# +# Prof. Dr. Peter A. Henning +# +# $Id$ +# +######################################################################################## +# +# Provides the following methods for OWX +# +# Define +# Detect +# Alarms +# Complex +# Discover +# Init +# Read +# Ready +# Reset +# Verify +# Write +# Query +# First +# Next +# Search +# SearchLow +# +######################################################################################## + +package OWX_SER; + +use strict; +use warnings; +use DevIo; + +######################################################################################## +# +# Constructor +# +######################################################################################## + +sub new($) { + my ($class,$hash) = @_; + + return bless { + #-- OWX device + hash => $hash, + #-- baud rate serial interface + baud => 9600, + #-- 16 byte search string + search => [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0], + ROM_ID => [0,0,0,0 ,0,0,0,0], + #-- search state for 1-Wire bus search + LastDiscrepancy => 0, + LastFamilyDiscrepancy => 0, + LastDeviceFlag => 0, + }, $class; +} + +######################################################################################## +# +# Define - Implements Define method +# +# Parameter def = definition string +# +# Return undef if ok, otherwise error message +# +######################################################################################## + +sub Define ($) { + my ($self,$def) = @_; + my $hash = $self->{hash}; + + my @a = split("[ \t][ \t]*", $def); + my $dev = $a[2]; + + #-- when the specified device name contains @, remove these. + $dev =~ s/\@\d*//; + + #-- store with OWX device + $hash->{DeviceName} = $dev; + $hash->{INTERFACE} = "serial"; + $hash->{ASYNCHRONOUS} = 0; + + #-- module version + $hash->{version} = "7.01"; + main::Log3 $hash->{NAME},1,"OWX_SER::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version + if( $hash->{version} ne $main::owx_version); + + #-- call low level init function for the device + $self->Init(); + return undef; +} + +######################################################################################## +# +# Detect - Find out if we have the proper interface +# +# Return 1 if ok, otherwise 0 +# +######################################################################################## + +sub Detect () { + my ($self) = @_; + my $hash = $self->{hash}; + + my ($i,$j,$k,$l,$res,$ret,$ress); + my $name = $hash->{NAME}; + my $ress0 = "OWX_SER::Detect 1-Wire bus $name: interface "; + $ress = $ress0; + + my $interface; + + #-- timing byte for DS2480 + $self->Query("\xC1",1); + + #-- Max 4 tries to detect an interface + for($l=0;$l<4;$l++) { + #-- write 1-Wire bus (Fig. 2 of Maxim AN192) + $res = $self->Query("\x17\x45\x5B\x0F\x91",5); + + #-- process 4/5-byte string for detection + if( !defined($res)){ + $res=""; + $ret=0; + }elsif( ($res eq "\x16\x44\x5A\x00") || ($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=1; + } elsif( ($res eq "\x17\x45\x5B\x0F\x91") || ($res eq "\x17\x45\x1B\x0F\x91")){ + $ress .= "master DS2480 re-detected"; + $interface="DS2480"; + $ret=1; + } else { + $ret=0; + } + last + if( $ret==1 ); + main::OWX_WDBGL($name,4,$ress."not found, answer was ",$res); + $ress = $ress0; + } + if( $ret == 0 ){ + $interface=undef; + main::OWX_WDBGL($name,4,$ress."not detected, answer was ",$res); + } else { + main::OWX_WDBGL($name,3,$ress,undef); + } + $hash->{INTERFACE} = $interface; + return $ret; +} + +######################################################################################## +# +# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set +# +# Return number of alarmed devices => DOES NOT WORK PROPERLY, WHY ???? +# +######################################################################################## + +sub Alarms () { + my ($self) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + + #-- Discover all alarmed devices on the 1-Wire bus + my $res = $self->First("alarm"); + while( $self->{LastDeviceFlag}==0 && $res != 0){ + $res = $res & $self->SER_Next("alarm"); + } + if( $hash->{ALARMDEVS} ) { + main::Log3 $name, 1, " Alarms = ".join(' ',@{$hash->{ALARMDEVS}}); + return( int(@{$hash->{ALARMDEVS}}) ); + } else { + return 0; + } +} + +######################################################################################## +# +# 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 Complex ($$$$) { + my ($self,$dev,$data,$numread) =@_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + + my $select; + my $res; + + #-- get the interface + my $interface = $hash->{INTERFACE}; + my $hwdevice = $hash->{HWDEVICE}; + + #-- 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).$data; + #-- has no match ROM part + } else { + $select=$data; + } + #-- has receive data part + if( $numread >0 ){ + #$numread += length($data); + for( my $i=0;$i<$numread;$i++){ + $select .= "\xFF"; + }; + } + + main::OWX_WDBGL($name,5,"OWX_SER::Complex sending ",$select); + + #-- send data block (Fig. 6 of Maxim AN192) + my $data2=""; + my $retlen = length($select); + + #-- if necessary, prepend E1 character for data mode + if( substr($select,0,1) ne '\xE1') { + $data2 = "\xE1"; + } + #-- all E3 characters have to be duplicated + for(my $i=0;$iQuery($data2,$retlen); + main::OWX_WDBGL($name,5,"OWX_SER::Complex receiving ",$res); + return $res +} + +######################################################################################## +# +# Discover - Find devices on the 1-Wire bus +# +# Return 1, if alarmed devices found, 0 otherwise. +# +######################################################################################## + +sub Discover () { + my ($self) = @_; + my $hash = $self->{hash}; + + #-- zero the array + @{$hash->{DEVS}}=(); + #-- Discover all devices on the 1-Wire bus + my $res = $self->First("discover"); + while( $self->{LastDeviceFlag}==0 && $res!=0 ){ + $res = $res & $self->Next("discover"); + } + return( @{$hash->{DEVS}} == 0); +} + +######################################################################################## +# +# Init - Initialize the 1-wire device +# +# Return undef if ok +# +######################################################################################## + +sub Init() { + my ($self) = @_; + my $hash = $self->{hash}; + my $dev = $hash->{DeviceName}; + my $name = $hash->{NAME}; + + main::Log3 $name,5,"OWX_SER::Init called on device $dev for bus $name, state is ".$hash->{STATE}; + + #if($hash->{STATE} ne "opened"){ + #XXX + #main::DevIo_CloseDev($hash); + main::DevIo_OpenDev($hash,0,undef); + #} + my $hwdevice = $hash->{USBDev}; + + if( !($hwdevice)){ + main::Log3 $name,1, "OWX_SER: Can't open serial device $dev: $!"; + }else{ + main::Log3 $name,3, "OWX_SER: opened serial device $dev: $!"; + $hwdevice->reset_error(); + $hwdevice->baudrate(9600); + $hwdevice->databits(8); + $hwdevice->parity('none'); + $hwdevice->stopbits(1); + $hwdevice->handshake('none'); + $hwdevice->write_settings; + #-- store with OWX device + $hash->{HWDEVICE} = $hwdevice; + } + return undef; +} + +####################################################################################### +# +# Read - Implement the Read function +# +# Parameter numexp = expected number of bytes +# +####################################################################################### + +sub Read(@) { + my ($self,$numexp) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $buffer = ""; + + #-- first try to read things + $buffer = main::DevIo_SimpleRead($hash); + return $buffer; +} + +######################################################################################## +# +# Ready - Implement the Ready function +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Ready () { + my ($self) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $success; + + $success = main::DevIo_OpenDev($hash,1,"main::OWX_Init") + if($hash->{STATE} eq "disconnected"); + + return $success; +} + +######################################################################################## +# +# Reset - Reset the 1-Wire bus (Fig. 4 of Maxim AN192) +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Reset () { + my ($self)=@_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $interface = $hash->{TYPE}; + my $asynchronous = $hash->{ASYNCHRONOUS}; + + return 0 + if( $hash->{STATE} eq "disconnected" ); + + my ($res,$r1,$r2); + #-- Reset command + my $cmd = "\xE3\xC5"; + + #-- OWX interface + if( $interface eq "OWX" ){ + $res = $self->Query($cmd,1); + $res = "" if( !$res ); + #-- OWX_ASYNC + }elsif( $interface eq "OWX_ASYNC"){ + $res = $self->Query($cmd,1); + $res = "" if( !$res ); + } + + #-- process result + $r1 = ord(substr($res,0,1)) & 192; + if( $r1 != 192){ + main::OWX_WDBGL($name,4,"OWX_SER::Reset failure on bus $name ",$res); + return 0; + } + $hash->{ALARMED} = "no"; + + $r2 = ord(substr($res,0,1)) & 3; + + if( $r2 == 3 ){ + main::Log3($name,4, "OWX_SER::Reset $name detects no presence"); + return 1; + }elsif( $r2 ==2 ){ + main::Log3($name,3, "OWX_SER::Reset Alarm presence detected on bus $name"); + $hash->{ALARMED} = "yes"; + } + return 1; +} + +######################################################################################## +# +# Query - Synchronously write to and read from the 1-Wire bus +# +# Parameter: cmd = string to send to the 1-Wire bus +# retlen = expected length of return string +# Return: string received from the 1-Wire bus +# +######################################################################################## + +sub Query ($$) { + + my ($self,$cmd,$numexp) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $state = $hash->{STATE}; + my $timeout = $hash->{timeout}; + my $numget = 0; + my $buffer = ""; + my $try; + + if($state ne "opened") { + main::Log3 $name, 4, "OWX_SER::Query $name: attempted to write to $state device $name"; + return undef; + } + + #-- write operation + main::OWX_WDBGL($name,5,"OWX_SER::Query $name: Sending out",$cmd); + return undef unless defined(main::DevIo_SimpleWrite($hash, $cmd, 0)); + + #-- sleeping for some time + select(undef,undef,undef,0.04); + + #-- first try to read things + $buffer = main::DevIo_SimpleRead($hash); + $numget = (defined($buffer))?length($buffer):0; + + #-- first try ok + if( $numget >= $numexp){ + main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in first attempt and state $state"; + return $buffer; + #-- several tries to read from slow device + }elsif( ($numget>0) && ($numget<$numexp) ){ + for($try=0;$try<3;$try++) { + $buffer .= main::DevIo_SimpleRead($hash); + $numget = length($buffer); + last + if( $numget>=$numexp ); + select(undef,undef,undef,0.01); + } + main::Log3 $name, 5, "OWX_SER::Query $name: $numget of $numexp bytes in attempt $try and state $state" + if( $numget < $numexp ); + return $buffer + if( $numget >= $numexp); + } + + #if( $numget >= $numexp){ + # main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt and state $state"; + # return $buffer; + #-- ultimate failure + #}else{ + main::Log3 $name, 1,"OWX_SER::Query $name: $numget of $numexp bytes in last attempt and state $state, this is an unrecoverable error"; + main::DevIo_Disconnected($hash); + main::InternalTimer(main::gettimeofday()+$timeout, "main::OWX_Ready", $hash,0); + return ""; + #} + + + #--reopen device + main::Log3 $name, 4, "OWX_SER::Query $name: trying to close and open the device"; + + #-- the next two lines are required to avoid a deadlock when the remote end + # closes the connection upon DevIo_OpenDev, as e.g. netcat -l does. + main::DevIo_CloseDev($hash); + main::DevIo_OpenDev($hash, 0, undef); + + #-- second try to read things - with timeout + #$buffer .= main::DevIo_SimpleReadWithTimeout($hash, $timeout); + $buffer .= main::DevIo_SimpleRead($hash); + $numget = (defined($buffer))?length($buffer):0; + + #-- second try ok + if( $numget >= $numexp){ + main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes after reopening and state $state"; + return $buffer; + #-- several tries to read from slow device + }elsif( ($numget>0) && ($numget<$numexp) ){ + for($try=0;$try<4;$try++) { + $buffer .= main::DevIo_SimpleRead($hash); + $numget = length($buffer); + last + if( $numget>=$numexp ); + select(undef,undef,undef,0.01); + } + main::Log3 $name, 5, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt try $try and state $state" + if( $numget < $numexp ); + } + + if( $numget >= $numexp){ + main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt and state $state"; + return $buffer; + #-- ultimate failure + }else{ + main::Log3 $name, 1,"OWX_SER::Query $name: $numget of $numexp bytes in last attempt and state $state, this is an unrecoverable error"; + main::DevIo_Disconnected($hash); + return ""; + } +} + +######################################################################################## +# +# Verify - Verify a particular device on the 1-Wire bus +# +# Parameter dev = 8 Byte ROM ID of device to be tested +# +# Return 1 : device found +# 0 : device not +# +######################################################################################## + +sub Verify ($) { + my ($self,$dev) = @_; + my $hash = $self->{hash}; + my @rom_id; + my $i; + + #-- from search string to byte id + my $devs=$dev; + $devs=~s/\.//g; + for($i=0;$i<8;$i++){ + $rom_id[$i]=hex(substr($devs,2*$i,2)); + } + @{$self->{ROM_ID}}=@rom_id; + #-- reset the search state + $self->{LastDiscrepancy} = 64; + $self->{LastDeviceFlag} = 0; + #-- now do the search + my $res=$self->Search("verify"); + my $dev2=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}}); + #-- reset the search state + $self->{LastDiscrepancy} = 0; + $self->{LastDeviceFlag} = 0; + #-- check result + if ($dev eq $dev2){ + return 1; + }else{ + return 0; + } +} + +####################################################################################### +# +# Write - Implement the write function +# +# Parameter cmd = string to be sent +# reset = 1 if initial bus reset has to be done +# +######################################################################################## + +sub Write(@) { + my ($self,$cmd, $reset) = @_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + + if($hash->{STATE} eq "disconnected"){ + main::Log3 $name,4,"OWX_SER::Write attempted to disconnected device $name"; + return undef; + } + + #-- if necessary, perform a reset operation + $self->Reset() + if( $reset ); + + #-- if necessary, prepend E1 character for data mode of DS2480 + my $cmd2 = ( substr($cmd,0,1) ne '\xE1')?"\xE1":""; + + #-- all E3 characters have to be duplicated in DS2480 + for(my $i=0;$i{search}} = (0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0); + #-- reset the search state + $self->{LastDiscrepancy} = 0; + $self->{LastDeviceFlag} = 0; + $self->{LastFamilyDiscrepancy} = 0; + #-- now do the search + return $self->Search($mode); +} + +######################################################################################## +# +# Next - Find the 'next' devices on the 1-Wire bus +# +# Parameter hash = hash of bus master, mode +# +# 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 Next ($) { + my ($self,$mode) = @_; + + #-- now do the search + return $self->Search($mode); +} + +####################################################################################### +# +# Search - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +# search state. +# +# Parameter 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 Search ($) { + my ($self,$mode)=@_; + my $hash = $self->{hash}; + my $name = $hash->{NAME}; + my $interface = $hash->{INTERFACE}; + my $hwdevice = $hash->{HWDEVICE}; + + my @owx_fams=(); + + return 0 unless (defined $hwdevice); + + #-- if the last call was the last one, no search + if ($self->{LastDeviceFlag}==1){ + return 0; + } + #-- 1-Wire reset + if ($self->Reset()==0){ + #-- reset the search + main::Log(1, "OWX_SER::Search reset failed on bus $name"); + $self->{LastDiscrepancy} = 0; + $self->{LastDeviceFlag} = 0; + $self->{LastFamilyDiscrepancy} = 0; + return 0; + } + + #-- Here we call the device dependent part + $self->SearchLow($mode); + + #--check if we really found a device + if( main::OWX_CRC($self->{ROM_ID})!= 0){ + #-- reset the search + main::Log(1, "OWX_SER::Search CRC failed on bus $name"); + $self->{LastDiscrepancy} = 0; + $self->{LastDeviceFlag} = 0; + $self->{LastFamilyDiscrepancy} = 0; + return 0; + } + + #-- character version of device ROM_ID, first byte = family + my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}}); + + #-- for some reason this does not work - replaced by another test, see below + #if( $self->{LastDiscrepancy}==0 ){ + # $self->{LastDeviceFlag}=1; + #} + #-- + if( $self->{LastDiscrepancy}==$self->{LastFamilyDiscrepancy} ){ + $self->{LastFamilyDiscrepancy}=0; + } + + #-- mode was to verify presence of a device + if ($mode eq "verify") { + main::Log(5, "OWX_SER::Search verified $dev on bus $name"); + return 1; + #-- mode was to discover devices + } elsif( $mode eq "discover" ){ + #-- check families + my $famfnd=0; + foreach (@owx_fams){ + if( substr($dev,0,2) eq $_ ){ + #-- if present, set the fam found flag + $famfnd=1; + last; + } + } + push(@owx_fams,substr($dev,0,2)) if( !$famfnd ); + foreach (@{$hash->{DEVS}}){ + if( $dev eq $_ ){ + #-- if present, set the last device found flag + $self->{LastDeviceFlag}=1; + last; + } + } + if( $self->{LastDeviceFlag}!=1 ){ + #-- push to list + push(@{$hash->{DEVS}},$dev); + main::Log(5, "OWX_SER::Search new device found $dev on bus $name"); + } + return 1; + + #-- mode was to discover alarm devices + } elsif( $hash->{ALARMDEVS} ) { + for(my $i=0;$i<@{$hash->{ALARMDEVS}};$i++){ + if( $dev eq ${$hash->{ALARMDEVS}}[$i] ){ + #-- if present, set the last device found flag + $self->{LastDeviceFlag}=1; + last; + } + } + if( $self->{LastDeviceFlag}!=1 ){ + #--push to list + push(@{$hash->{ALARMDEVS}},$dev); + main::Log(5, "OWX_SER::Search new alarm device found $dev on bus $name"); + } + return 1; + } +} + +######################################################################################## +# +# SearchLow - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +# search state. +# +# Parameter 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 SearchLow ($) { + my ($self,$mode)=@_; + my $hash = $self->{hash}; + + my ($sp1,$sp2,$response,$search_direction,$id_bit_number); + + #-- Response search data parsing operates bytewise + $id_bit_number = 1; + + #-- used to be 0.5 + select(undef,undef,undef,0.05); + + #-- clear 16 byte of search data + @{$self->{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 <= $self->{LastDiscrepancy}){ + #-- first use the ROM ID bit to set the search direction + if( $id_bit_number < $self->{LastDiscrepancy} ) { + $search_direction = (@{$self->{ROM_ID}}[$newcpos2]>>$newimsk2) & 1; + #-- at the last discrepancy search into 1 direction anyhow + } else { + $search_direction = 1; + } + #-- fill into search data; + @{$self->{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",@{$self->{search}}); + $response = $self->Query($sp1,1); + $response = $self->Query($sp2,16); + + #-- interpret the return data + if( length($response)!=16 ) { + main::Log(3, "OWX_SER::Search 2nd return has wrong parameter with length = ".length($response).""); + return 0; + } + #-- 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 + @{$self->{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; + + #-- output for test purpose + #print "id_bit_number=$id_bit_number => newcpos=$newcpos, newchar=0x".int(ord($newchar)/16). + # ".".int(ord($newchar)%16)." r$id_bit_number=$newibit d$id_bit_number=$newdbit\n"; + + #-- discrepancy=1 and ROM_ID=0 + if( ($newdbit==1) and ($newibit==0) ){ + $self->{LastDiscrepancy}=$id_bit_number; + if( $id_bit_number < 9 ){ + $self->{LastFamilyDiscrepancy}=$id_bit_number; + } + } + #-- fill into device data; one char per 8 bits + @{$self->{ROM_ID}}[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8); + + #-- increment number + $id_bit_number++; + } + return 1; +} + +1; + + +=pod +=item helper +=item summary to address an OWX interface device via USB/Serial Interface +=item summary_DE zur Adressierung eines OWX Interface Device via USB/Serial Interface +=begin html + + +

OWX_SER

+See OWX +end html +=begin html_DE + + +

OWX_SER

+Deutsche Dokumentation im Wiki vorhanden, die englische Version gibt es hier: OWX +=end html_DE \ No newline at end of file