From 2a10daae724ee4749123bc8d0fbf26a9d478149f Mon Sep 17 00:00:00 2001 From: pahenning <> Date: Fri, 4 Jan 2013 07:11:41 +0000 Subject: [PATCH] git-svn-id: https://svn.fhem.de/fhem/trunk@2428 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/00_OWX.pm | 850 +++++++++++++++++++++++++++----------------- 1 file changed, 517 insertions(+), 333 deletions(-) diff --git a/fhem/FHEM/00_OWX.pm b/fhem/FHEM/00_OWX.pm index 7c8103417..c69bdd377 100644 --- a/fhem/FHEM/00_OWX.pm +++ b/fhem/FHEM/00_OWX.pm @@ -2,18 +2,19 @@ # # OWX.pm # -# $Id$ -# # 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 # * via a network-attached CUNO -# Internally these interfaces are vastly different, read the corresponding Wiki pages -# http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire +# * via a COC attached to a Raspberry Pi # -# Version 2.26 - November, 2012 +# Prof. Dr. Peter A. Henning # -# Prof. Dr. Peter A. Henning, 2012 +# Contributions from: Martin Fischer, Rudolf König, Boris Neubert, Joachim Herold +# +# $Id$ +# +######################################################################################## # # define OWX for USB interfaces or # define OWX for a CUNO or COC interface @@ -58,7 +59,17 @@ package main; use strict; use warnings; -use Device::SerialPort; + +#-- unfortunately some things OS-dependent +my $owgdevregexp; +if ($^O=~m/linux/i) { + require Device::SerialPort; + $owgdevregexp= "/dev/"; +} else { + require Win32::SerialPort; + $owgdevregexp= "com"; +} + use vars qw{%attr %defs}; require "$attr{global}{modpath}/FHEM/DevIo.pm"; @@ -85,21 +96,13 @@ my %attrs = ( ); #-- some globals needed for the 1-Wire module -my $owx_hwdevice; #-- baud rate serial interface my $owx_baud=9600; +my $owx_cmdlen; #-- Debugging 0,1,2,3 my $owx_debug=0; -#-- bus master mode -my $owx_mode="undef"; -#-- bus interface -my $owx_interface=""; #-- 8 byte 1-Wire device address my @owx_ROM_ID =(0,0,0,0 ,0,0,0,0); -#-- List of addresses found on the bus -my @owx_devs=(); -my @owx_fams=(); -my @owx_alarm_devs=(); #-- 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 @@ -144,6 +147,8 @@ sub OWX_Define ($$) { my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); + my $owx_hwdevice; + #-- check syntax if(int(@a) < 3){ return "OWX: Syntax error - must be define OWX" @@ -155,16 +160,17 @@ sub OWX_Define ($$) { #-- If this line contains 3 parameters, it is the bus master definition my $dev = $a[2]; - #-- TODO: what should we do when the specified device name contains @ already ? - $hash->{DeviceName} = $dev."\@9600"; - #-- Dummy 1-Wire ROM identifier - $hash->{ROM_ID} = "FF"; - + #-- Dummy 1-Wire ROM identifier, empty device lists + $hash->{ROM_ID} = "FF"; + $hash->{DEVS} = (); + $hash->{ALARMDEVS} = (); + #-- First step: check if we have a directly connected serial interface or a CUNO/COC attached - # (mod suggested by T.Faust) - if ( $dev =~ m/\/dev\/.*/ ){ - #-- TODO: what should we do when the specified device name contains @ already ? - $hash->{DeviceName} = $dev."\@9600"; + 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); @@ -188,44 +194,58 @@ sub OWX_Define ($$) { #-- sleeping for some time select(undef,undef,undef,0.1); + #-- In 10 seconds discover all devices on the 1-Wire bus + InternalTimer(gettimeofday()+10, "OWX_Discover", $hash,0); + } else { $hash->{DeviceName} = $dev; #-- Second step in case of CUNO: See if we can open it my $msg = "OWX: CUNO/COC device $dev"; + #-- hash des COC/CUNO $owx_hwdevice = $main::defs{$dev}; if($owx_hwdevice){ Log 1,$msg." defined"; #-- store with OWX device $hash->{INTERFACE} = "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,10); + } + return "OWX: Can't open ".$dev if( $owx_hwdevice->{STATE} ne "Initialized"); #-- reset the 1-Wire system in CUNO CUL_SimpleWrite($owx_hwdevice, "Oi"); }else{ Log 1, $msg." not defined"; - return "OWX: Can't open CUNO/COC device $dev: $!" + return $msg." not defined"; } + #-- sleeping for some time + select(undef,undef,undef,0.5); + #-- In 10 seconds discover all devices on the 1-Wire bus + InternalTimer(gettimeofday()+10, "OWX_Discover", $hash,0); + } #-- Third step: see, if a bus interface is detected if (!OWX_Detect($hash)){ - $hash->{STATE} = "Failed"; $hash->{PRESENT} = 0; + readingsSingleUpdate($hash,"state","failed",1); $init_done = 1; return undef; } - #-- In 10 seconds discover all devices on the 1-Wire bus - InternalTimer(gettimeofday()+10, "OWX_Discover", $hash,0); - #-- Default settings - $hash->{interval} = 300; # kick every minute + $hash->{interval} = 300; # kick every 5 minutes $hash->{followAlarms} = "off"; $hash->{ALARMED} = "no"; #-- InternalTimer blocks if init_done is not true my $oid = $init_done; $hash->{PRESENT} = 1; - $hash->{STATE} = "Initialized"; + readingsSingleUpdate($hash,"state","defined",1); $init_done = 1; #-- Intiate first alarm detection and eventually conversion in a minute or so InternalTimer(gettimeofday() + $hash->{interval}, "OWX_Kick", $hash,1); @@ -249,15 +269,17 @@ sub OWX_Define ($$) { sub OWX_Alarms ($) { my ($hash) = @_; my $name = $hash->{NAME}; + my @owx_alarm_names=(); + + $hash->{ALARMDEVS}=(); #-- Discover all alarmed devices on the 1-Wire bus - @owx_alarm_devs=(); my $res = OWX_First_SER($hash,"alarm"); while( $owx_LastDeviceFlag==0 && $res != 0){ $res = $res & OWX_Next_SER($hash,"alarm"); } - if( @owx_alarm_devs == 0){ + if( @{$hash->{ALARMDEVS}} == 0){ return "OWX: No alarmed 1-Wire devices found on bus $name"; } @@ -267,7 +289,7 @@ sub OWX_Alarms ($) { next if( $name eq $main::defs{$fhem_dev}{NAME} ); #-- all OW types start with OW next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW"); - foreach my $owx_dev (@owx_alarm_devs) { + foreach my $owx_dev (@{$hash->{ALARMDEVS}}) { #-- two 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); @@ -303,12 +325,12 @@ sub OWX_Complex ($$$$) { my $name = $hash->{NAME}; #-- get the interface - $owx_interface = $hash->{INTERFACE}; - $owx_hwdevice = $hash->{HWDEVICE}; + my $owx_interface = $hash->{INTERFACE}; + my $owx_hwdevice = $hash->{HWDEVICE}; #-- interface error if( !(defined($owx_interface))){ - Log 3,"OWX: Complex called with unknown interface on bus $name"; + 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") ){ @@ -385,22 +407,25 @@ sub OWX_CRC ($) { sub OWX_CRC8 ($$) { my ($string,$crc) = @_; - my $crc0=ord($crc); my $crc8=0; my @strhex; + + #Log 1,"CRC8 calculated for string of length ".length($string); - for(my $i=0; $i<8; $i++){ + for(my $i=0; $i New CRC value = %x",$crc; +sub OWX_CRC16 ($$$) { + my ($string,$crclo,$crchi) = @_; + my $crc16=0; + my @strhex; + + #Log 1,"CRC16 calculated for string of length ".length($string); + + for(my $i=0; $i> 8); } - return $crc; -} - - -sub OWX_DOCRC16($$) { - my ($crc,$c) = @_; - - #-- polynomial for x^16 + x^15 + x^2 + 1 - my $mask = 0xA001; - - my $i; - for($i=0;$i<8;$i++) { - if(($crc ^ ord($c)) & 1) { - $crc=($crc>>1)^$mask; - } else { - $crc>>=1; + + if( defined($crclo) & defined($crchi) ){ + my $crc = (255-ord($crclo))+256*(255-ord($crchi)); + if ($crc == $crc16 ){ + return 1; + }else{ + return 0; } - $c>>=1; + }else{ + return $crc16; } - return ($crc); -} +} ######################################################################################## # @@ -468,18 +522,18 @@ sub OWX_Detect ($) { $ress = $ress0; #-- get the interface - $owx_interface = $hash->{INTERFACE}; - $owx_hwdevice = $hash->{HWDEVICE}; + 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"); + OWX_Query_2480($hash,"\xC1",1); #-- Max 4 tries to detect an interface - for($l=0;$l<4;$l++) { + 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"); + $res = OWX_Query_2480($hash,"\x17\x45\x5B\x0F\x91",5); #-- process 4/5-byte string for detection if( !defined($res)){ @@ -522,18 +576,23 @@ sub OWX_Detect ($) { $ress.=sprintf "0x%1x%1x ",$j,$k; } } - #-- here we treat the network-connected CUNO + #-- here we treat the CUNO/COC } else { - #-- sleeping for some time - select(undef,undef,undef,0.5); - #-- Max 4 tries to detect an interface - for($l=0;$l<4;$l++) { + select(undef,undef,undef,2); + #-- Max 6 tries to detect an interface + #Log 1, "Sending $owx_hwdevice->{NAME}: ORm"; + #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "ORm")); + #Log 1, "Answer from $owx_hwdevice->{NAME}:$ob: "; + #select(undef,undef,undef,3); + for($l=0;$l<6;$l++) { #-- write 1-Wire bus - CUL_SimpleWrite($owx_hwdevice, "ORm"); - my $ob = OWX_SimpleRead($owx_hwdevice); - + # Log 1, "Sending $owx_hwdevice->{NAME}: ORm"; + my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "ORm")); + # Log 1, "Answer from $owx_hwdevice->{NAME}:$ob: "; +# if( $owx_hwdevice->{NAME} eq "CUNO"); + #-- process result for detection - #Log 1,"ERGEBNIS ORm =>".$ob."<="; +# Log 1,"ERGEBNIS Oi =>".$ob."<="; if( !defined($ob)){ $ob=""; $ret=0; @@ -550,7 +609,7 @@ sub OWX_Detect ($) { Log 1, $ress; $ress = $ress0; #-- sleeping for some time - select(undef,undef,undef,0.5); + select(undef,undef,undef,10); } if( $ret == 0 ){ $owx_interface=undef; @@ -578,15 +637,19 @@ sub OWX_Detect ($) { sub OWX_Discover ($) { my ($hash) = @_; my $res; + my $ret= ""; my $name = $hash->{NAME}; + my $exname; + my $acname; #-- get the interface - $owx_interface = $hash->{INTERFACE}; - $owx_hwdevice = $hash->{HWDEVICE}; + my $owx_interface = $hash->{INTERFACE}; + my $owx_hwdevice = $hash->{HWDEVICE}; + my @owx_names=(); #-- Discover all devices on the 1-Wire bus - @owx_devs=(); - my @owx_names=(); + return undef + if( !defined($owx_interface) ); #-- directly connected interface if( $owx_interface =~ m/DS.*/ ){ $res = OWX_First_SER($hash,"discover"); @@ -595,28 +658,11 @@ sub OWX_Discover ($) { } #-- Ask the cuno }else { - 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 $ob = OWX_SimpleRead($owx_hwdevice); - if( $ob ){ - foreach my $dx (split(/\n/,$ob)){ - $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 (@owx_devs,$ddx); - } - } + $res = OWX_Discover_CUNO($hash); } #-- Go through all devices found on this bus - foreach my $owx_dev (@owx_devs) { + 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"; @@ -636,7 +682,8 @@ sub OWX_Discover ($) { #-- skip if busmaster # next if( $hash->{NAME} eq $main::defs{$fhem_dev}{NAME} ); #-- all OW types start with OW - next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW"); + next if( !defined($main::defs{$fhem_dev}{TYPE})); + 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 ); @@ -650,7 +697,8 @@ sub OWX_Discover ($) { ", correcting to ".substr($id_owx,0,2); $main::defs{$fhem_dev}{OW_FAMILY} = substr($id_owx,0,2); } - push(@owx_names,$main::defs{$fhem_dev}{NAME}); + $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; @@ -659,73 +707,99 @@ sub OWX_Discover ($) { } # } - - #-- autocreate the device - if( $match==0 ){ - #-- Default name OWX_FF_XXXXXXXXXXXX, default type = OWX_FF - my $name = sprintf "OWX_%s_%s",$owx_f,$owx_rnf; - #-- Family 10 = Temperature sensor, assume DS1820 as default - if( $owx_f eq "10" ){ - CommandDefine(undef,"$name OWTHERM DS1820 $owx_rnf"); - #-- Family 12 = Switch, assume DS2406 as default - }elsif( $owx_f eq "12" ){ - CommandDefine(undef,"$name OWSWITCH DS2406 $owx_rnf"); - #-- Family 1D = Counter/RAM, assume DS2423 as default - }elsif( $owx_f eq "1D" ){ - CommandDefine(undef,"$name OWCOUNT DS2423 $owx_rnf"); - #-- Family 20 = A/D converter, assume DS2450 as default - } elsif( $owx_f eq "20" ){ - CommandDefine(undef,"$name OWAD DS2450 $owx_rnf"); - #-- Family 22 = Temperature sensor, assume DS1822 as default - }elsif( $owx_f eq "22" ){ - CommandDefine(undef,"$name OWTHERM DS1822 $owx_rnf"); - #-- Family 26 = Multisensor, assume DS2438 as default - }elsif( $owx_f eq "26" ){ - CommandDefine(undef,"$name OWMULTI DS2438 $owx_rnf"); - #-- Family 28 = Temperature sensor, assume DS18B20 as default - }elsif( $owx_f eq "28" ){ - CommandDefine(undef,"$name OWTHERM DS18B20 $owx_rnf"); - #-- Family 29 = Switch, assume DS2408 as default - }elsif( $owx_f eq "29" ){ - CommandDefine(undef,"$name OWSWITCH DS2408 $owx_rnf"); - #-- Family 3A = Switch, assume DS2413 as default - }elsif( $owx_f eq "3A" ){ - CommandDefine(undef,"$name OWSWITCH DS2413 $owx_rnf"); - #-- Family FF = LCD display - }elsif( $owx_f eq "FF" ){ - CommandDefine(undef,"$name OWLCD $owx_rnf"); - #-- All unknown families are ID only (ID-Chips have family id 09) - } else { - CommandDefine(undef,"$name OWID $owx_f $owx_rnf"); - } - #-- yes, it is on the bus and therefore present - push(@owx_names,$name); + + #-- Determine the device type + 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 3A = Switch DS2413 + }elsif( $owx_f eq "3A" ){ + $chip = "DS2413"; + $acstring = "OWSWITCH DS2413"; + #-- Family FF = LCD display + }elsif( $owx_f eq "FF" ){ + $chip = "LCD Cont"; + $acstring = "OWLCD"; + #-- All unknown families are ID only (ID-Chips have family id 09) + } else { + $chip = "unknown"; + $acstring = "OWID $owx_f"; + } + #-- autocreate the device if necessary + if( $match==1 ){ + $ret .= sprintf("%s %-10s %s\n", $owx_f.$owx_rnf, $chip, $exname); + # + }else{ + my $acname = sprintf "OWX_%s_%s",$owx_f,$owx_rnf; + CommandDefine(undef,"$name $acstring $owx_rnf"); + push(@owx_names,$acname); $main::defs{$name}{PRESENT}=1; #-- THIS IODev, default room - CommandAttr (undef,"$name IODev $hash->{NAME}"); - CommandAttr (undef,"$name room OWX"); + #CommandAttr (undef,"$name IODev $hash->{NAME}"); + CommandAttr (undef,"$acname room OWX"); #-- replace the ROM ID by the proper value - $main::defs{$name}{ROM_ID}=$owx_dev; + $main::defs{$acname}{ROM_ID}=$owx_dev; + $ret .= sprintf("%s %-10s %s\n", $owx_f.$owx_rnf, $chip, $acname); } + } #-- final step: Undefine all 1-Wire devices which are not on this bus but have this IODev foreach my $fhem_dev (sort keys %main::defs) { #-- skip if malformed device #next if( !defined($main::defs{$fhem_dev}{NAME}) ); - #-- all OW types start with OW + #-- all OW types start with OW, but safeguard against deletion of other devices + next if( !defined($main::defs{$fhem_dev}{TYPE})); next if( substr($main::defs{$fhem_dev}{TYPE},0,2) ne "OW"); + next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWX"); + next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWFS"); + next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWSERVER"); + next if( uc($main::defs{$fhem_dev}{TYPE}) eq "OWDEVICE"); #-- skip if the device is present. next if( $main::defs{$fhem_dev}{PRESENT} == 1); - #-- skip if different IODev, but only if IODev exists + #-- skip if different IODev, but only if other IODev exists 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, "OWX: 1-Wire devices found on bus $name: (".join(",",@owx_names).")"; - return "OWX: 1-Wire devices found on bus $name: (".join(",",@owx_names).")"; + #-- Log the discovered devices + Log 1, "OWX: 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; + } ######################################################################################## @@ -808,8 +882,8 @@ sub OWX_Reset ($) { my ($hash)=@_; #-- get the interface - $owx_interface = $hash->{INTERFACE}; - $owx_hwdevice = $hash->{HWDEVICE}; + my $owx_interface = $hash->{INTERFACE}; + my $owx_hwdevice = $hash->{HWDEVICE}; #-- interface error if( !(defined($owx_interface))){ @@ -910,8 +984,7 @@ sub OWX_Verify ($$) { my $i; #-- get the interface - $owx_interface = $hash->{INTERFACE}; - $owx_hwdevice = $hash->{HWDEVICE}; + my $owx_interface = $hash->{INTERFACE}; #-- directly connected interface if( ($owx_interface eq "DS2480") || ($owx_interface eq "DS9097") ){ @@ -951,6 +1024,10 @@ sub OWX_Complex_SER ($$$$) { 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 @@ -1065,6 +1142,12 @@ sub OWX_Next_SER ($$) { sub OWX_Search_SER ($$) { my ($hash,$mode)=@_; + my @owx_fams=(); + + #-- get the interface + my $owx_interface = $hash->{INTERFACE}; + my $owx_hwdevice = $hash->{HWDEVICE}; + #-- if the last call was the last one, no search if ($owx_LastDeviceFlag==1){ return 0; @@ -1085,7 +1168,7 @@ sub OWX_Search_SER ($$) { }elsif( $owx_interface eq "DS9097" ){ OWX_Search_9097($hash,$mode); }else{ - Log 1,"OWX: Search called with unknown interface"; + Log 1,"OWX: Search called with unknown interface ".$owx_interface; return 0; } #--check if we really found a device @@ -1100,8 +1183,7 @@ sub OWX_Search_SER ($$) { #-- character version of device ROM_ID, first byte = family my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@owx_ROM_ID); - #Log 1,"OWX: Found device $dev"; - + #-- for some reason this does not work - replaced by another test, see below #if( $owx_LastDiscrepancy==0 ){ # $owx_LastDeviceFlag=1; @@ -1127,7 +1209,7 @@ sub OWX_Search_SER ($$) { } } push(@owx_fams,substr($dev,0,2)) if( !$famfnd ); - foreach (@owx_devs){ + foreach (@{$hash->{DEVS}}){ if( $dev eq $_ ){ #-- if present, set the last device found flag $owx_LastDeviceFlag=1; @@ -1136,15 +1218,15 @@ sub OWX_Search_SER ($$) { } if( $owx_LastDeviceFlag!=1 ){ #-- push to list - push(@owx_devs,$dev); + 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<@owx_alarm_devs;$i++){ - if( $dev eq $owx_alarm_devs[$i] ){ + 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; @@ -1152,7 +1234,7 @@ sub OWX_Search_SER ($$) { } if( $owx_LastDeviceFlag!=1 ){ #--push to list - push(@owx_alarm_devs,$dev); + push(@{$hash->{ALARMDEVS}},$dev); Log 5, "OWX: New alarm device found $dev"; } return 1; @@ -1216,9 +1298,10 @@ sub OWX_Block_2480 ($$) { my ($hash,$data) =@_; my $data2=""; + my $retlen = length($data); #-- if necessary, prepend E1 character for data mode - if( ($owx_mode ne "data") && (substr($data,0,1) ne '\xE1')) { + if( substr($data,0,1) ne '\xE1') { $data2 = "\xE1"; } #-- all E3 characters have to be duplicated @@ -1230,7 +1313,7 @@ sub OWX_Block_2480 ($$) { } } #-- write 1-Wire bus as a single string - my $res =OWX_Query_2480($hash,$data2); + my $res =OWX_Query_2480($hash,$data2,$retlen); return $res; } @@ -1248,15 +1331,16 @@ sub OWX_Block_2480 ($$) { sub OWX_Level_2480 ($$) { my ($hash,$newlevel) =@_; my $cmd=""; + my $retlen=0; #-- if necessary, prepend E3 character for command mode - if( $owx_mode ne "command") { - $cmd = "\xE3"; - } + $cmd = "\xE3"; + #-- return to normal level if( $newlevel eq "normal" ){ $cmd=$cmd."\xF1\xED\xF1"; + $retlen+=3; #-- write 1-Wire bus - my $res = OWX_Query_2480($hash,$cmd); + my $res = OWX_Query_2480($hash,$cmd,$retlen); #-- process result my $r1 = ord(substr($res,0,1)) & 236; my $r2 = ord(substr($res,1,1)) & 236; @@ -1270,8 +1354,9 @@ sub OWX_Level_2480 ($$) { #-- start pulse } else { $cmd=$cmd."\x3F\xED"; + $retlen+=2; #-- write 1-Wire bus - my $res = OWX_Query_2480($hash,$cmd); + my $res = OWX_Query_2480($hash,$cmd,$retlen); #-- process result if( $res eq "\x3E" ){ Log 5, "OWX: Level change OK"; @@ -1293,17 +1378,21 @@ sub OWX_Level_2480 ($$) { # ######################################################################################## -sub OWX_Query_2480 ($$) { +sub OWX_Query_2480 ($$$) { - my ($hash,$cmd) = @_; - my ($i,$j,$k); + my ($hash,$cmd,$retlen) = @_; + my ($i,$j,$k,$l,$m,$n); + my $string_in = ""; + my $string_part; + + #-- get hardware device + my $owx_hwdevice = $hash->{HWDEVICE}; - $owx_hwdevice = $hash->{HWDEVICE}; $owx_hwdevice->baudrate($owx_baud); $owx_hwdevice->write_settings; if( $owx_debug > 2){ - my $res = "OWX: Sending out "; + 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) ); + + 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 - 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; + #-- 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.04); + select(undef,undef,undef,0.01); return($string_in); } @@ -1351,28 +1457,28 @@ sub OWX_Reset_2480 ($) { my ($hash)=@_; my $cmd=""; + my $name = $hash->{NAME}; my ($res,$r1,$r2); #-- if necessary, prepend \xE3 character for command mode - if( $owx_mode ne "command" ){ - $cmd = "\xE3"; - } + $cmd = "\xE3"; + #-- Reset command \xC5 $cmd = $cmd."\xC5"; #-- write 1-Wire bus - $res =OWX_Query_2480($hash,$cmd); + $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); + $res =OWX_Query_2480($hash,$cmd,1); } #-- process result $r1 = ord(substr($res,0,1)) & 192; if( $r1 != 192){ - Log 3, "OWX: Reset failure"; + Log 3, "OWX: Reset failure on bus $name"; return 0; } $hash->{ALARMED} = "no"; @@ -1383,7 +1489,7 @@ sub OWX_Reset_2480 ($) { #Log 3, "OWX: No presence detected"; return 1; }elsif( $r2 ==2 ){ - Log 1, "OWX: Alarm presence detected"; + Log 1, "OWX: Alarm presence detected on bus $name"; $hash->{ALARMED} = "yes"; } return 1; @@ -1447,8 +1553,8 @@ sub OWX_Search_2480 ($$) { } #-- 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); - $response = OWX_Query_2480($hash,$sp2); + $response = OWX_Query_2480($hash,$sp1,1); + $response = OWX_Query_2480($hash,$sp2,16); #-- interpret the return data if( length($response)!=16 ) { @@ -1510,9 +1616,8 @@ sub OWX_WriteBytePower_2480 ($$) { my $cmd="\x3F"; my $ret="\x3E"; #-- if necessary, prepend \xE3 character for command mode - if( $owx_mode ne "command") { - $cmd = "\xE3".$cmd; - } + $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; @@ -1578,7 +1683,9 @@ sub OWX_Query_9097 ($$) { my ($hash,$cmd) = @_; my ($i,$j,$k); - $owx_hwdevice = $hash->{HWDEVICE}; + #-- get hardware device + my $owx_hwdevice = $hash->{HWDEVICE}; + $owx_hwdevice->baudrate($owx_baud); $owx_hwdevice->write_settings; @@ -1894,6 +2001,9 @@ sub OWX_Complex_CUNO ($$$$) { my $select; my $res = ""; + #-- get the interface + my $owx_hwdevice = $hash->{HWDEVICE}; + #-- has match ROM part if( $owx_dev ){ #-- ID of the device @@ -1910,8 +2020,9 @@ sub OWX_Complex_CUNO ($$$$) { $select=sprintf("Om%s%s%s%s%s%s%s%s",@owx_ROM_ID); Log 3,"OWX: Sending match ROM to CUNO ".$select if( $owx_debug > 1); + #-- CUL_SimpleWrite($owx_hwdevice, $select); - my $ob = OWX_SimpleRead($owx_hwdevice); + my ($err,$ob) = CUL_ReadAnswer($owx_hwdevice,"",0,undef); #-- padding first 9 bytes into result string, since we have this # in the serial interfaces as well $res .= "000000000"; @@ -1931,6 +2042,51 @@ sub OWX_Complex_CUNO ($$$$) { return $res; } +######################################################################################## +# +# OWX_Discover_CUNO - Discover devices on the 1-Wire bus via CUNO internal firmware +# +# Parameter hash = hash of bus master +# +# Return 0 : error +# 1 : OK +# +######################################################################################## + +sub OWX_Discover_CUNO ($) { + + my ($hash) = @_; + my $res; + + #-- get the interface + my $owx_hwdevice = $hash->{HWDEVICE}; + + #-- zero the array + @{$hash->{DEVS}}=(); + OWX_ReInit_CUNO($hash,0); + CUL_SimpleWrite($owx_hwdevice, "Oc"); + select(undef,undef,undef,0.5); + my ($err,$ob) = CUL_ReadAnswer($owx_hwdevice,"",0,undef); + if( $ob ){ + #Log 1,"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_Receive_CUNO - Read from the CUNO @@ -1943,13 +2099,19 @@ sub OWX_Complex_CUNO ($$$$) { sub OWX_Receive_CUNO ($$) { my ($hash,$numread) = @_; + my $res=""; my $res2=""; + #-- get the interface + my $owx_hwdevice = $hash->{HWDEVICE}; + for( my $i=0;$i<$numread;$i++){ - CUL_SimpleWrite($owx_hwdevice, "OrB"); - my $ob = OWX_SimpleRead($owx_hwdevice); + #Log 1, "Sending $owx_hwdevice->{NAME}: OrB"; + my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "OrB")); + #Log 1, "Answer from $owx_hwdevice->{NAME}:$ob: "; + #-- process results if( !(defined($ob)) ){ return ""; @@ -1957,14 +2119,18 @@ sub OWX_Receive_CUNO ($$) { }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)." "; #-- 20 bytes received = leftover from match }elsif( length($ob) == 20 ){ $numread++; }else{ - Log 1,"OWX: Received unexpected number of ".length($ob)." bytes from CUNO/COC"; + Log 1,"OWX: Received unexpected number of ".length($ob)." bytes on bus ".$owx_hwdevice->{NAME}; } } - Log 3, "OWX: Receive from CUNO/COC $numread bytes = $res2" + Log 3, "OWX: Received $numread bytes = $res2 on bus ".$owx_hwdevice->{NAME} if( $owx_debug > 1); return($res); @@ -1982,15 +2148,54 @@ sub OWX_Receive_CUNO ($$) { ######################################################################################## sub OWX_Reset_CUNO ($) { - CUL_SimpleWrite($owx_hwdevice, "ORb"); - my $ob = OWX_SimpleRead($owx_hwdevice); - if( substr($ob,0,4) eq "OK:1" ){ + 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_CUNO - 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_CUNO ($$) { + 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_CUNO - Send data block @@ -2004,10 +2209,14 @@ sub OWX_Reset_CUNO ($) { sub OWX_Send_CUNO ($$) { 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_SimpleRead - Reading with retry. -# Suggested in this way by Dirk Tostmann -# -# Parameter hash = hash of device -# -# Return response, if OK -# 0 if not OK -# -######################################################################################## - -sub OWX_SimpleRead($) -{ - my ($hash) = @_; - my $buf = DevIo_DoSimpleRead($owx_hwdevice); - - # Lets' try again: Some drivers return len(0) on the first read... - if(defined($buf) && length($buf) == 0) { - #-- allow some time - select(undef,undef,undef,0.5); - $buf = DevIo_DoSimpleRead($owx_hwdevice); - } - - if(!defined($buf) || length($buf) == 0) { - DevIo_Disconnected($hash); - return undef; - } - return $buf; -} ######################################################################################## # # OWX_Verify_CUNO - Verify a particular device on the 1-Wire bus @@ -2062,16 +2241,22 @@ sub OWX_SimpleRead($) sub OWX_Verify_CUNO ($$) { my ($hash,$dev) = @_; + my $i; - #-- Ask the cuno + #-- get the interface + my $owx_hwdevice = $hash->{HWDEVICE}; + + #-- Ask the CUNO/COC CUL_SimpleWrite($owx_hwdevice, "OCf"); #-- sleeping for some time select(undef,undef,undef,3); CUL_SimpleWrite($owx_hwdevice, "Oc"); - my $ob = OWX_SimpleRead($owx_hwdevice); + select(undef,undef,undef,0.5); + my ($err,$ob) = CUL_ReadAnswer($owx_hwdevice,"",0,undef); 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 @@ -2080,102 +2265,101 @@ sub OWX_Verify_CUNO ($$) { } $ddx .= ".".substr($dx,0,2); return 1 if( $dev eq $ddx); - } - return 0; - } else { - return 0; + } } + return 0; } 1; =pod =begin html - -

