From 77b0141c1f7e3cb206f47e9ec56a02c8753cd5e7 Mon Sep 17 00:00:00 2001 From: phenning Date: Sun, 29 Oct 2017 17:13:51 +0000 Subject: [PATCH] 00_OWX.pm: Endlich ! Asynchrones OWX Next Generation git-svn-id: https://svn.fhem.de/fhem/trunk@15349 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/00_OWX.pm | 3103 ++++++++++++++++--------------------------- 1 file changed, 1123 insertions(+), 1980 deletions(-) diff --git a/fhem/FHEM/00_OWX.pm b/fhem/FHEM/00_OWX.pm index 0ab7892e7..7f8eaee32 100644 --- a/fhem/FHEM/00_OWX.pm +++ b/fhem/FHEM/00_OWX.pm @@ -1,39 +1,17 @@ ######################################################################################## # -# OWX.pm -# -# FHEM module to commmunicate with 1-Wire bus devices -# * via an active DS2480/DS2482/DS2490/DS9097U bus master interface attached to an USB port -# * via a passive DS9097 interface attached to an USB port +# FHEM module (Next Generation) to commmunicate with 1-Wire bus devices +# * via an active DS2480/DS9097U bus master interface attached to an USB port +# * via an active DS2480 bus master interface attached to a TCP/IP-UART device # * via a network-attached CUNO # * via a COC attached to a Raspberry Pi +# * via an Arduino running OneWireFirmata attached to USB # # Prof. Dr. Peter A. Henning # -# Final version 6.314 before switching to asynchronous IO -# # $Id$ # ######################################################################################## -# -# define OWX for USB interfaces or -# define OWX for a CUNO or COC interface -# define OWX for a Arduino/Firmata (10_FRM.pm) interface -# -# where may be replaced by any name string -# is a serial (USB) device -# is a CUNO or COC device -# -# get alarms => find alarmed 1-Wire devices (not with CUNO) -# get devices => find all 1-Wire devices -# -# set interval => set period for temperature conversion and alarm testing -# set followAlarms on/off => determine whether an alarm is followed by a search for -# alarmed devices -# -# attr buspower real/parasitic - whether the 1-Wire bus is really powered or -# the 1-Wire devices take their power from the data wire (parasitic is default !) -# ######################################################################################## # # This programm is free software; you can redistribute it and/or modify @@ -56,59 +34,71 @@ package main; use strict; use warnings; - -use vars qw{%owg_family %gets %sets $owx_version $owx_debug}; +use Time::HiRes qw(gettimeofday); +use Data::Dumper qw(Dumper); +use DevIo; #-- unfortunately some things OS-dependent -my $owgdevregexp; +my $SER_regexp; if( $^O =~ /Win/ ) { require Win32::SerialPort; - $owgdevregexp= "com"; + $SER_regexp= "com"; } else { require Device::SerialPort; - $owgdevregexp= "/dev/"; + $SER_regexp= "/dev/"; } -use vars qw{%attr %defs}; +sub Log3($$$); -require "$attr{global}{modpath}/FHEM/DevIo.pm"; - -sub Log($$); - -# Line counter -my $cline=0; - -# These we may get on request -my %gets = ( - "alarms" => "A", - "devices" => "D" +use vars qw{%owg_family %gets %sets $owx_version $owx_debug}; +# 1-Wire devices +# http://owfs.sourceforge.net/family.html +%owg_family = ( + "01" => ["DS2401/DS1990A","OWID DS2401"], + "05" => ["DS2405","OWID DS2405"], + "09" => ["DS2502","OWID DS2502"], + "10" => ["DS18S20/DS1920","OWTHERM DS1820"], + "12" => ["DS2406/DS2507","OWSWITCH DS2406"], + "1B" => ["DS2436","OWID 1B"], + "1D" => ["DS2423","OWCOUNT DS2423"], + "20" => ["DS2450","OWAD DS2450"], + "22" => ["DS1822","OWTHERM DS1822"], + "23" => ["DS2433","OWID 23"], + "24" => ["DS2415/DS1904","OWID 24"], + "26" => ["DS2438","OWMULTI DS2438"], + "27" => ["DS2417","OWID 27"], + "28" => ["DS18B20","OWTHERM DS18B20"], + "29" => ["DS2408","OWSWITCH DS2408"], + "2C" => ["DS2890","OWVAR DS2890"], + "3A" => ["DS2413","OWSWITCH DS2413"], + "3B" => ["DS1825","OWID 3B"], + "7E" => ["OW-ENV","OWID 7E"], #Environmental sensor + "81" => ["DS1420","OWID 81"], + "FF" => ["LCD","OWLCD"] ); -# These occur in a pulldown menu as settable values for the bus master -my %sets = ( - "interval" => "T", - "followAlarms" => "F" +#-- These we may get on request +%gets = ( + "alarms" => "A", + "devices" => "D", + "qstatus" => "Q", + "version" => "V" ); -# These are attributes -my %attrs = ( +#-- These occur in a pulldown menu as settable values for the bus master +%sets = ( + "close" => "c", "open" => "o", "closeopen" => "co" ,"reopen" => "R", + "discover" => "C", + "detect" => "T", + "disconnected" => "D", + "process" => "P" ); #-- some globals needed for the 1-Wire module -#-- baud rate serial interface -my $owx_baud=9600; -my $owx_cmdlen; -#-- Debugging 0,1,2,3 -$owx_debug=0; +$owx_version="7.01"; -#-- 8 byte 1-Wire device address -my @owx_ROM_ID =(0,0,0,0 ,0,0,0,0); -#-- 16 byte search string -my @owx_search=(0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0); -#-- search state for 1-Wire bus search -my $owx_LastDiscrepancy = 0; -my $owx_LastFamilyDiscrepancy = 0; -my $owx_LastDeviceFlag = 0; +#-- debugging now verbosity, this is just for backward compatibility +$owx_debug=0; ######################################################################################## # @@ -124,25 +114,27 @@ my $owx_LastDeviceFlag = 0; sub OWX_Initialize ($) { my ($hash) = @_; - #-- Provider - $hash->{Clients} = ":OWAD:OWCOUNT:OWID:OWLCD:OWMULTI:OWSWITCH:OWTHERM:OWVAR"; - #-- Normal Devices + #-- Provider + $hash->{Clients} = ":OWAD:OWCOUNT:OWID:OWLCD:OWMULTI:OWSWITCH:OWTHERM:OWVAR:"; + $hash->{WriteFn} = "OWX_Write"; + $hash->{ReadFn} = "OWX_Read"; + $hash->{ReadyFn} = "OWX_Ready"; + + #-- Consumer $hash->{DefFn} = "OWX_Define"; $hash->{UndefFn} = "OWX_Undef"; $hash->{GetFn} = "OWX_Get"; $hash->{SetFn} = "OWX_Set"; - $hash->{NotifyFn} = "OWX_Notify"; $hash->{AttrFn} = "OWX_Attr"; - $hash->{AttrList}= "loglevel:0,1,2,3,4,5,6 buspower:real,parasitic dokick:1,0 interval IODev"; - - #-- Adapt to FRM - $hash->{InitFn} = "FRM_OWX_Init"; + $hash->{AttrList}= "asynchronous:0,1 dokick:0,1 ". + "interval timeout opendelay ". + $readingFnAttributes; } ######################################################################################## # -# OWX_Define - Implements DefFn function +# OWX_Define - Implements Define function # # Parameter hash = hash of device addressed, def = definition string # @@ -152,159 +144,84 @@ sub OWX_Define ($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); - my $owx_hwdevice; + my $hwdevice; + my $ret; #-- check syntax if(int(@a) < 3){ - return "OWX: Syntax error - must be define OWX" + return "OWX: Syntax error - must be define OWX |[:]|:||" } - - #-- check syntax - Log 1,"OWX: Warning - Some parameter(s) ignored, must be define OWX ||" - if(int(@a) > 3); - #-- If this line contains 3 parameters, it is the bus master definition + + Log3 $hash->{NAME},2,"OWX: Warning - Some parameter(s) ignored, must be define OWX |[:]|:||" + if( int(@a)>3 ); my $dev = $a[2]; - $hash->{NOTIFYDEV} = "global"; - #-- Dummy 1-Wire ROM identifier, empty device lists $hash->{ROM_ID} = "FF"; - $hash->{DEVS} = []; - $hash->{ALARMDEVS} = []; + $hash->{DEVS} = (); + $hash->{DEVHASH}{"$a[0]"}="Busmaster"; + $hash->{INITDONE} = 0; + #XXX + $hash->{PARTIAL} = ""; + delete $hash->{ALARMDEVS}; + delete $hash->{followAlarms}; + delete $hash->{version}; + + #-- Clear from leftovers of ASYNC + delete $hash->{ASYNC}; - #-- First step: check if we have a directly connected serial interface or a CUNO/COC attached - if ( $dev =~ m|$owgdevregexp|i){ - #-- when the specified device name contains @ already, use it as supplied - if ( $dev !~ m/\@\d*/ ){ - $hash->{DeviceName} = $dev."\@9600"; - } - #-- Second step in case of serial device: open the serial device to test it - my $msg = "OWX: Serial device $dev"; - my $ret = DevIo_OpenDev($hash,0,undef); - $owx_hwdevice = $hash->{USBDev}; - if(!defined($owx_hwdevice)){ - Log 1, $msg." not defined"; - return "OWX: Can't open serial device $dev: $!" - } else { - Log 1,$msg." defined"; - } - $owx_hwdevice->reset_error(); - $owx_hwdevice->baudrate(9600); - $owx_hwdevice->databits(8); - $owx_hwdevice->parity('none'); - $owx_hwdevice->stopbits(1); - $owx_hwdevice->handshake('none'); - $owx_hwdevice->write_settings; - #-- store with OWX device - $hash->{INTERFACE} = "serial"; - $hash->{HWDEVICE} = $owx_hwdevice; + #-- First step - different methods and parameters for setup + #-- check if we have a serial device attached + if ($dev =~ m|$SER_regexp|i) { + require "$attr{global}{modpath}/FHEM/11_OWX_SER.pm"; + $hwdevice = OWX_SER->new($hash); + + #-- check if we have a TCP connection + }elsif( $dev =~ m|\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?(\:\d+)?| ){ + require "$attr{global}{modpath}/FHEM/11_OWX_TCP.pm"; + #$hash->{Protocol} = "telnet" + $hwdevice = OWX_TCP->new($hash); + + #-- check if we have an i2c interface attached + }elsif( $dev =~ m|\d\:\d\d| ){ + require "$attr{global}{modpath}/FHEM/11_OWX_I2C.pm"; + $hwdevice = OWX_I2C->new($hash); + + #-- check if we have a COC/CUNO interface attached + }elsif( (defined( $defs{$dev}->{VERSION} ) ? $defs{$dev}->{VERSION} : "") =~ m/CSM|CUNO/ ){ + require "$attr{global}{modpath}/FHEM/11_OWX_CCC.pm"; + $hwdevice = OWX_CCC->new($hash); + #-- check if we are connecting to Arduino (via FRM): } elsif ($dev =~ /^\d{1,2}$/) { - $hash->{INTERFACE} = "firmata"; - main::LoadModule("FRM"); - FRM_Client_Define($hash,$def); + require "$attr{global}{modpath}/FHEM/11_OWX_FRM.pm"; + $hwdevice = OWX_FRM->new($hash); + } else { - $hash->{DeviceName} = $dev; - #-- Second step in case of CUNO: See if we can open it - my $msg = "OWX: COC/CUNO device $dev"; - #-- hash des COC/CUNO - $owx_hwdevice = $main::defs{$dev}; - if($owx_hwdevice){ - Log 1,$msg." defined"; - #-- store with OWX device - $hash->{INTERFACE} = "COC/CUNO"; - $hash->{HWDEVICE} = $owx_hwdevice; - #-- loop for some time until the state is "Initialized" - for(my $i=0;$i<6;$i++){ - last if( $owx_hwdevice->{STATE} eq "Initialized"); - Log 1,"OWX: Waiting, at t=$i ".$dev." is still ".$owx_hwdevice->{STATE}; - select(undef,undef,undef,3); - } - Log 1, "OWX: Can't open ".$dev if( $owx_hwdevice->{STATE} ne "Initialized"); - #-- reset the 1-Wire system in COC/CUNO - CUL_SimpleWrite($owx_hwdevice, "Oi"); - }else{ - Log 1, $msg." not defined"; - return $msg." not defined"; - } - } - - if ($main::init_done) { - return OWX_Start($hash); - } -} + return "OWX: Define failed, unable to identify interface type $dev for bus ".$hash->{NAME}; + }; -sub OWX_Start ($) { - my ($hash) = @_; + #-- Second step: perform low level init of device + #Log 1,"OWX: Performing define and low level init of bus ".$hash->{NAME}; + $ret = $hwdevice->Define($def); - #-- Third step: see, if a bus interface is detected - if (!OWX_Detect($hash)){ - $hash->{PRESENT} = 0; - readingsSingleUpdate($hash,"state","failed",1); - return undef; - } - #-- Fourth step: discovering devices on the bus - # in 10 seconds discover all devices on the 1-Wire bus - InternalTimer(gettimeofday()+10, "OWX_Discover", $hash,0); + #-- cancel definition of OWX if failed + return $ret if $ret; + $hash->{OWX} = $hwdevice; #-- Default settings - $hash->{interval} = AttrVal($hash->{NAME},"interval",300); # kick every 5 minutes - $hash->{followAlarms} = "off"; - $hash->{ALARMED} = "no"; + $hash->{interval} = 300; # kick every 5 minutes + $hash->{timeout} = 2; # timeout 2 seconds + $hash->{ALARMED} = 0; + $hash->{PRESENT} = 1; + + #-- Third step: perform high level init for 1-Wire Bus in minute or so + ### + InternalTimer(time()+60, "OWX_Init", $hash,0); - $hash->{PRESENT} = 1; - readingsSingleUpdate($hash,"state","defined",1); - #-- Intiate first alarm detection and eventually conversion in a minute or so - InternalTimer(gettimeofday() + $hash->{interval}, "OWX_Kick", $hash,0); - $hash->{STATE} = "Active"; return undef; } -sub OWX_Notify { - my ($hash,$dev) = @_; - my $name = $hash->{NAME}; - my $type = $hash->{TYPE}; - - if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) { - OWX_Start($hash); - } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) { - } -} - -####################################################################################### -# -# OWTX_Attr - Set one attribute value for device -# -# Parameter hash = hash of device addressed -# a = argument array -# -######################################################################################## - -sub OWX_Attr(@) { - my ($do,$name,$key,$value) = @_; - - my $hash = $defs{$name}; - my $ret; - - if ( $do eq "set") { - ARGUMENT_HANDLER: { - $key eq "buspower" and do { - #-- check value - $attr{$hash->{NAME}}{dokick} = $value eq "real" ? 1 : 0; - last; - }; - $key eq "interval" and do { - $hash->{interval} = $value; - if ($main::init_done) { - OWX_Kick($hash); - } - last; - }; - } - } - return $ret; -} - ######################################################################################## # # OWX_Alarms - Find devices on the 1-Wire bus, @@ -312,30 +229,49 @@ sub OWX_Attr(@) { # # Parameter hash = hash of bus master # -# Return 1 : OK -# 0 : no device present +# Return: Message or list of alarmed devices # ######################################################################################## sub OWX_Alarms ($) { my ($hash) = @_; - my $name = $hash->{NAME}; my @owx_alarm_names=(); - - $hash->{ALARMDEVS}=[]; - if ($hash->{INTERFACE} eq "firmata") { - FRM_OWX_Alarms($hash); - } else { - #-- Discover all alarmed devices on the 1-Wire bus - my $res = OWX_First_SER($hash,"alarm"); - while( $owx_LastDeviceFlag==0 && $res != 0){ - $res = $res & OWX_Next_SER($hash,"alarm"); + #-- get the interface + my $name = $hash->{NAME}; + my $owx = $hash->{OWX}; + my $devhash = $hash->{DEVHASH}; + + foreach my $owx_dev (sort keys %{$devhash}){ + push(@owx_alarm_names,$owx_dev) + if $defs{$owx_dev}->{ALARM}; + } + + my $res = int(@owx_alarm_names); + if( $res == 0){ + $hash->{ALARMED}=0; + return "OWX: No alarmed 1-Wire devices found on bus $name"; + } else{ + $hash->{ALARMED}=$res; + return "OWX: $res alarmed 1-Wire devices found on bus $name (".join(",",@owx_alarm_names).")"; + } + + ############### THE FOLLOWING IST DEAD CODE, BECAUSE ALARM SEARCH DOES NOT WORK PROPERLY - WHY ? + if (defined $owx) { + $res = $owx->Alarms(); + } else { + #-- interface error + my $owx_interface = $hash->{INTERFACE}; + if( !(defined($owx_interface))){ + return undef; + } else { + return "OWX: Alarms called with unknown interface $owx_interface on bus $name"; } } - if( @{$hash->{ALARMDEVS}} == 0){ - return "OWX: No alarmed 1-Wire devices found on bus $name"; + + if( $res == 0){ + return "OWX: No alarmed 1-Wire devices found on bus $name"; } #-- walk through all the devices to get their proper fhem names @@ -358,59 +294,67 @@ sub OWX_Alarms ($) { } } #-- so far, so good - what do we want to do with this ? - return "OWX: Alarmed 1-Wire devices found on bus $name (".join(",",@owx_alarm_names).")"; + return "OWX: $res alarmed 1-Wire devices found on bus $name (".join(",",@owx_alarm_names).")"; } -######################################################################################## -# -# OWX_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 +# OWX_Attr - Set one attribute value for device # -# Return response, if OK -# 0 if not OK +# Parameter hash = hash of device addressed +# a = argument array # ######################################################################################## -sub OWX_Complex ($$$$) { - my ($hash,$owx_dev,$data,$numread) =@_; - my $name = $hash->{NAME}; - - #-- get the interface - my $owx_interface = $hash->{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; +sub OWX_Attr(@) { + my ($do,$name,$key,$value) = @_; - #-- interface error - if( !(defined($owx_interface))){ - Log 3,"OWX: Complex called with undefined interface"; - return 0; - #-- here we treat the directly connected serial interfaces - }elsif( ($owx_interface eq "DS2480") || ($owx_interface eq "DS9097") ){ - return OWX_Complex_SER($hash,$owx_dev,$data,$numread); - - #-- here we treat the CUNO/COC devices - }elsif( ($owx_interface eq "COC") || ($owx_interface eq "CUNO") ){ - return OWX_Complex_CCC($hash,$owx_dev,$data,$numread); - - #-- here we treat Arduino/Firmata devices - }elsif( $owx_interface eq "firmata" ) { - return FRM_OWX_Complex( $hash, $owx_dev, $data, $numread ); - - #-- interface error - }else{ - Log 3,"OWX: Complex called with unknown interface $owx_interface on bus $name"; - return 0; + my $hash = $main::defs{$name}; + my $queue = $hash->{QUEUE}; + my $ret; + + if ( $do eq "set") { + ARGUMENT_HANDLER: { + $key eq "asynchronous" and do { + $hash->{ASYNCHRONOUS} = ($value==1 ? 1 : 0); + #-- stop queue prcoessing and delete it + if(!$value){ + RemoveInternalTimer ("queue:$name"); + delete($hash->{QUEUE}); + readingsSingleUpdate($hash,"queue",0,0); + OWX_Reset($hash); + } + last; + }; + $key eq "timeout" and do { + $hash->{timeout} = $value; + last; + }; + $key eq "opendelay" and do { + $hash->{opendelay} = $value; + last; + }; + $key eq "dokick" and do { + $hash->{dokick} = $value; + last; + }; + $key eq "interval" and do { + $hash->{interval} = $value; + if ($main::init_done) { + OWX_Kick($hash); + } + last; + } + } } + return $ret; } ######################################################################################## # -# OWX_CRC - Check the CRC8 code of a device address in @owx_ROM_ID +# OWX_CRC - Check the CRC code of a device address in @owx_ROM_ID # -# Parameter romid = if not zero, return the CRC8 value instead of checking it +# Parameter romid = if not reference to array, return the CRC8 value instead of checking it # ######################################################################################## @@ -437,14 +381,13 @@ sub OWX_CRC ($) { my ($romid) = @_; my $crc8=0; - if( $romid eq "0" ){ + my @owx_ROM_ID; + if( ref ($romid) eq 'ARRAY' ){ + @owx_ROM_ID = @$romid; for(my $i=0; $i<8; $i++){ $crc8 = $crc8_table[ $crc8 ^ $owx_ROM_ID[$i] ]; } - } elsif (ref($romid) eq "ARRAY") { - for(my $i=0; $i<8; $i++){ - $crc8 = $crc8_table[ $crc8 ^ $romid->[$i] ]; - } + return $crc8; } else { #-- from search string to byte id $romid=~s/\.//g; @@ -454,13 +397,13 @@ sub OWX_CRC ($) { for(my $i=0; $i<7; $i++){ $crc8 = $crc8_table[ $crc8 ^ $owx_ROM_ID[$i] ]; } + return $crc8; } - return $crc8; -} +} ######################################################################################## # -# OWX_CRC - Check the CRC8 code of an a byte string +# OWX_CRC8 - Check the CRC8 code of an a byte string # # Parameter string, crc. # If crc is defined, make a comparison, otherwise output crc8 @@ -471,8 +414,6 @@ sub OWX_CRC8 ($$) { my ($string,$crc) = @_; my $crc8=0; my @strhex; - - #Log 1,"CRC8 calculated for string of length ".length($string); for(my $i=0; $i{NAME}; - my $ress0 = "OWX: 1-Wire bus $name: interface "; - $ress = $ress0; - - #-- get the interface - my $owx_interface = $hash->{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- here we treat the directly connected serial interfaces - if($owx_interface eq "serial"){ - #-- timing byte for DS2480 - OWX_Query_2480($hash,"\xC1",1); - - #-- Max 4 tries to detect an interface - for($l=0;$l<100;$l++) { - #-- write 1-Wire bus (Fig. 2 of Maxim AN192) - $res = OWX_Query_2480($hash,"\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\x90") || ($res eq "\x16\x44\x5A\x00\x93")){ - $ress .= "master DS2480 detected for the first time"; - $owx_interface="DS2480"; - $ret=1; - } elsif( $res eq "\x17\x45\x5B\x0F\x91"){ - $ress .= "master DS2480 re-detected"; - $owx_interface="DS2480"; - $ret=1; - } 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"; - $owx_interface="DS9097"; - $ret=1; - } else { - $ret=0; - } - last - if( $ret==1 ); - $ress .= "not found, answer was "; - for($i=0;$i{IODev}) { - $ret=1; - $ress .= "Firmata detected in $hash->{IODev}->{NAME}"; - } else { - $ret = 0; - $ress .= "not associated to any FRM device"; - } - }; - if ($@) { - $ress .= FRM_Catch($@); - $ret = 0; - } - #-- here we treat the COC/CUNO - } else { - select(undef,undef,undef,2); - #-- type of interface - CUL_SimpleWrite($owx_hwdevice, "V"); - select(undef,undef,undef,0.01); - my ($err,$ob) = OWX_ReadAnswer_CCC($owx_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.*/){ - $owx_interface="COC"; - $ress .= "DS2482 / COC detected in $owx_hwdevice->{NAME} with response $ob"; - $ret=1; - #-- CUNO - }elsif( $ob =~ m/.*CUNO.*/){ - $owx_interface="CUNO"; - $ress .= "DS2482 / CUNO detected in $owx_hwdevice->{NAME} with response $ob"; - $ret=1; - #-- something else - } else { - $ret=0; - } - #-- treat the failure cases - if( $ret == 0 ){ - $owx_interface=undef; - $ress .= "in $owx_hwdevice->{NAME} could not be addressed, return was $ob"; - } - } - #-- store with OWX device - $hash->{INTERFACE} = $owx_interface; - Log3 $hash->{NAME}, 1, $ress; - return $ret; -} - ######################################################################################## # # OWX_Discover - Discover devices on the 1-Wire bus, @@ -705,53 +514,60 @@ sub OWX_Detect ($) { sub OWX_Discover ($) { my ($hash) = @_; + my $name = $hash->{NAME}; + my $res; my $ret= ""; - my $name = $hash->{NAME}; - my $exname; - my $acname; + my ($chip,$acstring,$acname,$exname); + my $ow_dev; + my $owx_rnf; + my $owx_f; + my $owx_crc; + my $id_owx; + my $match; - #-- get the interface - my $owx_interface = $hash->{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; - my @owx_names=(); + #-- get the interface - this should be the hardware device ??? + my $owx = $hash->{OWX}; + my $owx1 = $hash->{INTERFACE}; + my $owx2 = $hash->{DeviceName}; + my $owx3 = $hash->{HWDEVICE}; + + my @owx_names =(); + $hash->{DEVHASH}=(); + $hash->{DEVHASH}{"$name"} = "Busmaster"; #-- Discover all devices on the 1-Wire bus, they will be found in $hash->{DEVS} - return undef - if( !defined($owx_interface) ); - #-- Directly connected serial interface - if( ($owx_interface eq "DS2480") || ($owx_interface eq "DS9097") ){ - $res = OWX_First_SER($hash,"discover"); - while( $owx_LastDeviceFlag==0 && $res!=0 ){ - $res = $res & OWX_Next_SER($hash,"discover"); - } - #-- Ask the COC/CUNO - }elsif( ($owx_interface eq "COC" ) || ($owx_interface eq "CUNO") ){ - $res = OWX_Discover_CCC($hash); - #-- ask the Arduino - }elsif ( $owx_interface eq "firmata") { - $res = FRM_OWX_Discover($hash); - #-- Something else + if (defined $owx) { + $res = $owx->Discover(); } else { - Log 1,"OWX: Discover called with unknown interface"; - return undef; + my $owx_interface = $hash->{INTERFACE}; + if( !defined($owx_interface) ) { + return undef; + } else { + Log3 $name, 1,"OWX_Discover on bus $name called with unknown interface $owx_interface"; + return undef; + } + } + + if (defined $res and (ref($res) eq "ARRAY")) { + $hash->{DEVS} = $res; } #-- Go through all devices found on this bus foreach my $owx_dev (@{$hash->{DEVS}}) { #-- ignore those which do not have the proper pattern if( !($owx_dev =~ m/[0-9A-F]{2}\.[0-9A-F]{12}\.[0-9A-F]{2}/) ){ - Log 3,"OWX: Invalid 1-Wire device ID $owx_dev, ignoring it"; + Log3 $name, 3,"OWX_Discover found invalid 1-Wire device ID $owx_dev on bus $name, ignoring it"; next; } #-- three pieces of the ROM ID found on the bus - my $owx_rnf = substr($owx_dev,3,12); - my $owx_f = substr($owx_dev,0,2); - my $owx_crc = substr($owx_dev,15,3); - my $id_owx = $owx_f.".".$owx_rnf; + $owx_rnf = substr($owx_dev,3,12); + $owx_f = substr($owx_dev,0,2); + $owx_crc = substr($owx_dev,15,3); + $id_owx = $owx_f.".".$owx_rnf; - my $match = 0; + $match = 0; #-- Check against all existing devices foreach my $fhem_dev (sort keys %main::defs) { @@ -759,102 +575,55 @@ sub OWX_Discover ($) { # next if( $hash->{NAME} eq $main::defs{$fhem_dev}{NAME} ); #-- all OW types start with OW next if( !defined($main::defs{$fhem_dev}{TYPE})); - next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW"); + next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW"); my $id_fhem = substr($main::defs{$fhem_dev}{ROM_ID},0,15); #-- skip interface device next if( length($id_fhem) != 15 ); - #-- testing if equal to the one found here - # even with improper family - # Log 1, " FHEM-Device = ".substr($id_fhem,3,12)." OWX discovered device ".substr($id_owx,3,12); - if( substr($id_fhem,3,12) eq substr($id_owx,3,12) ) { - #-- warn if improper family id - if( substr($id_fhem,0,2) ne substr($id_owx,0,2) ){ - Log 1, "OWX: Warning, $fhem_dev is defined with improper family id ".substr($id_fhem,0,2). - ", must enter correct model in configuration"; - #$main::defs{$fhem_dev}{OW_FAMILY} = substr($id_owx,0,2); - } + #-- yes, family id and ROM id are the same + if( $id_fhem eq $id_owx ) { $exname=$main::defs{$fhem_dev}{NAME}; push(@owx_names,$exname); #-- replace the ROM ID by the proper value including CRC - $main::defs{$fhem_dev}{ROM_ID}=$owx_dev; - $main::defs{$fhem_dev}{PRESENT}=1; + $main::defs{$fhem_dev}{ROM_ID} = $owx_dev; + $main::defs{$fhem_dev}{PRESENT} = 1; + $main::defs{$fhem_dev}{ERRCOUNT} = 0; + #-- add to DEVHASH + $hash->{DEVHASH}{"$exname"}=$owx_dev; $match = 1; last; + #-- no, only duplicate ROMID + }elsif( substr($id_fhem,3,12) eq substr($id_owx,3,12) ) { + Log3 $name, 1, "OWX_Discover: Warning, $fhem_dev on bus $name is defined with duplicate ROM ID "; } - # } - #-- Determine the device type. This is done manually here - # could be automatic as in OWServer - my $acstring; - my $chip; - #-- Family 10 = Temperature sensor DS1820 - if( $owx_f eq "10" ){ - $chip = "DS1820"; - $acstring = "OWTHERM DS1820"; - #-- Family 12 = Switch DS2406 - }elsif( $owx_f eq "12" ){ - $chip = "DS2406"; - $acstring = "OWSWITCH DS2406"; - #-- Family 1D = Counter/RAM DS2423 - }elsif( $owx_f eq "1D" ){ - $chip = "DS2423"; - $acstring = "OWCOUNT DS2423"; - #-- Family 20 = A/D converter DS2450 - } elsif( $owx_f eq "20" ){ - $chip = "DS2450"; - $acstring = "OWAD DS2450"; - #-- Family 22 = Temperature sensor DS1822 - }elsif( $owx_f eq "22" ){ - $chip = "DS1822"; - $acstring = "OWTHERM DS1822"; - #-- Family 26 = Multisensor DS2438 - }elsif( $owx_f eq "26" ){ - $chip = "DS2438"; - $acstring = "OWMULTI DS2438"; - #-- Family 28 = Temperature sensor DS18B20 - }elsif( $owx_f eq "28" ){ - $chip = "DS18B20"; - $acstring = "OWTHERM DS18B20"; - #-- Family 29 = Switch DS2408 - }elsif( $owx_f eq "29" ){ - $chip = "DS2408"; - $acstring = "OWSWITCH DS2408"; - #-- Family 2C = Potentiometer DS2890 - }elsif( $owx_f eq "2C" ){ - $chip = "DS2890"; - $acstring = "OWVAR DS2890"; - #-- Family 3A = Switch DS2413 - }elsif( $owx_f eq "3A" ){ - $chip = "DS2413"; - $acstring = "OWSWITCH DS2413"; - #-- Family FF = LCD display - }elsif( $owx_f eq "FF" ){ - $chip = "LCD"; - $acstring = "OWLCD"; - #-- All unknown families are ID only (ID-Chips have family id 09) - } else { + #-- Determine the device type + if(exists $owg_family{$owx_f}) { + $chip = $owg_family{$owx_f}[0]; + $acstring = $owg_family{$owx_f}[1]; + }else{ + Log3 $name, 2, "OWX_Discover: Device with unknown family code '$owx_f' found on bus $name"; + #-- All unknown families are ID only $chip = "unknown"; $acstring = "OWID $owx_f"; } - - #Log 1,"###\nfor the following device match=$match, chip=$chip name=$name acstring=$acstring"; + #Log3 $name, 1,"###\nfor the following device match=$match, chip=$chip name=$name acstring=$acstring"; #-- device exists if( $match==1 ){ - $ret .= sprintf("%s.%s %-10s %s\n", $owx_f,$owx_rnf, $chip, $exname); - #-- device unknoen, autocreate + $ret .= sprintf("%s.%s %-14s %s\n", $owx_f,$owx_rnf, $chip, $exname); + #-- device unknown, autocreate }else{ #-- example code for checking global autocreate - do we want this ? #foreach my $d (keys %defs) { #next if($defs{$d}{TYPE} ne "autocreate"); #return undef if(AttrVal($defs{$d}{NAME},"disable",undef)); - my $acname = sprintf "OWX_%s_%s",$owx_f,$owx_rnf; - #Log 1, "to define $acname $acstring $owx_rnf"; + $acname = sprintf "OWX_%s_%s",$owx_f,$owx_rnf; + #Log3 $name, 1, "to define $acname $acstring $owx_rnf"; $res = CommandDefine(undef,"$acname $acstring $owx_rnf"); if($res) { - $ret.= "OWX: Error autocreating with $acname $acstring $owx_rnf: $res\n"; + $ret.= "OWX_Discover: Error autocreating device with $acname $acstring $owx_rnf: $res\n"; } else{ - select(undef,undef,undef,0.1); + select(undef,undef,undef,0.01); push(@owx_names,$acname); $main::defs{$acname}{PRESENT}=1; #-- THIS IODev, default room (model is set in the device module) @@ -862,6 +631,8 @@ sub OWX_Discover ($) { CommandAttr (undef,"$acname room OWX"); #-- replace the ROM ID by the proper value $main::defs{$acname}{ROM_ID}=$owx_dev; + #-- add to DEVHASH + $hash->{DEVHASH}{"$acname"}=$owx_dev; $ret .= sprintf("%s.%s %-10s %s\n", $owx_f,$owx_rnf, $chip, $acname); } } @@ -889,14 +660,12 @@ sub OWX_Discover ($) { if ( $main::defs{$fhem_dev}{IODev} ){ next if( $main::defs{$fhem_dev}{IODev}{NAME} ne $hash->{NAME} ); } - Log 1, "OWX: Deleting unused 1-Wire device $main::defs{$fhem_dev}{NAME} of type $main::defs{$fhem_dev}{TYPE}"; - CommandDelete(undef,$main::defs{$fhem_dev}{NAME}); - #Log 1, "present= ".$main::defs{$fhem_dev}{PRESENT}." iodev=".$main::defs{$fhem_dev}{IODev}{NAME}; + Log3 $name, 1, "OWX_Discover: Device $main::defs{$fhem_dev}{NAME} of type $main::defs{$fhem_dev}{TYPE} is unused, consider deletion !"; } #-- Log the discovered devices - Log 1, "OWX: 1-Wire devices found on bus $name (".join(",",@owx_names).")"; + Log3 $name, 1, "OWX_Discover: 1-Wire devices found on bus $name (".join(",",@owx_names).")"; #-- tabular view as return value - return "OWX: 1-Wire devices found on bus $name \n".$ret; + return "OWX_Discover: 1-Wire devices found on bus $name \n".$ret; } @@ -924,13 +693,90 @@ sub OWX_Get($@) { my $res = OWX_Discover($hash); #-- process result return $res + + } elsif( $a[1] eq "qstatus") { + my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0); + my $state = $hash->{STATE}; + my $dev = $hash->{DeviceName}; + my $busy = ($hash->{BUSY})? "BUSY" : "NOT BUSY"; + my $block = ($hash->{BLOCK})? "BLOCKED" : "NOT BLOCKED"; + $hash->{BUSY} = 1; + + + my $rout=''; + my $hash2 = $selectlist{'OWX_WIFI.192.168.0.97:23'}; + my $vc = vec($rout, $hash2->{FD}, 1); + my $res = "OWX: Queue $name: => dev=$dev vc=$vc length=$qlen, state=$state, $busy, $block\n"; + foreach my $diapoint (@{$hash->{QUEUE}}) { + $res .= " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}."\n"; + } + return $res; + + } elsif( $a[1] eq "version") { + return $owx_version; } else { - return "OWX: Get with unknown argument $a[1], choose one of ". + return "OWX_Get with unknown argument $a[1], choose one of ". join(" ", sort keys %gets); } } +####################################################################################### +# +# OWX_Init - High Level Init of 1-Wire Bus +# +# Parameter hash = hash of bus master +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub OWX_Init ($) { + my ($hash)=@_; + + #-- get the interface + my $owx = $hash->{OWX}; + my $name= $hash->{NAME}; + + if( defined($attr{$name}{"asynchronous"}) && ($attr{$name}{"asynchronous"}==1) ){ + $hash->{ASYNCHRONOUS} = 1; + }else{ + $hash->{ASYNCHRONOUS} = 0; + } + + return "OWX_Init finds a disconnected interface" + if($hash->{STATE} eq "disconnected"); + + Log3 $name,1,"OWX_Init called for bus $name with interface state ".$hash->{STATE}.", now going for detect"; + + if ($owx) { + #-- Fourth step: see, if a bus interface is detected + if (!($owx->Detect())) { + $hash->{PRESENT} = 0; + #$init_done = 1; + Log3 $name,4,"OWX_Init: Detection failed"; + return "OWX_Init Detection failed"; + } + } else { + #-- interface error + my $owx_interface = $hash->{INTERFACE}; + if( !(defined($owx_interface))){ + return "OWX_Init called with undefined interface"; + } else { + return "OWX_Init called with unknown interface $owx_interface"; + } + } + + #-- Fifth step: discovering devices on the bus + OWX_Discover($hash); + $hash->{INITDONE} = 1; + #-- Intiate first alarm detection and eventually conversion + OWX_Kick($hash); + + return undef; +} + ######################################################################################## # # OWX_Kick - Initiate some processes in all devices @@ -943,27 +789,33 @@ sub OWX_Get($@) { ######################################################################################## sub OWX_Kick($) { - my($hash) = @_; - my $ret; + my($hash) = @_; #-- Call us in n seconds again. - InternalTimer(gettimeofday()+ $hash->{interval}, "OWX_Kick", $hash,0); - #-- During reset we see if an alarmed device is present. - OWX_Reset($hash); + InternalTimer(gettimeofday()+ $hash->{interval}, "OWX_Kick", $hash,1); - #-- Only if we have real power on the bus - if( AttrVal($hash->{NAME},"dokick",0) ) { + #-- Only if we have the dokick attribute set to 1 + if( defined($attr{$hash->{NAME}}{dokick}) && ($attr{$hash->{NAME}}{dokick} eq "1") ){ + my $name = $hash->{NAME}; + my $interface = $hash->{TYPE}; + my $asynchronous = $hash->{ASYNCHRONOUS}; #-- issue the skip ROM command \xCC followed by start conversion command \x44 - $ret = OWX_Complex($hash,"","\xCC\x44",0); - if( $ret eq 0 ){ - Log 3, "OWX: Failure in temperature conversion\n"; - return 0; + my $cmd = "\xCC\x44"; + #-- OWX interface + if( $interface eq "OWX" ){ + #-- OLD OWX interface + if( !$asynchronous ){ + OWX_Reset($hash); + OWX_Complex($hash,"","\xCC\x44",0); + #-- NEW OWX interface + }else{ + #### master slave context proc owx_dev data crcpart numread startread callback delay + OWX_Qomplex($hash, undef, "kick", 1, "", $cmd, 0, -10, undef, undef, undef) + } } - #-- sleeping for some time - select(undef,undef,undef,0.5); } return 1; -} +} ######################################################################################## # @@ -980,29 +832,214 @@ sub OWX_Reset ($) { my ($hash)=@_; #-- get the interface - my $owx_interface = $hash->{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; + my $owx = $hash->{OWX}; + my $name = $hash->{NAME}; + my $queue = $hash->{QUEUE}; - #-- interface error - if( !(defined($owx_interface))){ - Log 3,"OWX: Reset called with undefined interface"; - return 0; - }elsif( $owx_interface eq "DS2480" ){ - return OWX_Reset_2480($hash); - }elsif( $owx_interface eq "DS9097" ){ - return OWX_Reset_9097($hash); - }elsif( $owx_interface eq "COC" ){ - return OWX_Reset_CCC($hash); - }elsif( $owx_interface eq "CUNO" ){ - return OWX_Reset_CCC($hash); - }elsif( $owx_interface eq "firmata" ) { - return FRM_OWX_Reset($hash); - }else{ - Log 3,"OWX: Reset called with unknown interface $owx_interface"; - return 0; + my $status = (defined( $queue->[0]->{status})) ? $queue->[0]->{status} : "completed"; + + if (defined $owx) { + #-- reset only when there is no queue or when the status of the current entry is completed + if( $status eq "completed" ){ + return $owx->Reset() + } else { + #Log 1,"==================> Reset attempted on waiting interface"; + my $i = 1; + while ( (my @call_details = (caller($i++))) ){ + Log 1,$call_details[1].":".$call_details[2]." in function ".$call_details[3]; + } + return 0; + } + + } else { + #-- interface error + my $owx_interface = $hash->{INTERFACE}; + if( !(defined($owx_interface))){ + return 0; + } else { + Log3 $name, 3,"OWX: Reset called with unknown interface $owx_interface on bus $name"; + return 0; + } } } +####################################################################################### +# +# OWX_Read - Callback from FHEM main loop - read from the bus +# +# Parameter hash = hash of bus master +# +######################################################################################## + +sub OWX_Read(@) { + my ($hash) = @_; + #-- must not be used when acting synchronously + return undef + if( !$hash->{ASYNCHRONOUS} ); + + my ($buffer, $buffer2, $qlen, $res, $ret); + + #-- master data + my $owx = $hash->{OWX}; + my $name = $hash->{NAME}; + my $queue = $hash->{QUEUE}; + my $query = $queue->[0]; + my $staexp = $query->{startread}; + my $sldev = $query->{hash}->{NAME}; + my $slcont = $query->{context}; + + my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0; + my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0; + + #--safeguard: really called from queue ? + #if( !defined($sldev) ){ + #Log 1,"OWX_Read: Erroneous call from somewhere in FHEM ???"; + #my $i = 1; + #Log 1, "=============> Empty call of OWX_Read Stack Trace:"; + #while ( (my @call_details = (caller($i++))) ){ + # Log 1,$call_details[1].":".$call_details[2]." in function ".$call_details[3]; + #} + #return; + #} + + #-- expected length + my $numget; + my $numexp = $query->{numread}; + $numexp = 0 + if( (!$numexp) || ($numexp eq "") ); + + #-- actual read operation + $buffer = $owx->Read($numexp); + #-- actual length + $numget = length($buffer)-10; + + #-- partial reception + if( $numget < $numexp ){ + #-- empty device buffer + if( length($hash->{PARTIAL}) == 0 ){ + $hash->{PARTIAL} = $buffer; + return undef; + #-- some data present already + }else{ + $buffer = $hash->{PARTIAL}.$buffer; + $numget = length($buffer)-10; + #-- NOT done with this return to main loop + if( $numget<$numexp ){ + $hash->{PARTIAL} = $buffer; + return undef; + } + } + } + + #-- IF WE ARE HERE, WE SHOULD HAVE ALL THE BYTES + $hash->{PARTIAL} = ""; + $queue->[0]->{status} = "completed"; + #Log3 $name, 1, "OWX_Read: queue $name context ".$queue->[0]->{context}." received $numget bytes, expected $numexp. status completed, processing entry"; + + #-- if necessary perform a reset after the full read operation + if( ($queue->[0]->{proc} & 1)==1 ){ + #select(undef,undef,undef,0.01); + $owx->Reset() + }; + + #-- slave data + my $slave = $query->{hash}; + my $context = $query->{context}; + my $proc = (defined($query->{proc}))?($query->{proc}):1; + my $owx_dev = $query->{owx_dev}; + my $crcpart = $query->{crcpart}; #-- needed for CRC check + + #-- successful completion, take off the queue + shift(@{$queue}); + $qlen = @{$queue}; + #Log3 $name, 4, "OWX_Read: $name queue contains $qlen entries after removal of active entry"; + + #-- calling callback + if( $query->{callback} ){ + #Log3 $name, 1, "OWX_Read: $name received $numget bytes, expected $numexp. Now calling callback for context $context"; + $ret = $query->{callback}($slave,$context, $proc, $owx_dev, $crcpart, $numexp, substr($buffer,$staexp,$numexp)); + }else{ + #Log3 $name, 1, "OWX_Read: $name received $numget bytes, expected $numexp. No callback defined for context $context"; + } + + #-- we are done - but maybe still have to do a synchronous job + if( $hash->{BLOCK} ){ + my $ret2 = $hash->{BLOCKCALL}->(); + }; + #-- reset busy flag + $hash->{BUSY} = 0; + + #-- more items in queue -> schedule next process + #if( $name eq "OWX_TEST"){ + # Log3 $name, 1,"----------------------------------------------"; + # Log3 $name,1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after read"; + # foreach my $diapoint (@{$hash->{QUEUE}}) { + # Log3 $name, 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}; + # } + # Log3 $name, 1,"----------------------------------------------"; + #} + my $now = gettimeofday(); + if( ($qlen > 0) ) { + InternalTimer($now+0.01, "OWX_PrQueue", "queue:$name", 0); + } + return $ret; +} + +####################################################################################### +# +# OWX_Ready +# +# Parameter hash = hash of bus master +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub OWX_Ready($) { + my ($hash)=@_; + + #-- relevant for Windows/USB only + if ( $hash->{STATE} ne "disconnected" ){ + if(defined($hash->{USBDev})) { + my $po = $hash->{USBDev}; + my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status; + return ( $InBytes > 0 ); + } + return undef; + } + + #-- If we are here, we are disconnected + #return DevIo_OpenDev($hash, 1, undef ) if ( $hash->{STATE} eq "disconnected" ); + #return DevIo_OpenDev($hash,1,"main::OWX_Init") if ( $hash->{STATE} eq "disconnected" ); + + #-- get the interface + my $owx = $hash->{OWX}; + my $name = $hash->{NAME}; + + #Log3 $name,1,"OWX_Ready called for $name"; + + my $time = time(); + + #-- skip this if delay time is not yet over + if($hash->{NEXT_OPEN} && $time < $hash->{NEXT_OPEN}) { + #Log3 $hash, 5, "NEXT_OPEN prevented opening $name"; + }else{ + #-- call the specific interface if delay time is over + my $success=$owx->Ready(); + if($success) { + delete($hash->{NEXT_OPEN}); + #-- re-init the bus + Log3 $name,1,"OWX_Ready calling low-level init of bus"; + my $ret = OWX_Init($hash); + + }else{ + $hash->{NEXT_OPEN} = $time+AttrVal($name, "opendelay", 60); + } + } + return undef; +} + ######################################################################################## # # OWX_Set - Implements SetFn function @@ -1014,45 +1051,65 @@ sub OWX_Reset ($) { sub OWX_Set($@) { my ($hash, @a) = @_; my $name = shift @a; - my $res; - - #-- First we need to find the ROM ID corresponding to the device name - my $owx_romid = $hash->{ROM_ID}; - Log 5, "OWX_Set request $name $owx_romid ".join(" ",@a); + my $res = 0; #-- for the selector: which values are possible - return join(" ", sort keys %sets) if(@a != 2); - return "OWX_Set: With unknown argument $a[0], choose one of " . join(" ", sort keys %sets) + return join(" ", sort keys %sets) if(!defined($sets{$a[0]})); - - #-- Set timer value - if( $a[0] eq "interval" ){ - #-- only values >= 15 secs allowed - if( $a[1] >= 15){ - $hash->{interval} = $a[1]; - $res = 1; - } else { - $res = 0; - } + #return "OWX_Set: With unknown argument $a[0], choose one of " . join(" ", sort keys %sets) + # if(!defined($sets{$a[0]})); + + #-- Set closedev + if( $a[0] eq "close" ){ + OWX_WDBGL($name,1,"====> CLOSING DEVICE",main::DevIo_CloseDev($hash)); + $res = 0; } - #-- Set alarm behaviour - if( $a[0] eq "followAlarms" ){ - #-- only values >= 15 secs allowed - if( (lc($a[1]) eq "off") && ($hash->{followAlarms} eq "on") ){ - $hash->{interval} = "off"; - $res = 1; - }elsif( (lc($a[1]) eq "on") && ($hash->{followAlarms} eq "off") ){ - $hash->{interval} = "off"; - $res = 1; - } else { - $res = 0; - } - + #-- Set opendev + if( $a[0] eq "open" ){ + OWX_WDBGL($name,1,"====> OPENING DEVICE",main::DevIo_OpenDev($hash,0,undef)); + $res = 0; } - Log GetLogLevel($name,3), "OWX_Set $name ".join(" ",@a)." => $res"; - DoTrigger($name, undef) if($main::init_done); - return "OWX_Set => $name ".join(" ",@a)." => $res"; + + #-- Set closeopendev + if( $a[0] eq "closeopen" ){ + OWX_WDBGL($name,1,"====> CLOSING DEVICE",main::DevIo_CloseDev($hash)); + OWX_WDBGL($name,1," OPENING DEVICE",main::DevIo_OpenDev($hash, 0, undef)); + } + + #-- Set reopen + if( $a[0] eq "reopen" ){ + OWX_WDBGL($name,1,"====> REOPENING DEVICE",main::DevIo_OpenDev($hash, 1, undef)); + $res = 0; + } + + #-- Set discover + if( $a[0] eq "discover" ){ + OWX_Discover($hash); + $res = 0; + } + + #-- Set detect + if( $a[0] eq "detect" ){ + my $owx = $hash->{OWX}; + $owx->Detect(); + $res = 0; + } + + if( $a[0] eq "process") { + my $res = OWX_PrQueue("queue:$name"); + #-- process result + return $res + } + + #-- Set reopen + if( $a[0] eq "disconnected" ){ + main::DevIo_Disconnected($hash); + $res = 0; + } + + Log3 $name, 3, "OWX_Set $name ".join(" ",@a)." => $res"; + # DoTrigger($name, undef) if($init_done); } ######################################################################################## @@ -1066,6 +1123,19 @@ sub OWX_Set($@) { sub OWX_Undef ($$) { my ($hash, $name) = @_; RemoveInternalTimer($hash); + RemoveInternalTimer ("queue:$name"); + + # TODO - THIS IS WRONG. DO NOT DELETE THEM, BUT INVALIDATE THEM + #-- invalidate clients + #foreach my $d (sort keys %defs) { + # if(defined($defs{$d}) && + # defined($defs{$d}{IODev}) && + # $defs{$d}{IODev} == $hash) { + # Log3 $hash, 3, "deleting port for $d"; + # delete $defs{$d}{IODev}; + # } + #} + DevIo_CloseDev($hash); return undef; } @@ -1079,42 +1149,468 @@ sub OWX_Undef ($$) { # Return 1 : device found # 0 : device not found # +# Unterbinden +# ######################################################################################## -sub OWX_Verify { +sub OWX_Verify ($$$$) { my ($hashorname,$devname,$devid,$type) = @_; my $hash; + my $i; if( $type == 1){ $hash = $defs{$hashorname}; }else{ $hash = $hashorname; } - my $i; #-- get the interface - my $owx_interface = $hash->{INTERFACE}; + my $name = $hash->{NAME}; + my $owx = $hash->{OWX}; + my $state = $hash->{STATE}; - #-- Verify this device on the 1-Wire bus - return 0 - if( !defined($owx_interface) ); - #-- Directly connected interface - if( ($owx_interface eq "DS2480") || ($owx_interface eq "DS9097") ){ - return OWX_Verify_SER($hash,$devid) - #-- Ask the COC/CUNO - }elsif( ($owx_interface eq "COC" ) || ($owx_interface eq "CUNO") ){ - return OWX_Verify_CCC($hash,$devid) - }elsif( $owx_interface eq "firmata" ){ - return FRM_OWX_Verify($hash,$devid); - } else { - Log 1,"OWX: Verify called with unknown interface"; - return 0; + #-- Do nothing, if devices not yet discovered + return undef + if( !$hash->{INITDONE} ); + + my $now = gettimeofday(); + my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0); + + #-- if called synchronously, or queue is blocked, or queue empty, do it now + if( !($hash->{ASYNCHRONOUS}) || + ( $qlen == 0) || + ( $hash->{ASYNCHRONOUS} && $hash->{BLOCK} ) ){ + if( $state ne "opened" ) { + Log3 $name, 3,"OWX_Verify called while interface $name not opened"; + return undef; + } + Log3 $name, 5,"OWX_Verify called for interface $name"; + $hash->{BLOCK} = 1; + $hash->{BUSY} = 1; + my $ret = $owx->Verify($devid); + #-- remove queue block and restart queue + $hash->{BLOCK} = 0; + $hash->{BUSY} = 0; + my $slave = $defs{$devname}; + + #-- generate an event only if presence has changed + if( $ret == 0 ){ + readingsSingleUpdate($slave,"present",0,$slave->{PRESENT}); + } else { + readingsSingleUpdate($slave,"present",1,!$slave->{PRESENT}); + } + $slave->{PRESENT} = $ret; + OWX_Ready($hash); + return $ret; + + #-- if called asynchronously, and queue is not blocked, issue queue block and delay. + }elsif( $hash->{ASYNCHRONOUS} && !($hash->{BLOCK}) ){ + #-- issue queue block + $hash->{BLOCK} = 1; + Log3 $name, 5,"OWX_Verify issued a queue block on interface $name"; + $hash->{BLOCKCALL}=sub(){ OWX_Verify($name,$devname,$devid,1); }; + return undef; } } ####################################################################################### # -# OWX_WDBG - Write a debug message +# OWX_Write - Write to the bus +# +# Parameter hash = hash of bus master, +# +######################################################################################## + +sub OWX_Write($){ + my $hash = shift; + my $queue = $hash->{QUEUE}; + my $data = $queue->[0]{data}; + my $proc = $queue->[0]{proc}; + + my $name = $hash->{NAME}; + my $owx = $hash->{OWX}; + my $buffer = ""; + + #-- catch late calls + return + if( !$queue ); + + #-- perform a send from queue + if($hash->{STATE} ne "disconnected"){ + + #-- empty the receive buffer + $hash->{PARTIAL} = ""; + + $queue->[0]{status} = "active"; + my $reset = 1-(($proc & 2) >> 1); + $owx->Write($data, $reset); + } + + #-- in this case we will never get a read operation + if( $queue->[0]{numread} == -10){ + $hash->{PARTIAL} = ""; + $queue->[0]->{status} = "completed"; + #Log3 $name, 1, "OWX_Write: queue $name gets status completed because donotwait is set"; + + #-- if necessary perform a reset after this operation + if( ($queue->[0]->{proc} & 1)==1 ){ + select(undef,undef,undef,0.01); + $owx->Reset() + }; + + #-- reset busy flag + shift(@{$queue}); + $hash->{BUSY} = 0; + } + return +} + +######################################################################################## +# +# Complex R/W operations and queue handling +# +######################################################################################## +# +# OWX_Complex - Synchronously 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 OWX_Complex ($$$$) { + my ($hash,$owx_dev,$data,$numread) =@_; + + #-- get the interface + my $name = $hash->{NAME}; + my $owx = $hash->{OWX}; + my $state = $hash->{STATE}; + + if( $state eq "disconnected" ) { + Log3 $name, 1,"OWX_Complex called while interface $name disconnected"; + return 0; + } + my $ret = $owx->Complex($owx_dev,$data,$numread); + return (!defined($ret))?0:$ret; +} + +####################################################################################### +# +# OWX_Qomplex - Put into queue: +# asynchronously send match ROM, data block and receive bytes as response +# but queued +# +# Parameter hash = hash of bus master, +# slave = hash of slave device +# context = mode for evaluating the binary data +# proc = processing instruction, also passed to OWX_Read. +# bitwise interpretation !! +# if 0, nothing special +# if 1 = bit 0, a reset will be performed also after +# the last operation in OWX_Read +# if 2 = bit 1, the initial reset of the bus will be suppressed +# if 4 = do not wait for a response for more than ??? +# if 8 = bit 3, the fillup of the data with 0xff will be suppressed +# if 16= bit 4, the insertion will be at the top of the queue +# owx_dev = ROM ID of slave device +# data = string to be sent +# crcpart = part of the data that needs to be part of the CRC check +# numread = number of bytes to receive +# startread = start index to begin reading in the return value +# callback = address of callback function +# mindelay = minimum time until next call to this device +# +# Return response, if NOK (died) +# undef if OK +# +######################################################################################## + +sub OWX_Qomplex(@){ + my ( $hash, $slave, $context, $proc, $owx_dev, $data, $crcpart, $numread, $startread, $callback, $mindelay) = @_; + #### master slave context proc owx_dev data crcpart numread startread callback delay + my $name = $hash->{NAME}; + + my $data2; + my $res = ""; + my $res2 = ""; + + if( !$defs{$name}) { + return undef; + } + + #-- get the interface + my $interface = $hash->{INTERFACE}; + my $owx = $hash->{OWX}; + + my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0; + my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0; + + #-- has match ROM part, prepend the rom id + if( $owx_dev ){ + #-- ID of the device + my $owx_rnf = substr($owx_dev,3,12); + my $owx_f = substr($owx_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 + $owx_dev=~s/\.//g; + for(my $i=0;$i<8;$i++){ + $rom_id[$i]=hex(substr($owx_dev,2*$i,2)); + } + $data2=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id).$data; + #-- has no match ROM part, insert a "skip ROM" command + } else { + $data2="\xCC".$data; + } + + #-- has receive data part, suppress fillup ? + if( (($proc & 8)>>3) != 1) { + if( $numread >0 ){ + #$numread += length($data); + for( my $i=0;$i<$numread;$i++){ + $data2 .= "\xFF"; + } + } + } + + my $now = gettimeofday(); + + my %dialog; + $dialog{hash} = $slave; + $dialog{context} = $context; + $dialog{proc} = $proc; + $dialog{owx_dev} = $owx_dev; + $dialog{data} = $data2; + $dialog{crcpart} = $crcpart; + $dialog{numread} = $numread; + $dialog{startread} = $startread; + $dialog{callback} = $callback; + $dialog{delay} = $mindelay; + $dialog{start} = $now; + $dialog{status} = "waiting"; + + if( !$context ){ + Log3 $name,1,"OWX_Qomplex: context missing in queue $name entry for device $owx_dev"; + $dialog{context} = "" + } + + my $qlen = ($hash->{QUEUE} ? scalar(@{$hash->{QUEUE}}) : 0); + readingsSingleUpdate($hash,"queue",$qlen,0); + + #-- single item => new queue, and start processing immediately + if(!$qlen) { + $hash->{QUEUE} = [ \%dialog ]; + Log3 $name, 4, "OWX_Qomplex: Added dev $owx_dev to queue $name context=$context"; + OWX_PrQueue("direct:".$name); + + #-- List queue for debugging + if( $qdebug ){ + Log3 $name,1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion"; + foreach my $diapoint (@{$hash->{QUEUE}}) { + Log3 $name, 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}; + } + Log3 $name, 1,"----------------------------------------------"; + return; + } + #-- priority insert at top of queue, and no immediate processing + } elsif( (($proc & 16)>>4)==1 ) { + #if ( $hash->{BUSY}==1 ) { + # my $dialogtop = shift(@{$hash->{QUEUE}}); + # unshift( @{$hash->{QUEUE}}, \%dialog ); + # unshift( @{$hash->{QUEUE}}, $dialogtop); + # Log 1,"------------> Prio insert as second task"; + # Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion"; + # foreach my $diapoint (@{$hash->{QUEUE}}) { + # Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}; + # } + # Log 1,"----------------------------------------------"; + #}else{ + unshift( @{$hash->{QUEUE}}, \%dialog ); + #Log 1,"------------> Prio insert as first task"; + #Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion"; + #foreach my $diapoint (@{$hash->{QUEUE}}) { + # Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}; + #} + #Log 1,"----------------------------------------------"; + #} + return; + + #-- insert into existing queue + } else { + if ($qlen > AttrVal($name, "queueMax", 100)) { + Log 1,"OWX_Qomplex: $name queue too long, dropping data" + if( $qdebug ); + } else { + push(@{$hash->{QUEUE}}, \%dialog); + Log 1, "OWX_Qomplex: Added dev $owx_dev to queue $name numread=$numread" + if( $qdebug ); + } + } + #-- List queue for debugging + if( $qdebug ){ + Log 1, " queue $name contains ".scalar(@{$hash->{QUEUE}})." entries after insertion"; + foreach my $diapoint (@{$hash->{QUEUE}}) { + Log 1, " => ".$diapoint->{owx_dev}." context ".$diapoint->{context}." expecting ".$diapoint->{numread}." bytes, ".$diapoint->{status}; + } + Log 1,"----------------------------------------------"; + } +} + +####################################################################################### +# +# OWX_PrQueue - ProcessQueue. +# Called by internal timer as"queue:$name" +# or directly with $direct:$name +# +# Flags: BUSY indicates an asynchronous r/w operation +# BLOCKED stops the asynchronous operations for synchronous r/w +# +# +# Return response, if OK +# 0 if not OK +# +######################################################################################## + +sub OWX_PrQueue($){ + my (undef,$name) = split(':', $_[0]); + my $hash = $defs{$name}; + my $queue = $hash->{QUEUE}; + my $slave; + + #-- 1 seconds queue delay as default, 2 seconds for timeout + my $longDelay = 1.0; + my $shortDelay = 0.05; + my $maxrunning = ( defined($attr{$name}{timeout})) ? $attr{$name}{timeout} : 2; + my $devlevel = (defined($attr{$name}{verbose})) ? $attr{$name}{verbose} : 0; + my $qdebug = ( ($devlevel > 3) || ($attr{global}{verbose}>3) ) ? 1 : 0; + + RemoveInternalTimer ("queue:$name"); + my $now = gettimeofday(); + + #-- fhem not initialized, wait with IO || !$hash->{INITDONE} + if ( !$main::init_done ) { + Log3 $name,1, "OWX_PrQueue: init not done, delay sending from queue $name"; + InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0); + return; + } + + #-- Done with the queue + return + if( !$queue); + my $qlen = @{$queue}; + return + if( $qlen == 0); + + #-- remove top entry if garbage + if( !defined($queue->[0]{data}) || ($queue->[0]{data} eq "" || $queue->[0]->{start} eq "" )) { + my $reason = (!defined($queue->[0]{data})) ? "garbage" : (($queue->[0]{data} eq "") ? "no data" : + (($queue->[0]->{start} eq "") ? "no start" : "unknown")); + Log3 $name,1, "OWX_PrQueue: removing garbage from queue $name, reason $reason "; + shift(@{$queue}); + InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0); + return; + } + + #-- still waiting for reply to last send + if ( $hash->{BUSY} && $hash->{BUSY}==1 ) { + my $running = int(100*($now-$queue->[0]->{start}))/100; + #-- donotwait flag is set + if( (($queue->[0]->{proc} & 4)>>2)==1 ){ + #Log 1, "OWX_PrQueue: removing ".$queue->[0]->{owx_dev}." w. context ".$queue->[0]->{context}." from queue $name, because donotwait is set" + # if( $name eq "OWX_TEST"); + $hash->{BUSY}=0; + shift(@{$queue}); + InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0); + return; + #-- timeout, so kill this entry + }elsif( $running > $maxrunning ){ + #Log 1, "OWX_PrQueue: removing ".$queue->[0]->{owx_dev}." w. context ".$queue->[0]->{context}." from queue $name. Timeout $running > $maxrunning seconds." + # if( $name eq "OWX_TEST"); + $hash->{BUSY}=0; + shift(@{$queue}); + InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0); + return; + #-- no timeout, but we have to wait for a reply + }else{ + my $rout=''; + #my $hash2 = $selectlist{'OWX_WIFI.192.168.0.97:23'}; + #my $fd = ( defined($hash2->{FD}) ) ? $hash2->{FD} : "NO"; + #my $vc = ( defined($hash2->{FD}) ) ? vec($rout, $hash2->{FD}, 1) : "NO"; + #Log 1, "OWX_PrQueue: still waiting for reply, delay sending from queue $name" + # if( $name eq "OWX_TEST"); + #Log 1, "OWX_PrQueue: still waiting for reply, delay sending from queue $name."; + # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status}; + InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0); + return; + } + } + + #-- if something to send, check if min delay for slave is over + if( $queue->[0]{data} ) { + $slave = $queue->[0]{hash}; # hash of 1-Wire device + #-- no, it is not + if ( defined($slave->{NEXTSEND})){ + if( $now < $slave->{NEXTSEND} ){ + #-- reschedule, if necessary + if( ($qlen==1) || (($qlen > 1) && ($queue->[1]{$hash} eq $slave)) ){ + #Log 1, "OWX_PrQueue: device ".$slave->{NAME}." mindelay not over, rescheduling." + InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0); + return; + #-- switch top positions of queue if possible + }else{ + #Log 1, "OWX_PrQueue: exchanging top positions of queue"; + my $dialogtop = shift(@{$queue}); + my $dialogsecond = shift(@{$queue}); + unshift( @{$queue}, $dialogtop); + unshift( @{$queue}, $dialogsecond); + } + } + } + } + + #-- something to send, check min delay of 10 ms + if( $queue->[0]{data} ) { + $slave = $queue->[0]{hash}; # may be a different one than above + #-- no, it is not, reschedule + if ($hash->{LASTSEND} && $now < $hash->{LASTSEND} + 0.01) { + #-- List queue for debugging + #Log 1, "OWX_PrQueue: queue $name mindelay not over, rescheduling."; + # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status}; + InternalTimer($now+$shortDelay, "OWX_PrQueue", "queue:$name", 0); + return; + } + #-- start write operation + $hash->{BUSY} = 1; # OWX queue is busy until response is received + $hash->{LASTSEND} = $now; # remember when last sent on this queue + #-- set delay for this device if necessary + if( defined($queue->[0]{delay}) ){ + $slave->{NEXTSEND} = $now + $queue->[0]{delay}; + }else{ + delete $slave->{NEXTSEND}; + } + # Log 1,"OWX_PrQueue: starting send-receive cycle, queue length $qlen. Setting entry to active"; + # Log 1, " => ".$queue->[0]->{owx_dev}." context ".$queue->[0]->{context}." expecting ".$queue->[0]->{numread}." bytes, ".$queue->[0]->{status}; + # Log 1,"----------------------------------------------"; + #-- REALLY do it + #Log 1, "OWX_PrQueue: queue $name starting OWX_Write with context ".$queue->[0]->{context} + # if( $name eq "OWX_TEST"); + $queue->[0]->{start} = $now; + OWX_Write($hash); + } + #-- schedule next processing + InternalTimer($now+$longDelay, "OWX_PrQueue", "queue:$name", 0); + return; +} + +####################################################################################### +# +# OWX_WDBG - Write a debug message unconditionally # # Parameter $name= device name # $msg = string message @@ -1168,1378 +1664,26 @@ sub OWX_WDBGL($$$$) { } main::Log3($name, 1, $msg); } - -######################################################################################## -# -# The following subroutines in alphabetical order are only for direct serial bus interface -# -######################################################################################## -# -# OWX_Complex_SER - 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 OWX_Complex_SER ($$$$) { - my ($hash,$owx_dev,$data,$numread) =@_; - - my $select; - my $res = ""; - my $res2 = ""; - my ($i,$j,$k); - - #-- get the interface - my $owx_interface = $hash->{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- has match ROM part - if( $owx_dev ){ - #-- ID of the device - my $owx_rnf = substr($owx_dev,3,12); - my $owx_f = substr($owx_dev,0,2); - - #-- 8 byte 1-Wire device address - my @owx_ROM_ID =(0,0,0,0 ,0,0,0,0); - #-- from search string to byte id - $owx_dev=~s/\.//g; - for(my $i=0;$i<8;$i++){ - $owx_ROM_ID[$i]=hex(substr($owx_dev,2*$i,2)); - } - $select=sprintf("\x55%c%c%c%c%c%c%c%c",@owx_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"; - }; - } - - #-- for debugging - if( $owx_debug > 1){ - $res2 = "OWX_Complex_SER: Sending out "; - for($i=0;$i 1){ - $res2 = "OWX_Complex_SER: Receiving "; - for($i=0;$i{INTERFACE}; - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- if the last call was the last one, no search - if ($owx_LastDeviceFlag==1){ - return 0; - } - #-- 1-Wire reset - if (OWX_Reset($hash)==0){ - #-- reset the search - Log 1, "OWX: Search reset failed"; - $owx_LastDiscrepancy = 0; - $owx_LastDeviceFlag = 0; - $owx_LastFamilyDiscrepancy = 0; - return 0; - } - - #-- Here we call the device dependent part - if( $owx_interface eq "DS2480" ){ - OWX_Search_2480($hash,$mode); - }elsif( $owx_interface eq "DS9097" ){ - OWX_Search_9097($hash,$mode); - }else{ - Log 1,"OWX: Search called with unknown interface ".$owx_interface; - return 0; - } - #--check if we really found a device - if( OWX_CRC(0)!= 0){ - #-- reset the search - Log 1, "OWX: Search CRC failed "; - $owx_LastDiscrepancy = 0; - $owx_LastDeviceFlag = 0; - $owx_LastFamilyDiscrepancy = 0; - return 0; - } - - #-- character version of device ROM_ID, first byte = family - my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@owx_ROM_ID); - - #-- for some reason this does not work - replaced by another test, see below - #if( $owx_LastDiscrepancy==0 ){ - # $owx_LastDeviceFlag=1; - #} - #-- - if( $owx_LastDiscrepancy==$owx_LastFamilyDiscrepancy ){ - $owx_LastFamilyDiscrepancy=0; - } - - #-- mode was to verify presence of a device - if ($mode eq "verify") { - Log 5, "OWX: Device verified $dev"; - 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 - $owx_LastDeviceFlag=1; - last; - } - } - if( $owx_LastDeviceFlag!=1 ){ - #-- push to list - push(@{$hash->{DEVS}},$dev); - Log 5, "OWX: New device found $dev"; - } - return 1; - - #-- mode was to discover alarm devices - } else { - for(my $i=0;$i<@{$hash->{ALARMDEVS}};$i++){ - if( $dev eq ${$hash->{ALARMDEVS}}[$i] ){ - #-- if present, set the last device found flag - $owx_LastDeviceFlag=1; - last; - } - } - if( $owx_LastDeviceFlag!=1 ){ - #--push to list - push(@{$hash->{ALARMDEVS}},$dev); - Log 5, "OWX: New alarm device found $dev"; - } - return 1; - } -} - -######################################################################################## -# -# OWX_Verify_SER - 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 OWX_Verify_SER ($$) { - my ($hash,$dev) = @_; - my $i; - - #-- from search string to byte id - my $devs=$dev; - $devs=~s/\.//g; - for($i=0;$i<8;$i++){ - $owx_ROM_ID[$i]=hex(substr($devs,2*$i,2)); - } - #-- reset the search state - $owx_LastDiscrepancy = 64; - $owx_LastDeviceFlag = 0; - #-- now do the search - my $res=OWX_Search_SER($hash,"verify"); - my $dev2=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@owx_ROM_ID); - #-- reset the search state - $owx_LastDiscrepancy = 0; - $owx_LastDeviceFlag = 0; - #-- check result - if ($dev eq $dev2){ - return 1; - }else{ - return 0; - } -} - -######################################################################################## -# -# The following subroutines in alphabetical order are only for a DS2480 bus interface -# -######################################################################################### -# -# OWX_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 OWX_Block_2480 ($$) { - my ($hash,$data) =@_; - - my $data2=""; - my $retlen = 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{HWDEVICE}; - - $owx_hwdevice->baudrate($owx_baud); - $owx_hwdevice->write_settings; - - if( $owx_debug > 2){ - my $res = "OWX: Sending out "; - for($i=0;$iwrite($cmd); - - if( !($count_out)){ - Log 3,"OWX_Query_2480: No return value after writing" if( $owx_debug > 0); - } else { - Log 3, "OWX_Query_2480: Write incomplete $count_out ne ".(length($cmd))."" if ( ($count_out != length($cmd)) & ($owx_debug > 0)); - } - #-- sleeping for some time - select(undef,undef,undef,0.04); - - #-- read the data - looping for slow devices suggested by Joachim Herold - $n=0; - for($l=0;$l<$retlen;$l+=$m) { - my ($count_in, $string_part) = $owx_hwdevice->read(48); - $string_in .= $string_part; - $m = $count_in; - $n++; - if( $owx_debug > 2){ - Log 3, "Schleifendurchlauf $n"; - } - if ($n > 100) { - $m = $retlen; - } - select(undef,undef,undef,0.02); - if( $owx_debug > 2){ - my $res = "OWX: Receiving in loop no. $n "; - for($i=0;$i<$count_in;$i++){ - $j=int(ord(substr($string_part,$i,1))/16); - $k=ord(substr($string_part,$i,1))%16; - $res.=sprintf "0x%1x%1x ",$j,$k; - } - Log 3, $res - if( $count_in > 0); - } - } - - #-- sleeping for some time - select(undef,undef,undef,0.01); - return($string_in); -} - -######################################################################################## -# -# OWX_Reset_2480 - Reset the 1-Wire bus (Fig. 4 of Maxim AN192) -# -# Parameter hash = hash of bus master -# -# Return 1 : OK -# 0 : not OK -# -######################################################################################## - -sub OWX_Reset_2480 ($) { - - my ($hash)=@_; - my $cmd=""; - my $name = $hash->{NAME}; - - my ($res,$r1,$r2); - #-- if necessary, prepend \xE3 character for command mode - $cmd = "\xE3"; - - #-- Reset command \xC5 - $cmd = $cmd."\xC5"; - #-- write 1-Wire bus - $res =OWX_Query_2480($hash,$cmd,1); - - #-- if not ok, try for max. a second time - $r1 = ord(substr($res,0,1)) & 192; - if( $r1 != 192){ - #Log 1, "Trying second reset"; - $res =OWX_Query_2480($hash,$cmd,1); - } - - #-- process result - $r1 = ord(substr($res,0,1)) & 192; - if( $r1 != 192){ - Log 3, "OWX: Reset failure on bus $name"; - return 0; - } - $hash->{ALARMED} = "no"; - - $r2 = ord(substr($res,0,1)) & 3; - - if( $r2 == 3 ){ - #Log 3, "OWX: No presence detected"; - return 1; - }elsif( $r2 ==2 ){ - Log 1, "OWX: Alarm presence detected on bus $name"; - $hash->{ALARMED} = "yes"; - } - return 1; -} - -######################################################################################## -# -# OWX_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 OWX_Search_2480 ($$) { - my ($hash,$mode)=@_; - - my ($sp1,$sp2,$response,$search_direction,$id_bit_number); - - #-- Response search data parsing operates bytewise - $id_bit_number = 1; - - select(undef,undef,undef,0.5); - - #-- clear 16 byte of search data - @owx_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 <= $owx_LastDiscrepancy){ - #-- first use the ROM ID bit to set the search direction - if( $id_bit_number < $owx_LastDiscrepancy ) { - $search_direction = ($owx_ROM_ID[$newcpos2]>>$newimsk2) & 1; - #-- at the last discrepancy search into 1 direction anyhow - } else { - $search_direction = 1; - } - #-- fill into search data; - $owx_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",@owx_search); - $response = OWX_Query_2480($hash,$sp1,1); - $response = OWX_Query_2480($hash,$sp2,16); - - #-- interpret the return data - if( length($response)!=16 ) { - Log 3, "OWX: 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 - @owx_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) ){ - $owx_LastDiscrepancy=$id_bit_number; - if( $id_bit_number < 9 ){ - $owx_LastFamilyDiscrepancy=$id_bit_number; - } - } - #-- fill into device data; one char per 8 bits - $owx_ROM_ID[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8); - - #-- increment number - $id_bit_number++; - } - return 1; -} - -######################################################################################## -# -# OWX_WriteBytePower_2480 - Send byte to bus with power increase (Fig. 16 of Maxim AN192) -# -# Parameter hash = hash of bus master, dbyte = byte to send -# -# Return 1 : OK -# 0 : not OK -# -######################################################################################## - -sub OWX_WriteBytePower_2480 ($$) { - - my ($hash,$dbyte) =@_; - my $cmd="\x3F"; - my $ret="\x3E"; - #-- if necessary, prepend \xE3 character for command mode - $cmd = "\xE3".$cmd; - - #-- distribute the bits of data byte over several command bytes - for (my $i=0;$i<8;$i++){ - my $newbit = (ord($dbyte) >> $i) & 1; - my $newchar = 133 | ($newbit << 4); - my $newchar2 = 132 | ($newbit << 4) | ($newbit << 1) | $newbit; - #-- last command byte still different - if( $i == 7){ - $newchar = $newchar | 2; - } - $cmd = $cmd.chr($newchar); - $ret = $ret.chr($newchar2); - } - #-- write 1-Wire bus - my $res = OWX_Query($hash,$cmd); - #-- process result - if( $res eq $ret ){ - Log 5, "OWX: WriteBytePower OK"; - return 1; - } else { - Log 3, "OWX: WriteBytePower failure"; - return 0; - } -} - -######################################################################################## -# -# The following subroutines in alphabetical order are only for a DS9097 bus interface -# -######################################################################################## -# -# OWX_Block_9097 - Send data block ( -# -# Parameter hash = hash of bus master, data = string to send -# -# Return response, if OK -# 0 if not OK -# -######################################################################################## - -sub OWX_Block_9097 ($$) { - my ($hash,$data) =@_; - - my $data2=""; - my $res=0; - for (my $i=0; $i{HWDEVICE}; - - $owx_hwdevice->baudrate($owx_baud); - $owx_hwdevice->write_settings; - - if( $owx_debug > 2){ - my $res = "OWX: Sending out "; - for($i=0;$iwrite($cmd); - - Log 1, "OWX: Write incomplete $count_out ne ".(length($cmd))."" if ( $count_out != length($cmd) ); - #-- sleeping for some time - select(undef,undef,undef,0.01); - - #-- read the data - my ($count_in, $string_in) = $owx_hwdevice->read(48); - - if( $owx_debug > 2){ - my $res = "OWX: Receiving "; - for($i=0;$i<$count_in;$i++){ - $j=int(ord(substr($string_in,$i,1))/16); - $k=ord(substr($string_in,$i,1))%16; - $res.=sprintf "0x%1x%1x ",$j,$k; - } - Log 3, $res; - } - - #-- sleeping for some time - select(undef,undef,undef,0.01); - - return($string_in); -} - -######################################################################################## -# -# OWX_ReadBit_9097 - Read 1 bit from 1-wire bus (Fig. 5/6 from Maxim AN214) -# -# Parameter hash = hash of bus master -# -# Return bit value -# -######################################################################################## - -sub OWX_ReadBit_9097 ($) { - my ($hash) = @_; - - #-- set baud rate to 115200 and query!!! - my $sp1="\xFF"; - $owx_baud=115200; - my $res=OWX_Query_9097($hash,$sp1); - $owx_baud=9600; - #-- process result - if( substr($res,0,1) eq "\xFF" ){ - return 1; - } else { - return 0; - } -} - -######################################################################################## -# -# OWX_Reset_9097 - Reset the 1-Wire bus (Fig. 4 of Maxim AN192) -# -# Parameter hash = hash of bus master -# -# Return 1 : OK -# 0 : not OK -# -######################################################################################## - -sub OWX_Reset_9097 ($) { - - my ($hash)=@_; - my $cmd=""; - - #-- Reset command \xF0 - $cmd="\xF0"; - #-- write 1-Wire bus - my $res =OWX_Query_9097($hash,$cmd); - #-- TODO: process result - #-- may vary between 0x10, 0x90, 0xe0 - return 1; -} - -######################################################################################## -# -# OWX_Search_9097 - 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 OWX_Search_9097 ($$) { - - my ($hash,$mode)=@_; - - my ($sp1,$sp2,$response,$search_direction,$id_bit_number); - - #-- Response search data parsing operates bitwise - $id_bit_number = 1; - my $rom_byte_number = 0; - my $rom_byte_mask = 1; - my $last_zero = 0; - - #-- issue search command - $owx_baud=115200; - $sp2="\x00\x00\x00\x00\xFF\xFF\xFF\xFF"; - $response = OWX_Query_9097($hash,$sp2); - $owx_baud=9600; - #-- issue the normal search command \xF0 or the alarm search command \xEC - #if( $mode ne "alarm" ){ - # $sp1 = 0xF0; - #} else { - # $sp1 = 0xEC; - #} - - #$response = OWX_TouchByte($hash,$sp1); - - while ( $id_bit_number <= 64) { - #loop until through all ROM bytes 0-7 - my $id_bit = OWX_TouchBit_9097($hash,1); - my $cmp_id_bit = OWX_TouchBit_9097($hash,1); - - #print "id_bit = $id_bit, cmp_id_bit = $cmp_id_bit\n"; - - if( ($id_bit == 1) && ($cmp_id_bit == 1) ){ - #print "no devices present at id_bit_number=$id_bit_number \n"; - last; - } - if ( $id_bit != $cmp_id_bit ){ - $search_direction = $id_bit; - } else { - # hä ? if this discrepancy if before the Last Discrepancy - # on a previous next then pick the same as last time - if ( $id_bit_number < $owx_LastDiscrepancy ){ - if (($owx_ROM_ID[$rom_byte_number] & $rom_byte_mask) > 0){ - $search_direction = 1; - } else { - $search_direction = 0; - } - } else { - # if equal to last pick 1, if not then pick 0 - if ($id_bit_number == $owx_LastDiscrepancy){ - $search_direction = 1; - } else { - $search_direction = 0; - } - } - # if 0 was picked then record its position in LastZero - if ($search_direction == 0){ - $last_zero = $id_bit_number; - # check for Last discrepancy in family - if ($last_zero < 9) { - $owx_LastFamilyDiscrepancy = $last_zero; - } - } - } - # print "search_direction = $search_direction, last_zero=$last_zero\n"; - # set or clear the bit in the ROM byte rom_byte_number - # with mask rom_byte_mask - #print "ROM byte mask = $rom_byte_mask, search_direction = $search_direction\n"; - if ( $search_direction == 1){ - $owx_ROM_ID[$rom_byte_number] |= $rom_byte_mask; - } else { - $owx_ROM_ID[$rom_byte_number] &= ~$rom_byte_mask; - } - # serial number search direction write bit - $response = OWX_WriteBit_9097($hash,$search_direction); - # increment the byte counter id_bit_number - # and shift the mask rom_byte_mask - $id_bit_number++; - $rom_byte_mask <<= 1; - #-- if the mask is 0 then go to new rom_byte_number and - if ($rom_byte_mask == 256){ - $rom_byte_number++; - $rom_byte_mask = 1; - } - } - $owx_LastDiscrepancy = $last_zero; - return 1; -} - -######################################################################################## -# -# OWX_TouchBit_9097 - Write/Read 1 bit from 1-wire bus (Fig. 5-8 from Maxim AN 214) -# -# Parameter hash = hash of bus master -# -# Return bit value -# -######################################################################################## - -sub OWX_TouchBit_9097 ($$) { - my ($hash,$bit) = @_; - - my $sp1; - #-- set baud rate to 115200 and query!!! - if( $bit == 1 ){ - $sp1="\xFF"; - } else { - $sp1="\x00"; - } - $owx_baud=115200; - my $res=OWX_Query_9097($hash,$sp1); - $owx_baud=9600; - #-- process result - my $sp2=substr($res,0,1); - if( $sp1 eq $sp2 ){ - return 1; - }else { - return 0; - } -} - -######################################################################################## -# -# OWX_TouchByte_9097 - Write/Read 8 bit from 1-wire bus -# -# Parameter hash = hash of bus master -# -# Return bit value -# -######################################################################################## - -sub OWX_TouchByte_9097 ($$) { - my ($hash,$byte) = @_; - - my $loop; - my $result=0; - my $bytein=$byte; - - for( $loop=0; $loop < 8; $loop++ ){ - #-- shift result to get ready for the next bit - $result >>=1; - #-- if sending a 1 then read a bit else write 0 - if( $byte & 0x01 ){ - if( OWX_ReadBit_9097($hash) ){ - $result |= 0x80; - } - } else { - OWX_WriteBit_9097($hash,0); - } - $byte >>= 1; - } - return $result; -} - -######################################################################################## -# -# OWX_WriteBit_9097 - Write 1 bit to 1-wire bus (Fig. 7/8 from Maxim AN 214) -# -# Parameter hash = hash of bus master -# -# Return bit value -# -######################################################################################## - -sub OWX_WriteBit_9097 ($$) { - my ($hash,$bit) = @_; - - my $sp1; - #-- set baud rate to 115200 and query!!! - if( $bit ==1 ){ - $sp1="\xFF"; - } else { - $sp1="\x00"; - } - $owx_baud=115200; - my $res=OWX_Query_9097($hash,$sp1); - $owx_baud=9600; - #-- process result - if( substr($res,0,1) eq $sp1 ){ - return 1; - } else { - return 0; - } -} - -######################################################################################## -# -# The following subroutines in alphabetical order are only for a COC/CUNO interface -# -######################################################################################## -# -# OWX_Complex_CCC - 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 OWX_Complex_CCC ($$$$) { - my ($hash,$owx_dev,$data,$numread) =@_; - - my $select; - my $res = ""; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- has match ROM part - if( $owx_dev ){ - #-- ID of the device - my $owx_rnf = substr($owx_dev,3,12); - my $owx_f = substr($owx_dev,0,2); - - #-- 8 byte 1-Wire device address - my @owx_ROM_ID =(0,0,0,0 ,0,0,0,0); - #-- from search string to reverse string id - $owx_dev=~s/\.//g; - for(my $i=0;$i<8;$i++){ - $owx_ROM_ID[7-$i]=substr($owx_dev,2*$i,2); - } - $select=sprintf("Om%s%s%s%s%s%s%s%s",@owx_ROM_ID); - Log 3,"OWX: Sending match ROM to COC/CUNO ".$select - if( $owx_debug > 1); - #-- - CUL_SimpleWrite($owx_hwdevice, $select); - my ($err,$ob) = OWX_ReadAnswer_CCC($owx_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 ){ - OWX_Send_CCC($hash,$data); - $res .= $data; - } - #-- has receive part - if( $numread > 0 ){ - #$numread += length($data); - Log 3,"COC/CUNO is expected to deliver $numread bytes" - if( $owx_debug > 1); - $res.=OWX_Receive_CCC($hash,$numread); - } - Log 3,"OWX: returned from COC/CUNO $res" - if( $owx_debug > 1); - return $res; -} - -######################################################################################## -# -# OWX_Discover_CCC - Discover devices on the 1-Wire bus via internal firmware -# -# Parameter hash = hash of bus master -# -# Return 0 : error -# 1 : OK -# -######################################################################################## - -sub OWX_Discover_CCC ($) { - - my ($hash) = @_; - my $res; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- zero the array - @{$hash->{DEVS}}=(); - #-- reset the busmaster - OWX_ReInit_CCC($hash,0); - #-- get the devices - CUL_SimpleWrite($owx_hwdevice, "Oc"); - select(undef,undef,undef,0.5); - my ($err,$ob) = OWX_ReadAnswer_CCC($owx_hwdevice); - if( $ob ){ - Log 3,"OWX: Answer to ".$owx_hwdevice->{NAME}." device search is ".$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 { - Log 1, "OWX: No answer to ".$owx_hwdevice->{NAME}." device search"; - return 0; - } -} - -######################################################################################## -# -# OWX_ReadAnswer_CCC - Replacement for CUL_ReadAnswer for better control -# -# Parameter: hash = hash of bus master -# -# Return: string received -# -######################################################################################## - -sub -OWX_ReadAnswer_CCC($) -{ - my ($hash) = @_; - - my $type = $hash->{TYPE}; - - my $arg =""; - my $anydata=0; - my $regexp =undef; - - my ($mculdata, $rin) = ("", ''); - my $buf; - my $to = 3; # 3 seconds timeout - $to = $hash->{RA_Timeout} if($hash->{RA_Timeout}); # ...or less - for(;;) { - return ("Device lost when reading answer for get $arg", undef) - if(!$hash->{FD}); - - vec($rin, $hash->{FD}, 1) = 1; - my $nfound = select($rin, undef, undef, $to); - if($nfound < 0) { - next if ($! == EAGAIN() || $! == EINTR() || $! == 0); - my $err = $!; - DevIo_Disconnected($hash); - return("OWX_ReadAnswer_CCC $arg: $err", undef); - } - return ("Timeout reading answer for get $arg", undef) - if($nfound == 0); - $buf = DevIo_SimpleRead($hash); - return ("No data", undef) if(!defined($buf)); - - - - if($buf) { - Log 5, "CUL/RAW (ReadAnswer): $buf"; - $mculdata .= $buf; - } - - # \n\n is socat special - if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) { - if($regexp && $mculdata !~ m/$regexp/) { - CUL_Parse($hash, $hash, $hash->{NAME}, $mculdata, $hash->{initString}); - } else { - return (undef, $mculdata) - } - } - } - -} - -######################################################################################## -# -# OWX_Receive_CCC - Read data from the 1-Wire bus -# -# Parameter: hash = hash of bus master, numread = number of bytes to read -# -# Return: string received -# -######################################################################################## - -sub OWX_Receive_CCC ($$) { - my ($hash,$numread) = @_; - - my $res=""; - my $res2=""; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - for( - my $i=0;$i<$numread;$i++){ - #Log 1, "Sending $owx_hwdevice->{NAME}: OrB"; - #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "OrB")); - CUL_SimpleWrite($owx_hwdevice, "OrB"); - select(undef,undef,undef,0.01); - my ($err,$ob) = OWX_ReadAnswer_CCC($owx_hwdevice); - #Log 1, "Answer from $owx_hwdevice->{NAME}:$ob: "; - - #-- process results - if( !(defined($ob)) ){ - return ""; - #-- four bytes received makes one byte of result - }elsif( length($ob) == 4 ){ - $res .= sprintf("%c",hex(substr($ob,0,2))); - $res2 .= "0x".substr($ob,0,2)." "; - #-- 11 bytes received makes one byte of result - }elsif( length($ob) == 11 ){ - $res .= sprintf("%c",hex(substr($ob,9,2))); - $res2 .= "0x".substr($ob,9,2)." "; - #-- 18 bytes received from CUNO - }elsif( length($ob) == 18 ){ - - my $res = "OWX: Receiving 18 bytes from CUNO: $ob\n"; - for(my $i=0;$i{NAME}; - } - } - Log 3, "OWX: Received $numread bytes = $res2 on bus ".$owx_hwdevice->{NAME} - if( $owx_debug > 1); - - return($res); -} - -######################################################################################## -# -# OWX_Reset_CCC - Reset the 1-Wire bus -# -# Parameter hash = hash of bus master -# -# Return 1 : OK -# 0 : not OK -# -######################################################################################## - -sub OWX_Reset_CCC ($) { - my ($hash) = @_; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "ORb")); - - if( substr($ob,9,4) eq "OK:1" ){ - return 1; - }else{ - return 0 - } -} - -######################################################################################## -# -# OWX_ReInit_CCC - Reset the 1-Wire bus master chip or subsystem -# -# Parameter hash = hash of bus master -# typ = 0 for bus master, 1 for subsystem -# -# Return 1 : OK -# 0 : not OK -# -######################################################################################## - -sub OWX_ReInit_CCC ($$) { - my ($hash,$type) = @_; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - if ( $type eq "0") { - my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "ORm")); - return 0 if( !defined($ob) ); - return 0 if( length($ob) < 13); - if( substr($ob,9,4) eq "OK" ){ - return 1; - }else{ - return 0 - } - }elsif( $type eq "1" ){ - my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "Oi")); - #Log 1, "Answer of sending Oi to ".$owx_hwdevice->{NAME}." was $ob"; - return 1; - } -} - -######################################################################################### -# -# OWX_Send_CCC - Send data block -# -# Parameter hash = hash of bus master, data = string to send -# -# Return response, if OK -# 0 if not OK -# -######################################################################################## - -sub OWX_Send_CCC ($$) { - my ($hash,$data) =@_; - - my ($i,$j,$k); - my $res = ""; - my $res2 = ""; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - for( $i=0;$i 1); -} - -######################################################################################## -# -# OWX_Verify_CCC - 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 OWX_Verify_CCC ($$) { - my ($hash,$dev) = @_; - - my $i; - - #-- get the interface - my $owx_hwdevice = $hash->{HWDEVICE}; - - #-- Ask the COC/CUNO - CUL_SimpleWrite($owx_hwdevice, "OCf"); - #-- sleeping for some time - select(undef,undef,undef,3); - CUL_SimpleWrite($owx_hwdevice, "Oc"); - select(undef,undef,undef,0.5); - my ($err,$ob) = OWX_ReadAnswer_CCC($owx_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; -} - - 1; =pod -=item device -=item summary backend module for the OWX system +=item helper +=item summary to commmunicate with 1-Wire bus devices +=item summary_DE zur Kommunikationm mit 1-Wire Geräten =begin html