OWX

-
    FHEM module to commmunicate with 1-Wire bus devices
      -
    • via an active DS2480/DS2482/DS2490/DS9097U bus master interface attached to an - USB port or
    • -
    • via a passive DS9097 interface attached to an USB port or
    • -
    • via a network-attached CUNO or through a COC on the RaspBerry Pi
    • -
    Internally these interfaces are vastly different, read the corresponding Wiki pages -
    -
    - Example
    -
      - define OWio1 OWX /dev/ttyUSB1 -
      - define OWio2 OWX COC -
      -
    -
    - - Define -
      - define <name> OWX <serial-device> or
      - define <name> OWX <cuno/coc-device> -

      Define a 1-Wire interface to communicate with a 1-Wire bus.
      -
      -
    • - <serial-device> The serial device (e.g. USB port) to which - the 1-Wire bus is attached.
    • -
    • - <cuno-device> The previously defined CUNO to which the 1-Wire - bus is attached.
    • -
    -
    - - Set -
      -
    • - set <name> interval <value> - -

      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.
      • -

      -
    • -
    • - 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.
    • -
    -
    - - Get -
      -
    • - get <name> alarms -

      performs an "alarm search" for devices on the 1-Wire bus and, if - found, generates an event in the log (not with CUNO).
    • -
      -
    • - get <name> devices -

      redicovers all devices on the 1-Wire bus. If a device found has a - previous definition, this is automatically used. If a device is found but has no - definition, it is autocreated. If a defined device is not on the 1-Wire bus, it - is autodeleted.
    • +

      OWX

      +

      FHEM module to commmunicate with 1-Wire bus devices

      +
        +
      • via an active DS2480/DS2482/DS2490/DS9097U bus master interface attached to an USB + port or
      • +
      • via a passive DS9097 interface attached to an USB port or
      • +
      • via a network-attached CUNO or through a COC on the RaspBerry Pi
      • +
      Internally these interfaces are vastly different, read the corresponding Wiki pages

      -
    - - Attributes - -
- +

Example


+

+ define OWio1 OWX /dev/ttyUSB1 +
+ define OWio2 OWX COC +
+

+
+ +

Define

+

+ define <name> OWX <serial-device> or
+ define <name> OWX <cuno/coc-device> +

Define a 1-Wire interface to communicate with a 1-Wire bus.
+
+

+
    +
  • + <serial-device> The serial device (e.g. USB port) to which the + 1-Wire bus is attached.
  • +
  • + <cuno-device> The previously defined CUNO to which the 1-Wire bus + is attached.
  • +
+
+ +

Set

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

    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.
    • +

    +
  • +
  • + 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.
  • +
+
+ +

Get

+
    +
  • + get <name> alarms +

    performs an "alarm search" for devices on the 1-Wire bus and, if found, + generates an event in the log (not with CUNO).
  • +
  • + get <name> devices +

    redicovers all devices on the 1-Wire bus. If a device found has a + previous definition, this is automatically used. If a device is found but has no + definition, it is autocreated. If a defined device is not on the 1-Wire bus, it is + autodeleted.
  • +
+
+ +

Attributes

+ =end html -=cut +=cut \ No newline at end of file