OWX

-

FHEM module to commmunicate with 1-Wire bus devices

+

Backend module to commmunicate with 1-Wire bus devices

    -
  • via an active DS2480/DS2482/DS2490/DS9097U bus master interface attached to an USB +
  • via an active DS2480/DS9097U bus master interface attached to an USB port or
  • -
  • via a passive DS9097 interface attached to an USB port or
  • +
  • via an active DS2480 bus master interface attached to a TCP/IP-UART interface
  • via a network-attached CUNO or through a COC on the RaspBerry Pi
  • via an Arduino running OneWireFirmata attached to USB
Internally these interfaces are vastly different, read the corresponding Wiki pages + href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire"> Wiki pages . + The passive DS9097 interface is no longer suppoorted.

Example


@@ -2556,6 +1700,8 @@ sub OWX_Verify_CCC ($$) {

Define

define <name> OWX <serial-device> or
+ define <name> OWX <tcpip>[:<port>] or
+ # define OWX for TCP/IP-UART interfaces or define <name> OWX <cuno/coc-device> or
define <name> OWX <arduino-pin>

Define a 1-Wire interface to communicate with a 1-Wire bus.
@@ -2577,25 +1723,11 @@ sub OWX_Verify_CCC ($$) {

Set

    -
  • - set <name> interval <value> +
  • + set <name> reopen -

    sets the time period in seconds for "kicking" the 1-Wire bus (default - is 300 seconds). This means:
      -
    • With 1-Wire bus interfaces that do not supply power to the 1-Wire bus (attr - buspower parasitic), the 1-Wire bus is reset at these intervals.
    • -
    • With 1-Wire bus interfaces that supply power to the 1-Wire bus (attr - buspower = real), all temperature measurement devices on the bus receive the - command to start a temperature conversion (saves a lot of time when reading)
    • -
    • With 1-Wire bus interfaces that contain a busmaster chip, the response to a - reset pulse contains information about alarms.
    • -

    +
    re-initializes the 1-Wire bus.
  • -
  • - set <name> followAlarms on|off - -

    instructs the module to start an alarm search in case a reset pulse - discovers any 1-Wire device which has the alarm flag set.

@@ -2616,16 +1748,27 @@ sub OWX_Verify_CCC ($$) {

Attributes

- =end html +=begin html_DE + + +

YAAHM

+Deutsche Dokumentation im Wiki vorhanden, die englische Version gibt es hier: YAAHM +=end html_DE =cut