diff --git a/fhem/FHEM/00_OWX_ASYNC.pm b/fhem/FHEM/00_OWX_ASYNC.pm index 5741bf423..6c41393ae 100644 --- a/fhem/FHEM/00_OWX_ASYNC.pm +++ b/fhem/FHEM/00_OWX_ASYNC.pm @@ -69,6 +69,9 @@ BEGIN { }; }; +use ProtoThreads; +no warnings 'deprecated'; + #-- unfortunately some things OS-dependent my $SER_regexp; if( $^O =~ /Win/ ) { @@ -79,7 +82,7 @@ if( $^O =~ /Win/ ) { $SER_regexp= "/dev/"; } -use Time::HiRes qw(gettimeofday); +use Time::HiRes qw( gettimeofday tv_interval ); require "$main::attr{global}{modpath}/FHEM/DevIo.pm"; sub Log3($$$); @@ -125,7 +128,7 @@ my %attrs = ( ); #-- some globals needed for the 1-Wire module -$owx_async_version=5.0; +$owx_async_version=5.1; #-- Debugging 0,1,2,3 $owx_async_debug=0; @@ -184,6 +187,7 @@ sub OWX_ASYNC_Define ($$) { $hash->{ROM_ID} = "FF"; $hash->{DEVS} = []; $hash->{ALARMDEVS} = []; + $hash->{tasks} = {}; my $owx; #-- First step - different methods @@ -759,18 +763,18 @@ sub OWX_ASYNC_Init ($) { my $owx = $hash->{OWX}; if (defined $owx) { - $hash->{INTERFACE} = $owx->{interface}; - my $ret; - #-- Third step: see, if a bus interface is detected - eval { - $ret = $owx->initialize($hash); - }; - if (my $err = GP_Catch($@)) { - $hash->{PRESENT} = 0; - $hash->{STATE} = "Init Failed: $err"; - return "OWX_ASYNC_Init failed: $err"; - }; - $hash->{ASYNC} = $ret; + $hash->{INTERFACE} = $owx->{interface}; + my $ret; + #-- Third step: see, if a bus interface is detected + eval { + $ret = $owx->initialize($hash); + }; + if (my $err = GP_Catch($@)) { + $hash->{PRESENT} = 0; + $hash->{STATE} = "Init Failed: $err"; + return "OWX_ASYNC_Init failed: $err"; + }; + $hash->{ASYNC} = $ret; $hash->{INTERFACE} = $owx->{interface}; } else { return "OWX: Init called with undefined interface"; @@ -791,9 +795,20 @@ sub OWX_ASYNC_Init ($) { #-- Intiate first alarm detection and eventually conversion in a minute or so InternalTimer(gettimeofday() + $hash->{interval}, "OWX_ASYNC_Kick", $hash,0); $hash->{STATE} = "Active"; + GP_ForallClients($hash,\&OWX_ASYNC_InitClient,undef); return undef; } +sub OWX_ASYNC_InitClient { + my ($hash) = @_; + my $name = $hash->{NAME}; + #return undef unless (defined $hash->{InitFn}); + my $ret = CallFn($name,"InitFn",$hash); + if ($ret) { + Log3 $name,2,"error initializing '".$hash->{NAME}."': ".$ret; + } +} + ######################################################################################## # # OWX_ASYNC_Kick - Initiate some processes in all devices @@ -814,12 +829,10 @@ sub OWX_ASYNC_Kick($) { #-- Only if we have the dokick attribute set to 1 if (main::AttrVal($hash->{NAME},"dokick",0)) { - #-- issue the skip ROM command \xCC followed by start conversion command \x44 - $ret = OWX_Execute($hash,"kick",1,undef,"\xCC\x44",0,undef); - if( !$ret ){ - Log3 ($hash->{NAME},3, "OWX: Failure in temperature conversion\n"); - return 0; - } + eval { + OWX_ASYNC_ScheduleMaster( $hash, PT_THREAD(\&OWX_ASYNC_PT_Kick), $hash ); + }; + Log3 $hash->{NAME},3,"OWX_ASYNC_Kick: ".GP_Catch($@) if $@; } if (OWX_ASYNC_Search($hash)) { @@ -829,6 +842,31 @@ sub OWX_ASYNC_Kick($) { return 1; } +sub OWX_ASYNC_PT_Kick($) { + my ($thread,$hash) = @_; + + PT_BEGIN($thread); + Log3 $hash->{NAME},5,"OWX_ASYNC_PT_Kick: kicking DS14B20 temperature conversion"; + #-- issue the skip ROM command \xCC followed by start conversion command \x44 + unless (OWX_ASYNC_Execute($hash,$thread,1,undef,"\x44",0)) { + PT_EXIT("OWX_ASYNC: Failure in temperature conversion"); + } + my ($seconds,$micros) = gettimeofday; + $thread->{execute_delayed} = [$seconds+1,$micros]; + #PT_YIELD_UNTIL(defined $thread->{ExecuteResponse}); + PT_YIELD_UNTIL(tv_interval($thread->{execute_delayed})>=0); + + GP_ForallClients($hash,sub { + my ($client) = @_; + Log3 $client->{NAME},5,"OWX_ASYNC_PT_Kick: doing tempConv for $client->{TYPE}, tempConv: ".main::AttrVal($client->{NAME},"tempConv","-"); + if ($client->{TYPE} eq "OWTHERM" and AttrVal($client->{NAME},"tempConv","") eq "onkick" ) { + OWX_ASYNC_Schedule($client, PT_THREAD(\&OWXTHERM_PT_GetValues), $client ); + } + },undef); + + PT_END; +} + ######################################################################################## # # OWX_ASYNC_Set - Implements SetFn function @@ -940,7 +978,6 @@ sub OWX_ASYNC_Verify ($$) { # ######################################################################################## - sub OWX_Execute($$$$$$$) { my ( $hash, $context, $reset, $owx_dev, $data, $numread, $delay ) = @_; if (my $executor = $hash->{ASYNC}) { @@ -951,6 +988,16 @@ sub OWX_Execute($$$$$$$) { } }; +sub OWX_ASYNC_Execute($$$$$$) { + my ( $hash, $context, $reset, $owx_dev, $data, $numread ) = @_; + if (my $executor = $hash->{ASYNC}) { + delete $context->{ExecuteResponse}; + return $executor->execute( $hash, $context, $reset, $owx_dev, $data, $numread, undef ); + } else { + return 0; + } +}; + ####################################################################################### # # OWX_AwaitExecuteResponse - Wait for the result of a call to OWX_Execute @@ -1019,30 +1066,86 @@ sub OWX_ASYNC_AfterExecute($$$$$$$$) { ", readdata: ".(defined $readdata ? unpack ("H*",$readdata) : "undef")); if (defined $owx_dev) { - foreach my $d ( sort keys %main::defs ) { - if ( my $hash = $main::defs{$d} ) { - if ( defined( $hash->{ROM_ID} ) - && defined( $hash->{IODev} ) - && $hash->{IODev} == $master - && $hash->{ROM_ID} eq $owx_dev ) { - if ($main::modules{$hash->{TYPE}}{AfterExecuteFn}) { - my $ret = CallFn($d,"AfterExecuteFn", $hash, $context, $success, $reset, $owx_dev, $writedata, $numread, $readdata); - Log3 ($master->{NAME},4,"OWX_ASYNC_AfterExecute [".(defined $owx_dev ? $owx_dev : "unknown owx device")."]: $ret") if ($ret); - if ($success) { - readingsSingleUpdate($hash,"PRESENT",1,1) unless ($hash->{PRESENT}); - } else { - readingsSingleUpdate($hash,"PRESENT",0,1) if ($hash->{PRESENT}); - } - } - } - } - } - if (defined $context) { - $master->{replies}{$owx_dev}{$context} = $readdata; - } + if ( defined $context and ref $context eq "ProtoThreads" ) { + $context->{ExecuteResponse} = { + success => $success, + 'reset' => $reset, + writedata => $writedata, + readdata => $readdata, + numread => $numread, + }; + } else { + foreach my $d ( sort keys %main::defs ) { + if ( my $hash = $main::defs{$d} ) { + if ( defined( $hash->{ROM_ID} ) + && defined( $hash->{IODev} ) + && $hash->{IODev} == $master + && $hash->{ROM_ID} eq $owx_dev ) { + if ($main::modules{$hash->{TYPE}}{AfterExecuteFn}) { + my $ret = CallFn($d,"AfterExecuteFn", $hash, $context, $success, $reset, $owx_dev, $writedata, $numread, $readdata); + Log3 ($master->{NAME},4,"OWX_ASYNC_AfterExecute [".(defined $owx_dev ? $owx_dev : "unknown owx device")."]: $ret") if ($ret); + if ($success) { + readingsSingleUpdate($hash,"PRESENT",1,1) unless ($hash->{PRESENT}); + } else { + readingsSingleUpdate($hash,"PRESENT",0,1) if ($hash->{PRESENT}); + } + } + } + } + } + if (defined $context) { + $master->{replies}{$owx_dev}{$context} = $readdata; + } + } } }; +sub OWX_ASYNC_Schedule($$@) { + my ( $hash, $task, @args ) = @_; + my $master = $hash->{IODev}; + die "OWX_ASYNC_Schedule: Master not Active" unless $master->{STATE} eq "Active"; + my $owx_dev = $hash->{ROM_ID}; + $task->{ExecuteArgs} = \@args; + if (defined $master->{tasks}->{$owx_dev}) { + push @{$master->{tasks}->{$owx_dev}}, $task; + } else { + $master->{tasks}->{$owx_dev} = [$task]; + } + main::InternalTimer(gettimeofday(), "OWX_ASYNC_RunTasks", $master,0); +}; + +sub OWX_ASYNC_ScheduleMaster($$@) { + my ( $master, $task, @args ) = @_; + die "OWX_ASYNC_Schedule: Master not Active" unless $master->{STATE} eq "Active"; + $task->{ExecuteArgs} = \@args; + if (defined $master->{tasks}->{master}) { + push @{$master->{tasks}->{master}}, $task; + } else { + $master->{tasks}->{master} = [$task]; + } + main::InternalTimer(gettimeofday(), "OWX_ASYNC_RunTasks", $master,0); +}; + +sub OWX_ASYNC_RunTasks($) { + my ( $master ) = @_; + my ( $owx_dev, $queue ); + if ($master->{STATE} eq "Active") { + while ( ( $owx_dev, $queue ) = each %{$master->{tasks}} ) { + if (@$queue) { + my $task = $queue->[0]; + unless ($task->PT_SCHEDULE(@{$task->{ExecuteArgs}})) { + shift @$queue; + delete $master->{tasks}->{$owx_dev} unless @$queue; + } + OWX_ASYNC_Poll( $master ); + } else { + delete $master->{tasks}->{$owx_dev}; + } + } + main::InternalTimer(gettimeofday(), "OWX_ASYNC_RunTasks", $master,0) if (keys %{$master->{tasks}}); + } +}; + 1; =pod diff --git a/fhem/FHEM/11_OWX_CCC.pm b/fhem/FHEM/11_OWX_CCC.pm new file mode 100644 index 000000000..ba06facea --- /dev/null +++ b/fhem/FHEM/11_OWX_CCC.pm @@ -0,0 +1,512 @@ +######################################################################################## +# +# OWX_CCC.pm +# +# FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX +# +# Prof. Dr. Peter A. Henning +# Norbert Truchsess +# +# $Id: 11_OWX_CCC.pm 2013-03 - pahenning $ +# +######################################################################################## +# +# Provides the following methods for OWX +# +# Alarms +# Complex +# Define +# Discover +# Init +# Reset +# Verify +# +######################################################################################## + +package OWX_CCC; + +use strict; +use warnings; + +######################################################################################## +# +# Constructor +# +######################################################################################## + +sub new($) { + my ($class) = @_; + + return bless { + interface => "COC/CUNO", + #-- module version + version => 4.0 + }, $class; +} + +######################################################################################## +# +# Public methods +# +######################################################################################## +# +# Define - Implements Define method +# +# Parameter def = definition string +# +# Return undef if ok, otherwise error message +# +######################################################################################## + +sub Define($) { + my ($self,$hash,$def) = @_; + + my @a = split("[ \t][ \t]*", $def); + + #-- check syntax + if(int(@a) < 3){ + return "OWX_CCC: Syntax error - must be define OWX " + } + my $dev = $a[2]; + + $hash->{DeviceName} = $dev; + #-- Second step in case of CUNO: See if we can open it + my $msg = "OWX_CCC: COC/CUNO device $dev"; + #-- hash des COC/CUNO + my $hwdevice = $main::defs{$dev}; + if($hwdevice){ + main::Log(1,$msg." defined"); + #-- store with OWX device + $self->{hwdevice} = $hwdevice; + #-- loop for some time until the state is "Initialized" + for(my $i=0;$i<6;$i++){ + last if( $hwdevice->{STATE} eq "Initialized"); + main::Log(1,"OWX_CCC: Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE}); + select(undef,undef,undef,3); + } + main::Log(1, "OWX_CCC: Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized"); + #-- reset the 1-Wire system in COC/CUNO + main::CUL_SimpleWrite($hwdevice, "Oi"); + + $dev = split('@',$dev); + #-- let fhem.pl MAIN call OWX_Ready when setup is done. + $main::readyfnlist{"$hash->{NAME}.$dev"} = $hash; + + return undef; + }else{ + main::Log(1, $msg." not defined"); + return $msg." not defined"; + } +} + +######################################################################################## +# +# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set +# +# Return 0 because not implemented here. +# +######################################################################################## + +sub Alarms () { + my ($self) = @_; + + return 0; +} + +######################################################################################## +# +# Complex - Send match ROM, data block and receive bytes as response +# +# Parameter dev = ROM ID of device +# data = string to send +# numread = number of bytes to receive +# +# Return response, if OK +# 0 if not OK +# +######################################################################################## + +sub Complex ($$$) { + my ($self,$dev,$data,$numread) =@_; + + my $select; + my $res = ""; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + #-- has match ROM part + if( $dev ){ + #-- ID of the device + my $owx_rnf = substr($dev,3,12); + my $owx_f = substr($dev,0,2); + + #-- 8 byte 1-Wire device address + my @rom_id =(0,0,0,0 ,0,0,0,0); + #-- from search string to reverse string id + $dev=~s/\.//g; + for(my $i=0;$i<8;$i++){ + $rom_id[7-$i]=substr($dev,2*$i,2); + } + $select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id); + main::Log(3,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select) + if( $main::owx_debug > 1); + #-- + main::CUL_SimpleWrite($hwdevice, $select); + my ($err,$ob) = ReadAnswer($hwdevice); + #-- padding first 9 bytes into result string, since we have this + # in the serial interfaces as well + $res .= "000000000"; + } + #-- has data part + if ( $data ){ + $self->Send($data); + $res .= $data; + } + #-- has receive part + if( $numread > 0 ){ + #$numread += length($data); + main::Log(3,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes") + if( $main::owx_debug > 1); + $res.=$self->Receive($numread); + } + main::Log(3,"OWX_CCC::Complex: returned from COC/CUNO $res") + if( $main::owx_debug > 1); + return $res; +} + +######################################################################################## +# +# Discover - Discover devices on the 1-Wire bus via internal firmware +# +# Return 0 : error +# 1 : OK +# +######################################################################################## + +sub Discover () { + my ($self) = @_; + + my $res; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + #-- zero the array + my @devs=(); + #-- reset the busmaster + $self->Init(); + #-- get the devices + main::CUL_SimpleWrite($hwdevice, "Oc"); + select(undef,undef,undef,0.5); + my ($err,$ob) = ReadAnswer($hwdevice); + if( $ob ){ + # main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob); + foreach my $dx (split(/\n/,$ob)){ + next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/); + $dx =~ s/\d+\://; + my $ddx = substr($dx,14,2)."."; + #-- reverse data from culfw + for( my $i=1;$i<7;$i++){ + $ddx .= substr($dx,14-2*$i,2); + } + $ddx .= ".".substr($dx,0,2); + push (@devs,$ddx); + } + return \@devs; + } else { + main::Log(1, "OWX_CCC: No answer to ".$hwdevice->{NAME}." device search"); + return 0; + } +} + +######################################################################################## +# +# Init - Initialize the 1-wire device +# +# Return 1 or Errormessage: not OK +# 0 or undef: OK +# +######################################################################################## + +sub Init ($) { + my ($self,$hash) = @_; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + my $get = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm")); + return 1 if( !defined($get) ); + return 1 if( length($get) < 13); + return 1 unless ( substr($get,9,4) eq "OK" ); + + my ($ret,$ress); + my $name = $hash->{NAME}; + my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface "; + $ress = $ress0; + + #-- get the interface + my $interface; + + select(undef,undef,undef,2); + #-- type of interface + main::CUL_SimpleWrite($hwdevice, "V"); + select(undef,undef,undef,0.01); + my ($err,$ob) = ReadAnswer($hwdevice); + #my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V")); + #-- process result for detection + if( !defined($ob)){ + $ob=""; + $ret=1; + #-- COC + }elsif( $ob =~ m/.*CSM.*/){ + $interface="COC"; + $ress .= "DS2482 / COC detected in $hwdevice->{NAME}"; + $ret=0; + #-- CUNO + }elsif( $ob =~ m/.*CUNO.*/){ + $interface="CUNO"; + $ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}"; + $ret=0; + #-- something else + } else { + $ret=1; + } + #-- treat the failure cases + if( $ret == 1 ){ + $interface=undef; + $ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob"; + } + #-- store with OWX device + $self->{interface} = $interface; + main::Log(1, $ress); + return $ret; +} + + +######################################################################################## +# +# Reset - Reset the 1-Wire bus +# +# Return 1 : OK +# 0 : not OK +# +######################################################################################## + +sub Reset () { + my ($self) = @_; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb")); + + if( substr($ob,9,4) eq "OK:1" ){ + return 1; + }else{ + return 0 + } +} + +######################################################################################## +# +# Verify - Verify a particular device on the 1-Wire bus +# +# Parameter dev = 8 Byte ROM ID of device to be tested +# +# Return 1 : device found +# 0 : device not +# +######################################################################################## + +sub Verify ($) { + my ($self,$dev) = @_; + + my $i; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + #-- Ask the COC/CUNO + main::CUL_SimpleWrite($hwdevice, "OCf"); + #-- sleeping for some time + select(undef,undef,undef,3); + main::CUL_SimpleWrite($hwdevice, "Oc"); + select(undef,undef,undef,0.5); + my ($err,$ob) = $self->($hwdevice); + if( $ob ){ + foreach my $dx (split(/\n/,$ob)){ + next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/); + $dx =~ s/\d+\://; + my $ddx = substr($dx,14,2)."."; + #-- reverse data from culfw + for( my $i=1;$i<7;$i++){ + $ddx .= substr($dx,14-2*$i,2); + } + $ddx .= ".".substr($dx,0,2); + return 1 if( $dev eq $ddx); + } + } + return 0; +} + +####################################################################################### +# +# Private methods +# +######################################################################################## +# +# ReadAnswer - Replacement for CUL_ReadAnswer for better control +# +# Parameter: hash = hash of bus master +# +# Return: string received +# +######################################################################################## + +sub ReadAnswer($) +{ + my ($hwdevice) = @_; + + my $type = $hwdevice->{TYPE}; + + my $arg =""; + my $anydata=0; + my $regexp =undef; + + my ($mculdata, $rin) = ("", ''); + my $buf; + my $to = 3; # 3 seconds timeout + $to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less + for(;;) { + return ("Device lost when reading answer for get $arg", undef) + if(!$hwdevice->{FD}); + + vec($rin, $hwdevice->{FD}, 1) = 1; + my $nfound = select($rin, undef, undef, $to); + if($nfound < 0) { + next if ($! == EAGAIN() || $! == EINTR() || $! == 0); + my $err = $!; + main::DevIo_Disconnected($hwdevice); # TODO: DevIO_Disconnected sets hash on readyFnList! -> results in errors later as there's no ReadyFn in OWX + return("ReadAnswer $arg: $err", undef); + } + return ("Timeout reading answer for get $arg", undef) + if($nfound == 0); + $buf = main::DevIo_SimpleRead($hwdevice); + return ("No data", undef) if(!defined($buf)); + + + + if($buf) { + main::Log(5, "OWX_CCC::ReadAnswer $buf"); + $mculdata .= $buf; + } + + # \n\n is socat special + if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) { + if($regexp && $mculdata !~ m/$regexp/) { + main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString}); + } else { + return (undef, $mculdata) + } + } + } +} + +######################################################################################## +# +# Receive - Read data from the 1-Wire bus +# +# Parameter: hash = hash of bus master, numread = number of bytes to read +# +# Return: string received +# +######################################################################################## + +sub Receive ($) { + my ($self,$numread) = @_; + + my $res=""; + my $res2=""; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + for( + my $i=0;$i<$numread;$i++){ + #main::Log(1, "Sending $hwdevice->{NAME}: OrB"; + #my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB")); + main::CUL_SimpleWrite($hwdevice, "OrB"); + select(undef,undef,undef,0.01); + my ($err,$ob) = ReadAnswer($hwdevice); + #main::Log(1, "Answer from $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_CCC::Receive: 18 bytes from CUNO: $ob\n"; + for(my $i=0;$i{NAME}); + } + } + main::Log(3, "OWX_CCC::Receive: $numread bytes = $res2 on bus ".$hwdevice->{NAME}) + if( $main::owx_debug > 1); + + return($res); +} + +######################################################################################### +# +# Send - Send data block +# +# Parameter hash = hash of bus master, data = string to send +# +# Return response, if OK +# 0 if not OK +# +######################################################################################## + +sub Send ($) { + my ($self,$data) =@_; + + my ($i,$j,$k); + my $res = ""; + my $res2 = ""; + + #-- get the interface + my $hwdevice = $self->{hwdevice}; + + for( $i=0;$i 1); +} + +1; \ No newline at end of file diff --git a/fhem/FHEM/11_OWX_DS9097.pm b/fhem/FHEM/11_OWX_DS9097.pm new file mode 100644 index 000000000..8ffe27c22 --- /dev/null +++ b/fhem/FHEM/11_OWX_DS9097.pm @@ -0,0 +1,404 @@ +######################################################################################## +# +# OWX_DS2480.pm +# +# FHEM module providing hardware dependent functions for the DS9097 interface of OWX +# +# Prof. Dr. Peter A. Henning +# Norbert Truchsess +# +# $Id: 11_OWX_SER.pm 2013-03 - pahenning $ +# +######################################################################################## +# +# Provides the following methods for OWX +# +# Alarms +# Complex +# Define +# Discover +# Init +# Reset +# Verify +# +######################################################################################## + +package OWX_DS9097; + +use strict; +use warnings; + +use constant { + QUERY_TIMEOUT => 1.0 +}; + +use vars qw/@ISA/; +@ISA='OWX_SER'; + +use ProtoThreads; +no warnings 'deprecated'; + +sub new($) { + my ($class,$serial) = @_; + + $serial->{pt_reset} = PT_THREAD(\&pt_reset); + $serial->{pt_block} = PT_THREAD(\&pt_block); + $serial->{pt_search} = PT_THREAD(\&pt_search); + + return bless $serial,$class; +} + +######################################################################################## +# +# The following subroutines in alphabetical order are only for a DS9097 bus interface +# +######################################################################################## +# +# Block_9097 - Send data block ( +# +# Parameter hash = hash of bus master, data = string to send +# +# Return response, if OK +# 0 if not OK +# +######################################################################################## + +sub pt_block ($) { + my ($thread,$self,$data) =@_; + PT_BEGIN($thread); + my $data2=""; + my $res=0; + for (my $i=0; $iTouchByte_9097(ord(substr($data,$i,1))); + $data2 = $data2.chr($res); + } + PT_EXIT($data2); + PT_END; +} + +######################################################################################## +# +# Query_9097 - Write to and read from the 1-Wire bus +# +# Parameter: hash = hash of bus master, cmd = string to send to the 1-Wire bus +# +# Return: string received from the 1-Wire bus +# +######################################################################################## + +sub Query_9097 ($) { + + my ($self,$cmd) = @_; + my ($i,$j,$k); + #-- get hardware device + my $hwdevice = $self->{hwdevice}; + + return undef unless (defined $hwdevice); + + $hwdevice->baudrate($self->{baud}); + $hwdevice->write_settings; + + if( $main::owx_debug > 2){ + my $res = "OWX_SER::Query_9097 Sending out "; + for($i=0;$i{name},3, $res); + } + + my $count_out = $hwdevice->write($cmd); + + main::Log3($self->{name},1, "OWX_SER::Query_9097 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) = $hwdevice->read(48); + return undef if (not defined $string_in); + + if( $main::owx_debug > 2){ + my $res = "OWX_SER::Query_9097 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; + } + main::Log3($self->{name},3, $res); + } + + #-- sleeping for some time + select(undef,undef,undef,0.01); + + return($string_in); +} + +######################################################################################## +# +# 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 ReadBit_9097 () { + my ($self) = @_; + + #-- set baud rate to 115200 and query!!! + my $sp1="\xFF"; + $self->{baud}=115200; + my $res=$self->Query_9097($sp1); + return undef if (not defined $res); + $self->{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 pt_reset () { + + my ($thread,$self)=@_; + + #-- Reset command \xF0 + my $cmd="\xF0"; + #-- write 1-Wire bus + PT_BEGIN($thread); + #-- write 1-Wire bus + my $res = $self->Query_9097($cmd); + PT_EXIT if (not defined $res); + #-- TODO: process result + #-- may vary between 0x10, 0x90, 0xe0 + PT_EXIT(1); + PT_END; +} + +######################################################################################## +# +# 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 pt_search ($) { + my ($thread,$self,$mode)=@_; + + PT_BEGIN($thread); + 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 + $self->{baud}=115200; + #TODO: add specific command to search alarmed devices only + $sp2="\x00\x00\x00\x00\xFF\xFF\xFF\xFF"; + $response = $self->Query_9097($sp2); + return undef if (not defined $response); + $self->{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); + + #-- clear 8 byte of device id for current search + @{$self->{ROM_ID}} =(0,0,0,0 ,0,0,0,0); + + while ( $id_bit_number <= 64) { + #loop until through all ROM bytes 0-7 + my $id_bit = $self->TouchBit_9097(1); + my $cmp_id_bit = $self->TouchBit_9097(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"; + next; + } + 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 < $self->{LastDiscrepancy} ){ + if ((@{$self->{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 == $self->{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) { + $self->{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){ + @{$self->{ROM_ID}}[$rom_byte_number] |= $rom_byte_mask; + } else { + @{$self->{ROM_ID}}[$rom_byte_number] &= ~$rom_byte_mask; + } + # serial number search direction write bit + $response = $self->WriteBit_9097($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; + } + $self->{LastDiscrepancy} = $last_zero; + } + PT_EXIT(1); + PT_END; +} + +######################################################################################## +# +# 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 TouchBit_9097 ($) { + my ($self,$bit) = @_; + + my $sp1; + #-- set baud rate to 115200 and query!!! + if( $bit == 1 ){ + $sp1="\xFF"; + } else { + $sp1="\x00"; + } + $self->{baud}=115200; + my $res=$self->Query_9097($sp1); + return undef if (not defined $res); + $self->{baud}=9600; + #-- process result + my $sp2=substr($res,0,1); + if( $sp1 eq $sp2 ){ + return 1; + }else { + return 0; + } +} + +######################################################################################## +# +# TouchByte_9097 - Write/Read 8 bit from 1-wire bus +# +# Parameter hash = hash of bus master +# +# Return bit value +# +######################################################################################## + +sub TouchByte_9097 ($) { + my ($self,$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( $self->ReadBit_9097() ){ + $result |= 0x80; + } + } else { + $self->WriteBit_9097(0); + } + $byte >>= 1; + } + return $result; +} + +######################################################################################## +# +# 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 WriteBit_9097 ($) { + my ($self,$bit) = @_; + + my $sp1; + #-- set baud rate to 115200 and query!!! + if( $bit ==1 ){ + $sp1="\xFF"; + } else { + $sp1="\x00"; + } + $self->{baud}=115200; + my $res=$self->Query_9097($sp1); + return undef if (not defined $res); + $self->{baud}=9600; + #-- process result + if( substr($res,0,1) eq $sp1 ){ + return 1; + } else { + return 0; + } +}; + +# dummy implementation of read - data is actually read within methods above. +sub read() { + return 1; +} + +1; diff --git a/fhem/FHEM/21_OWAD.pm b/fhem/FHEM/21_OWAD.pm index 060d8a200..5a4242a73 100644 --- a/fhem/FHEM/21_OWAD.pm +++ b/fhem/FHEM/21_OWAD.pm @@ -74,9 +74,23 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; +use GPUtils qw(:all); +use Time::HiRes qw( gettimeofday tv_interval usleep ); + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use ProtoThreads; +no warnings 'deprecated'; sub Log($$); -my $owx_version="5.12"; +my $owx_version="5.13"; #-- fixed raw channel name, flexible channel name my @owg_fixed = ("A","B","C","D"); my @owg_channel = ("A","B","C","D"); @@ -174,8 +188,6 @@ sub OWAD_Initialize ($) { $hash->{owg_vlow} = []; $hash->{owg_vhigh} = []; - #-- this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXAD_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -580,8 +592,13 @@ sub OWAD_Get($@) { #-- get reading according to interface type if($a[1] eq "reading") { #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXAD_GetPage($hash,"reading",1,1); + if( $interface eq "OWX" ){ + $ret = OWXAD_GetPage($hash,"reading",1); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXAD_PT_GetPage); + while ($task->PT_SCHEDULE($hash,"reading",1)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSAD_GetPage($hash,"reading",1); @@ -604,8 +621,13 @@ sub OWAD_Get($@) { #-- get alarm values according to interface type if($a[1] eq "alarm") { #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXAD_GetPage($hash,"alarm",1,1); + if( $interface eq "OWX" ){ + $ret = OWXAD_GetPage($hash,"alarm",1); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXAD_PT_GetPage); + while ($task->PT_SCHEDULE($hash,"alarm",1)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSAD_GetPage($hash,"alarm",1); @@ -636,8 +658,13 @@ sub OWAD_Get($@) { #-- get status values according to interface type if($a[1] eq "status") { #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXAD_GetPage($hash,"status",1,1); + if( $interface eq "OWX" ){ + $ret = OWXAD_GetPage($hash,"status",1); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXAD_PT_GetPage); + while ($task->PT_SCHEDULE($hash,"status",1)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSAD_GetPage($hash,"status",1); @@ -721,13 +748,20 @@ sub OWAD_GetValues($) { InternalTimer(time()+$hash->{INTERVAL}, "OWAD_GetValues", $hash, 1); #-- Get readings, alarms and stati according to interface type - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- max 3 tries #for(my $try=0; $try<3; $try++){ $ret1 = OWXAD_GetPage($hash,"reading",0); $ret2 = OWXAD_GetPage($hash,"alarm",0); $ret3 = OWXAD_GetPage($hash,"status",1); - #} + #} + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_GetPage),$hash,"reading",0 ); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_GetPage),$hash,"alarm",0 ); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_GetPage),$hash,"status",1 ); + }; + $ret .= GP_Catch($@) if $@; }elsif( $interface eq "OWServer" ){ $ret1 = OWFSAD_GetPage($hash,"reading",0); $ret2 = OWFSAD_GetPage($hash,"alarm",0); @@ -817,6 +851,12 @@ sub OWAD_InitializeDevice($) { if( $interface eq "OWX" ){ $ret1 = OWXAD_SetPage($hash,"status"); $ret2 = OWXAD_SetPage($hash,"alarm"); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_SetPage),$hash,"status" ); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_SetPage),$hash,"alarm" ); + }; + $ret .= GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret1 = OWFSAD_SetPage($hash,"status"); @@ -932,8 +972,13 @@ sub OWAD_Set($@) { } #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXAD_SetPage($hash,"status"); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_SetPage),$hash,"status" ); + }; + $ret = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSAD_SetPage($hash,"status"); @@ -978,8 +1023,13 @@ sub OWAD_Set($@) { } #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXAD_SetPage($hash,"alarm"); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXAD_PT_SetPage),$hash,"status" ); + }; + $ret = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSAD_SetPage($hash,"alarm"); @@ -1358,22 +1408,13 @@ sub OWXAD_GetPage($$$@) { #=============== get the voltage reading =============================== if( $page eq "reading") { #-- issue the match ROM command \x55 and the start conversion command - #-- asynchronous mode - # difficult here, shoul dbe put into Binvalues as in OWSWITCH - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "getpageconvert", 1, $owx_dev, "\x3C\x0F\x00\xFF\xFF", 0, 20 )) { - return "not accessible for conversion"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - $res= OWX_Complex($master,$owx_dev,"\x3C\x0F\x00\xFF\xFF",0); - if( $res eq 0 ){ - return "not accessible for conversion"; - } - #-- conversion needs some 5 ms per channel - select(undef,undef,undef,0.02); + OWX_Reset($master); + $res= OWX_Complex($master,$owx_dev,"\x3C\x0F\x00\xFF\xFF",0); + if( $res eq 0 ){ + return "not accessible for conversion"; } + #-- conversion needs some 5 ms per channel + select(undef,undef,undef,0.02); #-- issue the match ROM command \x55 and the read conversion page command # \xAA\x00\x00 @@ -1393,25 +1434,16 @@ sub OWXAD_GetPage($$$@) { return "wrong memory page requested from $owx_dev"; } my $context = "ds2450.get".$page.($final ? ".final" : ""); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - #-- reading 9 + 3 + 8 data bytes and 2 CRC bytes = 22 bytes - if (!OWX_Execute( $master, $context, 1, $owx_dev, $select, 10, undef) or ($sync and !OWX_AwaitExecuteResponse($master,$context,$owx_dev))) { - return "$owx_dev not accessible in reading $page page"; - } - #-- synchronous mode - } else { - #-- reset the bus - OWX_Reset($master); - #-- reading 9 + 3 + 8 data bytes and 2 CRC bytes = 22 bytes - $res=OWX_Complex($master,$owx_dev,$select,10); - return "$owx_dev not accessible in reading page $page" - if( $res eq 0 ); - return "$owx_dev has returned invalid data" - if( length($res)!=22); - #-- for processing we also need the 3 command bytes - OWXAD_BinValues($hash,$context,1,undef,$owx_dev,$select,10,substr($res,12,10)); - } + #-- reset the bus + OWX_Reset($master); + #-- reading 9 + 3 + 8 data bytes and 2 CRC bytes = 22 bytes + $res=OWX_Complex($master,$owx_dev,$select,10); + return "$owx_dev not accessible in reading page $page" + if( $res eq 0 ); + return "$owx_dev has returned invalid data" + if( length($res)!=22); + #-- for processing we also need the 3 command bytes + OWXAD_BinValues($hash,$context,1,undef,$owx_dev,$select,10,substr($res,12,10)); return undef; } @@ -1481,24 +1513,163 @@ sub OWXAD_SetPage($$) { } else { return "wrong memory page write attempt"; } - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (OWX_Execute( $master, "setpage", 1, $owx_dev, $select, 0, undef )) { - return undef; - } else { + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + if( $res eq 0 ){ return "device $owx_dev not accessible for writing"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - if( $res eq 0 ){ - return "device $owx_dev not accessible for writing"; - } } return undef; } +######################################################################################## +# +# OWXAD_PT_GetPage - Get one memory page from device +# +# Parameter hash = hash of device addressed +# page = "reading", "alarm" or "status" +# final= 1 if FormatValues is to be called +# +######################################################################################## + +sub OWXAD_PT_GetPage($$$) { + + my ($thread,$hash,$page,$final) = @_; + + my ($select, $res, $res2, $res3, @data, $an, $vn); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + my ($i,$j,$k); + + PT_BEGIN($thread); + + #-- reset presence + $hash->{PRESENT} = 0; + + #=============== get the voltage reading =============================== + if( $page eq "reading") { + #-- issue the match ROM command \x55 and the start conversion command + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x3C\x0F\x00\xFF\xFF", 0 )) { + PT_EXIT("$owx_dev not accessible for conversion"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + #TODO async 20ms delay + select(undef,undef,undef,0.02); + + #-- issue the match ROM command \x55 and the read conversion page command + # \xAA\x00\x00 + $select="\xAA\x00\x00"; + #=============== get the alarm reading =============================== + } elsif ( $page eq "alarm" ) { + #-- issue the match ROM command \x55 and the read alarm page command + # \xAA\x10\x00 + $select="\xAA\x10\x00"; + #=============== get the status reading =============================== + } elsif ( $page eq "status" ) { + #-- issue the match ROM command \x55 and the read status memory page command + # \xAA\x08\x00 r + $select="\xAA\x08\x00"; + #=============== wrong value requested =============================== + } else { + return "wrong memory page requested from $owx_dev"; + } + #-- reading 9 + 3 + 8 data bytes and 2 CRC bytes = 22 bytes + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 10 )) { + PT_EXIT("$owx_dev not accessible in reading $page page"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + my $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev read not successful"); + } + my $res = OWXAD_BinValues($hash,"ds2450.get".$page.($final ? ".final" : ""),1,1,$owx_dev,$response->{writedata},$response->{numread},$response->{readdata}); + if ($res) { + PT_EXIT($res); + } + PT_END; +} + +######################################################################################## +# +# OWXAD_PT_SetPage - Set one page of device +# +# Parameter hash = hash of device addressed +# page = "alarm" or "status" +# +######################################################################################## + +sub OWXAD_PT_SetPage($$) { + + my ($thread,$hash,$page) = @_; + + my ($select, $res, $res2, $res3, @data); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + my ($i,$j,$k); + + PT_BEGIN($thread); + + #=============== set the alarm values =============================== + if ( $page eq "alarm" ) { + #-- issue the match ROM command \x55 and the set alarm page command + # \x55\x10\x00 reading 8 data bytes and 2 CRC bytes + $select="\x55\x10\x00"; + for( $i=0;$i{owg_vlow}->[$i]*256000/$owg_range[$i]); + $select .= sprintf "%c\xFF\xFF\xFF",int($hash->{owg_vhigh}->[$i]*256000/$owg_range[$i]); + } + +#++Use of uninitialized value within @owg_vlow in multiplication at +#++/usr/share/fhem/FHEM/21_OWAD.pm line 1362. + #=============== set the status =============================== + } elsif ( $page eq "status" ) { + my ($sb1,$sb2)=(0,0); + #-- issue the match ROM command \x55 and the set status memory page command + # \x55\x08\x00 reading 8 data bytes and 2 CRC bytes + $select="\x55\x08\x00"; + for( $i=0;$i 0){ + #-- resolution (TODO: check !) + $sb1 = $owg_resoln[$i] & 15; + #-- alarm enabled + if( defined($hash->{owg_slow}->[$i]) ){ + $sb2 = ( $hash->{owg_slow}->[$i] ne 0 ) ? 4 : 0; + } + if( defined($hash->{owg_shigh}->[$i]) ){ + $sb2 += ( $hash->{owg_shigh}->[$i] ne 0 ) ? 8 : 0; + } + #-- range + $sb2 |= 1 + if( $owg_range[$i] > 2560 ); + } else { + $sb1 = 128; + $sb2 = 0; + } + $select .= sprintf "%c\xFF\xFF\xFF",$sb1; + $select .= sprintf "%c\xFF\xFF\xFF",$sb2; + } + #=============== wrong page write attempt =============================== + } else { + PT_EXIT("wrong memory page write attempt"); + } + #"setpage" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + my $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev write not successful"); + } + PT_END; +} + 1; =pod diff --git a/fhem/FHEM/21_OWCOUNT.pm b/fhem/FHEM/21_OWCOUNT.pm index ca00e26d8..d057c4bae 100644 --- a/fhem/FHEM/21_OWCOUNT.pm +++ b/fhem/FHEM/21_OWCOUNT.pm @@ -7,7 +7,7 @@ # Prof. Dr. Peter A. Henning # Norbert Truchsess # -# $Id$ +# $Id: 21_OWCOUNT.pm 2014-04 - pahenning $ # ######################################################################################## # @@ -84,9 +84,22 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use ProtoThreads; +no warnings 'deprecated'; + sub Log3($$$); -my $owx_version="5.17"; +my $owx_version="5.19"; #-- fixed raw channel name, flexible channel name my @owg_fixed = ("A","B"); my @owg_channel = ("A","B"); @@ -154,8 +167,6 @@ sub OWCOUNT_Initialize ($) { } $hash->{AttrList} = $attlist; - #-- ASYNC this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXCOUNT_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -759,7 +770,7 @@ sub OWCOUNT_Get($@) { }elsif( $reading eq "counters" ){ return "OWCOUNT: Get needs no parameter when reading counters" if( int(@a)==1 ); - $ret1 = OWCOUNT_GetPage($hash,14,0); + $ret1 = OWCOUNT_GetPage($hash,14,0,1); $ret2 = OWCOUNT_GetPage($hash,15,1,1); #-- process results @@ -792,16 +803,31 @@ sub OWCOUNT_GetPage ($$$@) { my $interface= $hash->{IODev}->{TYPE}; my $name = $hash->{NAME}; my $ret; + my $oldfinal= $final; #-- check if memory usage has been disabled my $nomemory = defined($attr{$name}{"nomemory"}) ? $attr{$name}{"nomemory"} : 0; + $final=0 + if($nomemory==1); #-- even if memory usage has been disabled, we need to read the page because it contains the counter values if( ($nomemory==0) || ($nomemory==1 && (($page==14)||($page==15))) ){ #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXCOUNT_GetPage($hash,$page,$final,$sync); + if( $interface eq "OWX" ){ + $ret = OWXCOUNT_GetPage($hash,$page,$final); + }elsif( $interface eq "OWX_ASYNC" ){ + if ($sync) { + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXCOUNT_PT_GetPage); + while ($task->PT_SCHEDULE($hash,$page,$final)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); + } else { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXCOUNT_PT_GetPage),$hash,$page,$final ); + }; + $ret = GP_Catch($@) if $@; + } #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSCOUNT_GetPage($hash,$page,$final); @@ -818,26 +844,6 @@ sub OWCOUNT_GetPage ($$$@) { return undef } -sub OWCOUNT_parseMidnight($$$) { - my ($hash,$strval,$page) = @_; - - #-- midnight value - #-- new format - if ( defined $strval and $strval =~ /^\d\d\d\d-\d\d-\d\d.*/ ) { - my @data=split(' ',$strval); - $strval = $data[2]; - } - if ( defined $strval ) { - #-- parse float from midnight - $strval =~ s/[^\d\.]+//g; - $strval = 0.0 if($strval !~ /^\d+\.\d*$/); - $strval = int($strval*100)/100; - } else { - $strval = 0.0; - } - $hash->{owg_midnight}->[$page-14] = $strval; -} - ######################################################################################## # # OWCOUNT_GetMonth Read monthly data from a file @@ -1111,13 +1117,17 @@ sub OWCOUNT_InitializeDevice($) { # the same family ID although the DS2423emu does not fully support the DS2423 commands. # Model attribute will be modified now after checking for memory #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXCOUNT_GetPage($hash,14,0,1); + if( $interface eq "OWX" ){ + $ret = OWXCOUNT_GetPage($hash,14,0); $olddata = $hash->{owg_str}->[14]; $ret = OWXCOUNT_SetPage($hash,14,$newdata); - $ret = OWXCOUNT_GetPage($hash,14,0,1); + $ret = OWXCOUNT_GetPage($hash,14,0); $ret = OWXCOUNT_SetPage($hash,14,$olddata); - + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXCOUNT_PT_InitializeDevicePage); + while ($task->PT_SCHEDULE($hash,14,$newdata)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSCOUNT_GetPage($hash,14,0); @@ -1132,13 +1142,17 @@ sub OWCOUNT_InitializeDevice($) { #Log 1,"FIRST CHECK: written $newdata, read ".substr($hash->{owg_str}->[14],0,length($newdata)); my $nomid = ( substr($hash->{owg_str}->[14],0,length($newdata)) ne $newdata ); #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXCOUNT_GetPage($hash,0,0,1); + if( $interface eq "OWX" ){ + $ret = OWXCOUNT_GetPage($hash,0,0); $olddata = $hash->{owg_str}->[0]; $ret = OWXCOUNT_SetPage($hash,0,$newdata); - $ret = OWXCOUNT_GetPage($hash,0,0,1); + $ret = OWXCOUNT_GetPage($hash,0,0); $ret = OWXCOUNT_SetPage($hash,0,$olddata); - + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXCOUNT_PT_InitializeDevicePage); + while ($task->PT_SCHEDULE($hash,0,$newdata)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSCOUNT_GetPage($hash,0,0); @@ -1169,6 +1183,36 @@ sub OWCOUNT_InitializeDevice($) { return undef; } +####################################################################################### +# +# OWCOUNT_ParseMidnight - Read the stored midnight value +# +# Parameter hash = hash of device addressed +# strval = data string +# page = page number +# +######################################################################################## + +sub OWCOUNT_ParseMidnight($$$) { + my ($hash,$strval,$page) = @_; + + #-- midnight value + #-- new format + if ( defined $strval and $strval =~ /^\d\d\d\d-\d\d-\d\d.*/ ) { + my @data=split(' ',$strval); + $strval = $data[2]; + } + if ( defined $strval ) { + #-- parse float from midnight + $strval =~ s/[^\d\.]+//g; + $strval = 0.0 if($strval !~ /^\d+\.\d*$/); + $strval = int($strval*100)/100; + } else { + $strval = 0.0; + } + $hash->{owg_midnight}->[$page-14] = $strval; +} + ####################################################################################### # # OWCOUNT_Set - Set one value for device @@ -1321,8 +1365,13 @@ sub OWCOUNT_SetPage ($$$) { if( $nomemory==0 ){ #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXCOUNT_SetPage($hash,$page,$data); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXCOUNT_PT_SetPage),$hash,$page,$data ); + }; + $ret = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSCOUNT_SetPage($hash,$page,$data); @@ -1463,7 +1512,7 @@ sub OWFSCOUNT_GetPage($$$) { $hash->{owg_val}->[$page-14] = $vval; $hash->{owg_str}->[$page] = defined $strval ? $strval : ""; #-- midnight value - OWCOUNT_parseMidnight($hash,$strval,$page); + OWCOUNT_ParseMidnight($hash,$strval,$page); }else { $strval = OWServer_Read($master,"/$owx_add/pages/page.".$page); return "no return from OWServer" @@ -1505,7 +1554,7 @@ sub OWFSCOUNT_SetPage($$$) { } #=============== midnight value ===================================== if( ($page==14) || ($page==15) ){ - OWCOUNT_parseMidnight($hash,$data,$page); + OWCOUNT_ParseMidnight($hash,$data,$page); } OWServer_Write($master, "/$owx_add/pages/page.".$page,$data ); return undef @@ -1584,8 +1633,8 @@ sub OWXCOUNT_BinValues($$$$$$$$) { $value = (ord($data[3])<<24) + (ord($data[2])<<16) +(ord($data[1])<<8) + ord($data[0]); $hash->{owg_val}->[$page-14] = $value; #-- midnight value - Log3 $name,5, "OWCOUNT_BinValues parseMidnight: ".(defined $strval ? $strval : "undef"); - OWCOUNT_parseMidnight($hash,$strval,$page); + Log3 $name,5, "OWCOUNT_BinValues ParseMidnight: ".(defined $strval ? $strval : "undef"); + OWCOUNT_ParseMidnight($hash,$strval,$page); } #-- and now from raw to formatted values $hash->{PRESENT} = 1; @@ -1593,34 +1642,6 @@ sub OWXCOUNT_BinValues($$$$$$$$) { my $value = OWCOUNT_FormatValues($hash); Log3 $name,5, "OWCOUNT_BinValues->FormatValues returns: ".(defined $value ? $value : "undef"); } - } elsif ($cmd eq "set") { - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if ($page == 1) { - #-- issue the match ROM command \x55 and the read scratchpad command - # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content - $select = "\xAA"; - if (OWX_Execute( $hash->{IODev}, "setpage.2", 1, $owx_dev, $select, 28, undef )) { - return undef; - } else { - Log3 $name,3,"OWCOUNT: device $owx_dev not accessible in reading scratchpad"; - return "device $owx_dev not accessible in reading scratchpad"; - } - } elsif ($page == 2) { - #-- issue the match ROM command \x55 and the copy scratchpad command - # \x5A followed by 3 byte authentication code obtained in previous read - $select="\x5A".substr($res,0,3); - if (OWX_Execute( $hash->{IODev}, "setpage.3", 1, $owx_dev, $select, 6, undef )) { - return undef; - } else { - Log3 $name,3,"OWCOUNT: device $owx_dev not accessible for copying scratchpad"; - return "device $owx_dev not accessible for copying scratchpad"; - } - } elsif ($page == 3 and $res eq 0) { - Log3 $name,3,"OWCOUNT: device $owx_dev not accessible for copying scratchpad, return: $res"; - return "device $owx_dev not accessible for copying scratchpad, return: $res"; - } - } } return undef; } @@ -1635,8 +1656,8 @@ sub OWXCOUNT_BinValues($$$$$$$$) { # ######################################################################################## -sub OWXCOUNT_GetPage($$$@) { - my ($hash,$page,$final,$sync) = @_; +sub OWXCOUNT_GetPage($$$) { + my ($hash,$page,$final) = @_; my ($select, $res, $res2, $res3, @data); @@ -1661,42 +1682,33 @@ sub OWXCOUNT_GetPage($$$@) { $select=sprintf("\xA5%c%c",$ta1,$ta2); my $context = "getpage.".$page.($final ? ".final" : ""); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, $context, 1, $owx_dev, $select, 42, 20 ) or ($sync and !OWX_AwaitExecuteResponse($master,$context,$owx_dev))) { - return "not accessible for reading"; - } - return undef; - #-- synchronous mode - } else { - #-- reset the bus - OWX_Reset($master); - #-- reading 9 + 3 + 40 data bytes (32 byte memory, 4 byte counter + 4 byte zeroes) and 2 CRC bytes = 54 bytes - $res=OWX_Complex($master,$owx_dev,$select,42); - if( $res eq 0 ){ - return "device $owx_dev not accessible in reading page $page"; - } + #-- reset the bus + OWX_Reset($master); + #-- reading 9 + 3 + 40 data bytes (32 byte memory, 4 byte counter + 4 byte zeroes) and 2 CRC bytes = 54 bytes + $res=OWX_Complex($master,$owx_dev,$select,42); + if( $res eq 0 ){ + return "device $owx_dev not accessible in reading page $page"; + } + #-- process results + if( length($res) < 54 ) { + #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in first step"; + #-- read the data in a second step + $res.=OWX_Complex($master,"","",0); #-- process results if( length($res) < 54 ) { - #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in first step"; - #-- read the data in a second step + #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in second step"; + #-- read the data in a third step $res.=OWX_Complex($master,"","",0); - #-- process results - if( length($res) < 54 ) { - #Log 1, "OWXCOUNT: warning, have received ".length($res)." bytes in second step"; - #-- read the data in a third step - $res.=OWX_Complex($master,"","",0); - } - } - #-- reset the bus (needed to stop receiving data ?) - OWX_Reset($master); - #-- for processing we need 45 bytes - return "$owx_dev not accessible in reading" - if( $res eq 0 ); - return "$owx_dev has returned invalid data" - if( length($res)!=54); - return OWXCOUNT_BinValues($hash,$context,1,1,$owx_dev,$select,42,substr($res,12)); - } + } + } + #-- reset the bus (needed to stop receiving data ?) + OWX_Reset($master); + #-- for processing we need 45 bytes + return "$owx_dev not accessible in reading" + if( $res eq 0 ); + return "$owx_dev has returned invalid data" + if( length($res)!=54); + return OWXCOUNT_BinValues($hash,$context,1,1,$owx_dev,$select,42,substr($res,12)); return undef; } @@ -1727,7 +1739,7 @@ sub OWXCOUNT_SetPage($$$) { } #=============== midnight value ===================================== if( ($page==14) || ($page==15) ){ - OWCOUNT_parseMidnight($hash,$data,$page); + OWCOUNT_ParseMidnight($hash,$data,$page); } #=============== set memory ========================================= #-- issue the match ROM command \x55 and the write scratchpad command @@ -1746,66 +1758,269 @@ sub OWXCOUNT_SetPage($$$) { #} #main::Log(1, $res2); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - #the followup-call to OWX_Execute (setpage.3) is located in OWX_Binvalues! - if (!OWX_Execute( $master, "setpage.1", 1, $owx_dev, $select, 0, undef ) or (!OWX_AwaitExecuteResponse($master,"setpage.3",$owx_dev))) { - return "device $owx_dev not accessible in writing scratchpad"; - } - #-- synchronous mode - } else { - #-- reset the bus - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - if( $res eq 0 ){ - return "device $owx_dev not accessible in writing scratchpad"; - } - - #-- issue the match ROM command \x55 and the read scratchpad command - # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content - $select = "\xAA"; - #-- reset the bus - OWX_Reset($master); - #-- reading 9 + 3 + up to 32 bytes - # TODO: sometimes much less than 28 - $res=OWX_Complex($master,$owx_dev,$select,28); - if( length($res) < 13 ){ - return "device $owx_dev not accessible in reading scratchpad"; - } - - #-- first 1 command, next 2 are address, then data - #$res3 = substr($res,9,10); - #$res2 = "OWCOUNT SET PAGE 2 device $owx_dev "; - #for($i=0;$i<10;$i++){ - # $j=int(ord(substr($res3,$i,1))/16); - # $k=ord(substr($res3,$i,1))%16; - # $res2.=sprintf "0x%1x%1x ",$j,$k; - #} - #main::Log(1, $res2); - #-- issue the match ROM command \x55 and the copy scratchpad command - # \x5A followed by 3 byte authentication code obtained in previous read - $select="\x5A".substr($res,10,3); - #-- first command, next 2 are address, then data - #$res2 = "OWCOUNT SET PAGE 3 device $owx_dev "; - #for($i=0;$i<10;$i++){ - # $j=int(ord(substr($select,$i,1))/16); - # $k=ord(substr($select,$i,1))%16; - # $res2.=sprintf "0x%1x%1x ",$j,$k; - #} - #main::Log(1, $res2); - - #-- reset the bus - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,6); - - #-- process results - if( $res eq 0 ){ - return "device $owx_dev not accessible for copying scratchpad"; - } + #-- reset the bus + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + if( $res eq 0 ){ + return "device $owx_dev not accessible in writing scratchpad"; } + + #-- issue the match ROM command \x55 and the read scratchpad command + # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content + $select = "\xAA"; + #-- reset the bus + OWX_Reset($master); + #-- reading 9 + 3 + up to 32 bytes + # TODO: sometimes much less than 28 + $res=OWX_Complex($master,$owx_dev,$select,28); + if( length($res) < 13 ){ + return "device $owx_dev not accessible in reading scratchpad"; + } + + #-- first 1 command, next 2 are address, then data + #$res3 = substr($res,9,10); + #$res2 = "OWCOUNT SET PAGE 2 device $owx_dev "; + #for($i=0;$i<10;$i++){ + # $j=int(ord(substr($res3,$i,1))/16); + # $k=ord(substr($res3,$i,1))%16; + # $res2.=sprintf "0x%1x%1x ",$j,$k; + #} + #main::Log(1, $res2); + #-- issue the match ROM command \x55 and the copy scratchpad command + # \x5A followed by 3 byte authentication code obtained in previous read + $select="\x5A".substr($res,10,3); + #-- first command, next 2 are address, then data + #$res2 = "OWCOUNT SET PAGE 3 device $owx_dev "; + #for($i=0;$i<10;$i++){ + # $j=int(ord(substr($select,$i,1))/16); + # $k=ord(substr($select,$i,1))%16; + # $res2.=sprintf "0x%1x%1x ",$j,$k; + #} + #main::Log(1, $res2); + + #-- reset the bus + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,6); + + #-- process results + if( $res eq 0 ){ + return "device $owx_dev not accessible for copying scratchpad"; + } return undef; } +######################################################################################## +# +# OWXCOUNT_PT_GetPage - Get one memory page + counter from device async +# +# Parameter hash = hash of device addressed +# page = 0..15 +# final= 1 if FormatValues is to be called +# +######################################################################################## + +sub OWXCOUNT_PT_GetPage($$$) { + my ($thread,$hash,$page,$final) = @_; + + my ($select, $res, $response); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + #-- reset presence + $hash->{PRESENT} = 0; + + #=============== wrong value requested =============================== + if( ($page<0) || ($page>15) ){ + PT_EXIT("wrong memory page requested"); + } + #=============== get memory + counter =============================== + #-- issue the match ROM command \x55 and the read memory + counter command + # \xA5 TA1 TA2 reading 40 data bytes and 2 CRC bytes + my $ta2 = ($page*32) >> 8; + my $ta1 = ($page*32) & 255; + $select=sprintf("\xA5%c%c",$ta1,$ta2); + + #-- reading 9 + 3 + 40 data bytes (32 byte memory, 4 byte counter + 4 byte zeroes) and 2 CRC bytes = 54 bytes + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 42 )) { + PT_EXIT("device $owx_dev not accessible for reading page $page"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + + #-- reset the bus (needed to stop receiving data ?) + OWX_ASYNC_Execute( $master, $thread, 1, undef, undef, undef ); + + unless ($response->{success}) { + PT_EXIT("device $owx_dev error reading page $page"); + } + $res = $response->{readdata}; + + #TODO validate whether testing '0' is appropriate with async interface + if( $res eq 0 ) { + PT_EXIT("device $owx_dev error reading page $page"); + } + $res = OWXCOUNT_BinValues($hash,"getpage.".$page.($final ? ".final" : ""),1,1,$owx_dev,$response->{writedata},$response->{numread},$res); + if ($res) { + PT_EXIT($res); + } + PT_END; +} + +######################################################################################## +# +# OWXCOUNT_PT_SetPage - Set one memory page of device async +# +# Parameter hash = hash of device addressed +# page = "alarm" or "status" +# +######################################################################################## + +sub OWXCOUNT_PT_SetPage($$$) { + + my ($thread,$hash,$page,$data) = @_; + + my ($select, $res, $response); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + #=============== wrong page requested =============================== + if( ($page<0) || ($page>15) ){ + PT_EXIT("wrong memory page write attempt"); + } + #=============== midnight value ===================================== + if( ($page==14) || ($page==15) ){ + OWCOUNT_ParseMidnight($hash,$data,$page); + } + #=============== set memory ========================================= + #-- issue the match ROM command \x55 and the write scratchpad command + # \x0F TA1 TA2 followed by the data + my $ta2 = ($page*32) >> 8; + my $ta1 = ($page*32) & 255; + #Log 1, "OWXCOUNT: setting page Nr. $ta2 $ta1 $data"; + $select=sprintf("\x0F%c%c",$ta1,$ta2).$data; + + #-- first command, next 2 are address, then data + #$res2 = "OWCOUNT SET PAGE 1 device $owx_dev "; + #for($i=0;$i<10;$i++){ + # $j=int(ord(substr($select,$i,1))/16); + # $k=ord(substr($select,$i,1))%16; + # $res2.=sprintf "0x%1x%1x ",$j,$k; + #} + #main::Log(1, $res2); + + #"setpage.1" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0)) { + PT_EXIT("device $owx_dev not accessible in writing scratchpad"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("device $owx_dev error writing scratchpad"); + } + + #-- issue the match ROM command \x55 and the read scratchpad command + # \xAA, receiving 2 address bytes, 1 status byte and scratchpad content + $select = "\xAA"; + #-- reading 9 + 3 + up to 32 bytes + # TODO: sometimes much less than 28 + #"setpage.2" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 28)) { + PT_EXIT("device $owx_dev not accessible in writing scratchpad"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("device $owx_dev error writing scratchpad"); + } + $res = $response->{readdata}; + if( length($res) < 13 ){ + PT_EXIT("device $owx_dev not accessible in reading scratchpad"); + } + + #-- first 1 command, next 2 are address, then data + #$res3 = substr($res,9,10); + #$res2 = "OWCOUNT SET PAGE 2 device $owx_dev "; + #for($i=0;$i<10;$i++){ + # $j=int(ord(substr($res3,$i,1))/16); + # $k=ord(substr($res3,$i,1))%16; + # $res2.=sprintf "0x%1x%1x ",$j,$k; + #} + #main::Log(1, $res2); + #-- issue the match ROM command \x55 and the copy scratchpad command + # \x5A followed by 3 byte authentication code obtained in previous read + $select="\x5A".substr($res,0,3); + #-- first command, next 2 are address, then data + #$res2 = "OWCOUNT SET PAGE 3 device $owx_dev "; + #for($i=0;$i<10;$i++){ + # $j=int(ord(substr($select,$i,1))/16); + # $k=ord(substr($select,$i,1))%16; + # $res2.=sprintf "0x%1x%1x ",$j,$k; + #} + #main::Log(1, $res2); + + #"setpage.3" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 6)) { + PT_EXIT("device $owx_dev not accessible for copying scratchpad"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("device $owx_dev error copying scratchpad"); + } + $res = $response->{readdata}; + #TODO validate whether testing '0' is appropriate with async interface + #-- process results + if( $res eq 0 ){ + PT_EXIT("device $owx_dev error copying scratchpad"); + } + PT_END; +} + +sub OWXCOUNT_PT_InitializeDevicePage($$$) { + my ($thread,$hash,$page,$newdata) = @_; + + my $ret; + + PT_BEGIN($thread); + + $thread->{task} = PT_THREAD(\&OWXCOUNT_PT_GetPage); + PT_WAIT_THREAD($thread->{task},$hash,$page,0); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + + $thread->{olddata} = $hash->{owg_str}->[14]; + + $thread->{task} = PT_THREAD(\&OWXCOUNT_PT_SetPage); + PT_WAIT_THREAD($thread->{task},$hash,$page,$newdata); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + + $thread->{task} = PT_THREAD(\&OWXCOUNT_PT_GetPage); + PT_WAIT_THREAD($thread->{task},$hash,$page,0); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + + $thread->{task} = PT_THREAD(\&OWXCOUNT_PT_SetPage); + PT_WAIT_THREAD($thread->{task},$hash,$page,$thread->{olddata}); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + PT_END; +} + 1; =pod diff --git a/fhem/FHEM/21_OWLCD.pm b/fhem/FHEM/21_OWLCD.pm index 67149638c..4377d2838 100644 --- a/fhem/FHEM/21_OWLCD.pm +++ b/fhem/FHEM/21_OWLCD.pm @@ -60,9 +60,23 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use GPUtils qw(:all); +use ProtoThreads; +no warnings 'deprecated'; + sub Log3($$$); -my $owx_version="3.35"; +my $owx_version="3.36"; #-- controller may be HD44780 or KS0073 # these values have to be changed for different display # geometries or memory maps @@ -118,13 +132,12 @@ sub OWLCD_Initialize ($) { $hash->{UndefFn} = "OWLCD_Undef"; $hash->{GetFn} = "OWLCD_Get"; $hash->{SetFn} = "OWLCD_Set"; + $hash->{InitFn} = "OWLCD_Init"; $hash->{AttrFn} = "OWLCD_Attr"; my $attlist = "IODev do_not_notify:0,1 showtime:0,1 ". ""; $hash->{AttrList} = $attlist; - #-- this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXLCD_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -183,12 +196,20 @@ sub OWLCD_Define ($$) { $modules{OWLCD}{defptr}{$id} = $hash; $hash->{STATE} = "Defined"; - Log3 $name,3, "OWLCD: Device $name defined."; + Log3 $name,3, "OWLCD: Device $name defined."; + + if (($hash->{IODev}->{TYPE} eq "OWX") or $main::init_done) { + return OWLCD_Init($hash); + } + return undef; +} +sub OWLCD_Init($) { + my ($hash) = @_; #-- Initialization reading according to interface type my $interface= $hash->{IODev}->{TYPE}; #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ OWXLCD_InitializeDevice($hash); #-- set backlight on OWXLCD_SetFunction($hash,"bklon",0); @@ -196,6 +217,17 @@ sub OWLCD_Define ($$) { OWXLCD_SetIcon($hash,0,0); #-- erase alarm state OWXLCD_SetFunction($hash,"gpio",15); + } elsif ( $interface eq "OWX_ASYNC" ) { + eval { + OWXLCD_InitializeDevice($hash); + #-- set backlight on + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_SetFunction),$hash,"bklon",0); + #-- erase all icons + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_SetIcon),$hash,0,0); + #-- erase alarm state + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_SetFunction),$hash,"gpio",15); + }; + return GP_Catch($@) if $@; #-- Unknown interface }else{ return "OWLCD: Wrong IODev type $interface"; @@ -286,29 +318,61 @@ sub OWLCD_Get($@) { #-- get gpio states if($a[1] eq "gpio") { - $value = OWXLCD_Get($hash,"gpio"); - return "$name.gpio => $value"; + if ($hash->{ASYNC}) { + my $task = PT_THREAD(\&OWXLCD_PT_Get); + while ($task->PT_SCHEDULE($hash,"gpio")) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); + return $ret if $ret; + return "$name.gpio => ".main::ReadingsVal($hash->{NAME},"gpio",""); + } else { + $value = OWXLCD_Get($hash,"gpio"); + return "$name.gpio => $value"; + } } #-- get gpio counters if($a[1] eq "counter") { - $value = OWXLCD_Get($hash,"counter"); - return "$name.counter => $value"; + if ($hash->{ASYNC}) { + my $task = PT_THREAD(\&OWXLCD_PT_Get); + while ($task->PT_SCHEDULE($hash,"counter")) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); + return $ret if $ret; + return "$name.counter => ".main::ReadingsVal($hash->{NAME},"counter",""); + } else { + $value = OWXLCD_Get($hash,"counter"); + return "$name.counter => $value"; + } } #-- get version if($a[1] eq "version") { - $value = OWXLCD_Get($hash,"version"); - return "$name.version => $owx_version (LCD firmware $value)"; + if ($hash->{ASYNC}) { + my $task = PT_THREAD(\&OWXLCD_PT_Get); + while ($task->PT_SCHEDULE($hash,"version")) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); + return $ret if $ret; + return "$name.gpio => ".main::ReadingsVal($hash->{NAME},"version",""); + } else { + $value = OWXLCD_Get($hash,"version"); + return "$name.version => $owx_version (LCD firmware $value)"; + } } #-- get EEPROM content if($a[1] eq "memory") { - my $page = (defined $a[2] and $a[2] =~ m/\d/) ? int($a[2]) : 0; - Log3 $name,1,"Calling GetMemory with page $page"; - $value = OWXLCD_GetMemory($hash,$page); - return "$name $reading $page => $value"; - } + my $page = (defined $a[2] and $a[2] =~ m/\d/) ? int($a[2]) : 0; + Log3 $name,1,"Calling GetMemory with page $page"; + if ($hash->{ASYNC}) { + my $task = PT_THREAD(\&OWXLCD_PT_GetMemory); + while ($task->PT_SCHEDULE($hash,$page)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); + return $ret if $ret; + return "$name $reading $page => ".main::ReadingsVal($hash->{NAME},"memory$page",""); + } else { + $value = OWXLCD_GetMemory($hash,$page); + return "$name $reading $page => $value"; + } + } } ####################################################################################### @@ -394,7 +458,14 @@ sub OWLCD_Set($@) { #-- check value and write to device return "OWLCD: Set with wrong value for gpio port, must be 0 <= gpio <= 7" if( ! ((int($value) >= 0) && (int($value) <= 7)) ); - OWXLCD_SetFunction($hash, "gpio", int($value)); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", int($value) ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash, "gpio", int($value)); + } return undef; } @@ -402,9 +473,23 @@ sub OWLCD_Set($@) { if($key eq "lcd") { #-- check value and write to device if( uc($value) eq "ON"){ - OWXLCD_SetFunction($hash, "lcdon", 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "lcdon", 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash, "lcdon", 0); + } }elsif( uc($value) eq "OFF" ){ - OWXLCD_SetFunction($hash, "lcdoff", 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "lcdoff", 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash, "lcdoff", 0); + } } else { return "OWLCD: Set with wrong value for lcd, must be on/off" } @@ -415,9 +500,23 @@ sub OWLCD_Set($@) { if($key eq "backlight") { #-- check value and write to device if( uc($value) eq "ON"){ - OWXLCD_SetFunction($hash, "bklon", 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "blkon", 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash, "bklon", 0); + } }elsif( uc($value) eq "OFF" ){ - OWXLCD_SetFunction($hash, "bkloff", 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "blkoff", 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash, "bkloff", 0); + } } else { return "OWLCD: Set with wrong value for backlight, must be on/off" } @@ -426,9 +525,18 @@ sub OWLCD_Set($@) { #-- reset if($key eq "reset") { - OWXLCD_SetFunction($hash,"reset",0); - OWXLCD_SetIcon($hash,0,0); - OWXLCD_SetFunction($hash,"gpio",15); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "reset", 0 ); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, 0, 0 ); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", 15 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash,"reset",0); + OWXLCD_SetIcon($hash,0,0); + OWXLCD_SetFunction($hash,"gpio",15); + } return undef; } @@ -439,21 +547,63 @@ sub OWLCD_Set($@) { #-- check value and write to device if( $icon == 16 ){ if( uc($value) eq "OFF" ){ - OWXLCD_SetIcon($hash, 16, 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, 16, 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, 16, 0); + } }elsif( uc($value) eq "BLINK" ){ - OWXLCD_SetIcon($hash, 16, 6); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, 16, 6 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, 16, 6); + } }elsif( ((int($value) > 0) && (int($value) < 6)) ){ - OWXLCD_SetIcon($hash, 16, int($value)); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, 16, int($value) ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, 16, int($value)); + } } else { return "OWLCD: Set with wrong value for icon #16, must be 0..5/off/blink" } }else{ if( uc($value) eq "OFF"){ - OWXLCD_SetIcon($hash, $icon, 0); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, $icon, 0 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, $icon, 0); + } }elsif( uc($value) eq "ON" ){ - OWXLCD_SetIcon($hash, $icon, 1); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, $icon, 1 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, $icon, 1); + } }elsif( uc($value) eq "BLINK" ){ - OWXLCD_SetIcon($hash, $icon, 2); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetIcon), $hash, $icon, 2 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetIcon($hash, $icon, 2); + } } else { return "OWLCD: Set with wrong value for icon $icon, must be on/off/blink" } @@ -469,7 +619,14 @@ sub OWLCD_Set($@) { return "OWLCD: Wrong line length, must be <= ".$lcdchars if( length($value) > $lcdchars ); #-- check value and write to device - OWXLCD_SetLine($hash,$line,$value); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetLine), $hash, $line, $value ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetLine($hash,$line,$value); + } return undef; } @@ -480,24 +637,59 @@ sub OWLCD_Set($@) { return "OWLCD: Wrong line length, must be <=16 " if( length($value) > 16 ); #-- check value and write to device - Log3 $name,1,"Calling SetMemory with page $line"; - OWXLCD_SetMemory($hash,$line,$value); - return undef; + Log3 $name,1,"Calling SetMemory with page $line"; + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetMemory), $hash, $line, $value ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetMemory($hash,$line,$value); + } + return undef; } #-- set alert if($key eq "alert") { if(lc($value) eq "beep") { - OWXLCD_SetFunction($hash,"gpio",14); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", 14 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash,"gpio",14); + } return undef; }elsif(lc($value) eq "red") { - OWXLCD_SetFunction($hash,"gpio",13); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", 13 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash,"gpio",13); + } return undef; }elsif(lc($value) eq "yellow") { - OWXLCD_SetFunction($hash,"gpio",11); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", 11 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash,"gpio",11); + } return undef; }elsif( (lc($value) eq "off") || (lc($value) eq "none") ) { - OWXLCD_SetFunction($hash,"gpio",15); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetFunction), $hash, "gpio", 15 ); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetFunction($hash,"gpio",15); + } return undef; }else{ return "OWLCD: Set with wrong value for alert type, must be beep/red/yellow/off"; @@ -506,10 +698,20 @@ sub OWLCD_Set($@) { #-- start test if($key eq "test") { - OWXLCD_SetLine($hash,0,"Hallo Welt"); - OWXLCD_SetLine($hash,1,"Mary had a big lamb"); - OWXLCD_SetLine($hash,2,"Solar 4.322 kW "); - OWXLCD_SetLine($hash,3,"\x5B\x5C\x5E\x7B\x7C\x7E\xBE"); + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetLine), $hash,0,"Hallo Welt"); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetLine), $hash,1,"Mary had a big lamb"); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetLine), $hash,2,"Solar 4.322 kW "); + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXLCD_PT_SetLine), $hash,3,"\x5B\x5C\x5E\x7B\x7C\x7E\xBE"); + }; + return GP_Catch($@) if $@; + } else { + OWXLCD_SetLine($hash,0,"Hallo Welt"); + OWXLCD_SetLine($hash,1,"Mary had a big lamb"); + OWXLCD_SetLine($hash,2,"Solar 4.322 kW "); + OWXLCD_SetLine($hash,3,"\x5B\x5C\x5E\x7B\x7C\x7E\xBE"); + } return undef; } } @@ -568,25 +770,67 @@ sub OWXLCD_Byte($$$) { return "OWXLCD: Wrong byte write attempt"; } - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "byte", 1, $owx_dev, $select, 0, 0 )) { - return "OWLCD: Device $owx_dev not accessible for writing a byte"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for writing a byte"; - } - } - + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for writing a byte"; + } + return undef; } +######################################################################################## +# +# OWXLCD_PT_Byte - write a single byte to the LCD device async +# +# Parameter hash = hash of device addressed +# cmd = register or data +# byte = byte +# +######################################################################################## + +sub OWXLCD_PT_Byte($$$) { + + my ($thread,$hash,$cmd,$byte) = @_; + + my ($select); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + + my ($i,$j,$k); + + PT_BEGIN($thread); + + #=============== write to LCD register =============================== + if ( $cmd eq "register" ) { + #-- issue the read LCD register command \x10 + $select = sprintf("\x10%c",$byte); + #=============== write to LCD data =============================== + }elsif ( $cmd eq "data" ) { + #-- issue the read LCD data command \x12 + $select = sprintf("\x12%c",$byte); + #=============== wrong value requested =============================== + } else { + return "OWXLCD: Wrong byte write attempt"; + } + + #"byte" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for writing a byte"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing a byte"); + } + PT_END; +} + ######################################################################################## # # OWXLCD_Get - get values from the LCD device @@ -598,14 +842,12 @@ sub OWXLCD_Byte($$$) { sub OWXLCD_Get($$) { - my ($hash,$cmd,$value) = @_; + my ($hash,$cmd) = @_; my ($select, $select2, $len, $addr, $res, $res2); #-- ID of the device my $owx_dev = $hash->{ROM_ID}; - my $owx_rnf = substr($owx_dev,3,12); - my $owx_f = substr($owx_dev,0,2); #-- hash of the busmaster my $master = $hash->{IODev}; @@ -630,43 +872,93 @@ sub OWXLCD_Get($$) { } else { return "OWXLCD: Wrong get attempt"; } - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "get.prepare", 1, $owx_dev, $select, 0, 0 )) { - return "OWLCD: Device $owx_dev not accessible for reading"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for reading"; - } - } + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for reading"; + } #-- issue the read scratchpad command \xBE $select2 = "\xBE"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "get.".$cmd, 1, $owx_dev, $select2, $len, 0 ) or !OWX_AwaitExecuteResponse($master,"get.".$cmd,$owx_dev)) { - return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select2,$len); - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; - } - OWXLCD_BinValues($hash, "get.".$cmd, 1, 1, $owx_dev, $select2, $len, substr($res,10)); + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select2,$len); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; } + OWXLCD_BinValues($hash, "get.".$cmd, 1, 1, $owx_dev, $select2, $len, substr($res,10)); + return main::ReadingsVal($hash->{NAME},$cmd,""); } +######################################################################################## +# +# OWXLCD_PT_Get - get values from the LCD device async +# +# Parameter hash = hash of device addressed +# cmd = command string +# +######################################################################################## + +sub OWXLCD_PT_Get($$) { + + my ($thread,$hash,$cmd) = @_; + + my ($select); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + + my ($i,$j,$k); + + PT_BEGIN($thread); + #=============== fill scratch with gpio ports =============================== + if ( $cmd eq "gpio" ) { + #-- issue the read GPIO command \x22 (1 byte) + $select = "\x22"; + $thread->{len} = 1; + #=============== fill scratch with gpio counters =============================== + }elsif ( $cmd eq "counter" ) { + #-- issue the read counter command \x23 (8 bytes) + $select = "\x23"; + $thread->{len} = 8; + #=============== fill scratch with version =============================== + }elsif ( $cmd eq "version" ) { + #-- issue the read version command \x41 + $select = "\x41"; + $thread->{len} = 16; + } else { + PT_EXIT("OWXLCD: Wrong get attempt"); + } + #"get.prepare" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for reading"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing a byte"); + } + + #-- issue the read scratchpad command \xBE + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xBE", $thread->{len} )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for reading in 2nd step"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + my $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing a byte"); + } + OWXLCD_BinValues($hash, "get.".$cmd, 1, 1, $owx_dev, $response->{writedata}, $response->{numread}, $response->{readdata}); + + PT_END; +} + ######################################################################################## # # OWXLCD_GetMemory - get memory page from LCD device (EXPERIMENTAL) @@ -695,21 +987,65 @@ sub OWXLCD_GetMemory($$) { #-- issue the match ROM command \x55 and the copy eeprom to scratchpad command \x4E #Log 1," page read is ".$page; $select = sprintf("\4E%c\x10\x37",$page); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "prepare", 1, $owx_dev, $select, 0, 0 )) { - return "OWLCD: Device $owx_dev not accessible for reading"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for reading"; - } + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for reading"; + } + + #-- sleeping for some time + #select(undef,undef,undef,0.5); + + #-- issue the match ROM command \x55 and the read scratchpad command \xBE + $select = "\xBE"; + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,16); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; + } + OWXLCD_BinValues($hash, "get.memory.$page", 1, 1, $owx_dev, $select, 16, substr($res,11,16)); + #-- process results (10 bytes or more have been sent) + #$res2 = substr($res,11,16); + #return $res2; + return main::ReadingsVal($hash->{NAME},"memory$page",""); +} + +######################################################################################## +# +# OWXLCD_PT_GetMemory - get memory page from LCD device async (EXPERIMENTAL) +# +# Parameter hash = hash of device addressed +# page = memory page address +# +######################################################################################## + +sub OWXLCD_PT_GetMemory($$) { + + my ($thread,$hash,$page) = @_; + + my ($select); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + #-- issue the match ROM command \x55 and the copy eeprom to scratchpad command \x4E + #Log 1," page read is ".$page; + $select = sprintf("\4E%c\x10\x37",$page); + #"prepare" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for reading memory"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error reading memory"); } #-- sleeping for some time @@ -717,26 +1053,21 @@ sub OWXLCD_GetMemory($$) { #-- issue the match ROM command \x55 and the read scratchpad command \xBE $select = "\xBE"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "get.memory.$page", 1, $owx_dev, $select,16, 0 ) or !OWX_AwaitExecuteResponse($master,"get.memory.$page",$owx_dev)) { - return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,16); - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for reading in 2nd step"; - } - OWXLCD_BinValues($hash, "get.memory.$page", 1, 1, $owx_dev, $select, 16, substr($res,11,16)); - #-- process results (10 bytes or more have been sent) - #$res2 = substr($res,11,16); - #return $res2; + #"get.memory.$page" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select,16 )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for reading in 2nd step"); } - return main::ReadingsVal($hash->{NAME},"memory$page",""); + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + my $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error reading memory in 2nd step"); + } + + OWXLCD_BinValues($hash, "get.memory.$page", 1, 1, $owx_dev, $response->{writedata}, $response->{numread}, $response->{readdata}); + #-- process results (10 bytes or more have been sent) + #$res2 = substr($res,11,16); + #return $res2; + PT_END; } ######################################################################################## @@ -754,8 +1085,6 @@ sub OWXLCD_InitializeDevice($) { #-- ID of the device my $owx_dev = $hash->{ROM_ID}; - my $owx_rnf = substr($owx_dev,3,12); - my $owx_f = substr($owx_dev,0,2); #-- hash of the busmaster my $master = $hash->{IODev}; @@ -772,21 +1101,31 @@ sub OWXLCD_InitializeDevice($) { #-- Entry Mode Set: cursor auto increment = \x06 #OWXLCD_Byte($hash,"register",6); - #-- Function Set: 4 bit data size, RE => 1, blink Enable = \x26 - OWXLCD_Byte($hash,"register",38); - - #-- Ext. Function Set: 4 line mode = \x09 - OWXLCD_Byte($hash,"register",9); - - #-- Function Set: 4 bit data size, RE => 0 = \x20 - OWXLCD_Byte($hash,"register",32); - - #-- Display ON/OFF: display on, cursor off, blink off = \x0C - OWXLCD_Byte($hash,"register",12); - - #-- Clear Display - OWXLCD_Byte($hash,"register",1); - + if ($hash->{ASYNC}) { + eval { + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_Byte),$hash,"register",38); + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_Byte),$hash,"register", 9); + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_Byte),$hash,"register",32); + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_Byte),$hash,"register",12); + OWX_ASYNC_Schedule($hash,PT_THREAD(\&OWXLCD_PT_Byte),$hash,"register", 1); + }; + return GP_Catch($@) if $@; + } else { + #-- Function Set: 4 bit data size, RE => 1, blink Enable = \x26 + OWXLCD_Byte($hash,"register",38); + + #-- Ext. Function Set: 4 line mode = \x09 + OWXLCD_Byte($hash,"register",9); + + #-- Function Set: 4 bit data size, RE => 0 = \x20 + OWXLCD_Byte($hash,"register",32); + + #-- Display ON/OFF: display on, cursor off, blink off = \x0C + OWXLCD_Byte($hash,"register",12); + + #-- Clear Display + OWXLCD_Byte($hash,"register",1); + } return undef; #-- or else } else { @@ -847,25 +1186,82 @@ sub OWXLCD_SetFunction($$$) { return "OWXLCD: Wrong function selected"; } - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "set.function", 1, $owx_dev, $select, 0, 0 )) { - return "OWLCD: Device $owx_dev not accessible for writing"; - } - #-- synchronous mode - } else { - #-- write to device - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - #-- process results - if( $res eq 0 ){ - return "OWLCD: Device $owx_dev not accessible for writing"; - } + #-- write to device + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); + #-- process results + if( $res eq 0 ){ + return "OWLCD: Device $owx_dev not accessible for writing"; } return undef; } +######################################################################################## +# +# OWXLCD_PT_SetFunction - write state and values of the LCD device async +# +# Parameter hash = hash of device addressed +# cmd = command string +# value = data value +# +######################################################################################## + +sub OWXLCD_PT_SetFunction($$$) { + + my ($thread,$hash,$cmd,$value) = @_; + + my ($select); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + my ($i,$j,$k); + + PT_BEGIN($thread); + + #=============== set gpio ports =============================== + if ( $cmd eq "gpio" ) { + #-- issue the write GPIO command + # \x21 followed by the data value (= integer 0 - 7) + $select = sprintf("\x21%c",$value); + #=============== switch LCD on =============================== + }elsif ( $cmd eq "lcdon" ) { + #-- issue the lcd on cmd + $select = "\x03"; + #=============== switch LCD off =============================== + }elsif ( $cmd eq "lcdoff" ) { + #-- issue the lcd off cmd + $select = "\x05"; + #=============== switch LCD backlight on =============================== + }elsif ( $cmd eq "bklon" ) { + #-- issue the backlight on cmd + $select = "\x08"; + #=============== switch LCD backlight off =============================== + }elsif ( $cmd eq "bkloff" ) { + #-- issue the backlight off cmd + $select = "\x07"; + #=============== switch LCD backlight off =============================== + }elsif ( $cmd eq "reset" ) { + #-- issue the clear LCD command + $select = "\x49"; + #=============== wrong write attempt =============================== + } else { + return "OWXLCD: Wrong function selected"; + } + #"set.function" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("OWLCD: Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + PT_END; +} + ######################################################################################## # # OWXLCD_SetIcon - set one of the icons @@ -893,38 +1289,20 @@ sub OWXLCD_SetIcon($$$) { if( $icon == 0){ #-- 4 bit data size, RE => 1, blink Enable = \x26 $select = "\x10\x26"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.1", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- SEGRAM addres to 0 = \x40, $select = "\x10\x40"; #-- write 16 zeros to scratchpad $select .= "\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.2", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- issue the copy scratchpad to LCD command \x48 $select="\x48"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.3", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); } else { #-- determine data value if( int($icon) != 16 ){ @@ -958,54 +1336,173 @@ sub OWXLCD_SetIcon($$$) { } #-- 4 bit data size, RE => 1, blink Enable = \x26 $select = "\x10\x26"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.4", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- SEGRAM addres to 0 = \x40 + icon address $select = sprintf("\x10%c",63+$icon); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.5", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- data $select = sprintf("\x12%c",$data); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.6", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); } #-- return to normal state $select = "\x10\x20"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute( $master, "set.icon.7", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- or else } else { return "OWXLCD: Wrong LCD controller type"; } } +######################################################################################## +# +# OWXLCD_PT_SetIcon - set one of the icons async +# +# Parameter hash = hash of device addressed +# icon = address of the icon used = 0,1 .. 16 (0 = all off) +# value = data value: 0 = off, 1 = on, 2 = blink +# for battery icon 16: 0 = off, 1 = empty ... 5 = full, 6 = empty blink +# +######################################################################################## + +sub OWXLCD_PT_SetIcon($$$) { + my ($thread,$hash,$icon,$value) = @_; + + my ($i,$data,$select, $res); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + #-- only for KS0073 + if ( $lcdcontroller eq "KS0073"){ + + #-- write 16 zeros to erase all icons + if( $icon == 0){ + #-- 4 bit data size, RE => 1, blink Enable = \x26 + $select = "\x10\x26"; + #"set.icon.1" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- SEGRAM addres to 0 = \x40, + $select = "\x10\x40"; + #-- write 16 zeros to scratchpad + $select .= "\x4E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + #"set.icon.2" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- issue the copy scratchpad to LCD command \x48 + $select="\x48"; + #"set.icon.3" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + } else { + #-- determine data value + if( int($icon) != 16 ){ + if( $value == 0 ){ + $data = 0; + } elsif ( $value == 1) { + $data = 16; + } elsif ( $value == 2) { + $data = 80; + } else { + PT_EXIT("OWXLCD: Wrong data value $value for icon $icon"); + } + } else { + if( $value == 0 ){ + $data = 0; + } elsif ( $value == 1) { + $data = 16; + } elsif ( $value == 2) { + $data = 24; + } elsif ( $value == 3) { + $data = 28; + } elsif ( $value == 4) { + $data = 30; + } elsif ( $value == 5) { + $data = 31; + } elsif ( $value == 6) { + $data = 80; + } else { + PT_EXIT("OWXLCD: Wrong data value $value for icon $icon"); + } + } + #-- 4 bit data size, RE => 1, blink Enable = \x26 + $select = "\x10\x26"; + #"set.icon.4" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- SEGRAM addres to 0 = \x40 + icon address + $select = sprintf("\x10%c",63+$icon); + #"set.icon.5" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- data + $select = sprintf("\x12%c",$data); + #"set.icon.6" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + } + + #-- return to normal state + $select = "\x10\x20"; + #"set.icon.7" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + #-- or else + } else { + PT_EXIT("OWXLCD: Wrong LCD controller type"); + } + PT_END; +} + ######################################################################################## # # OWXLCD_SetLine - set one of the display lines @@ -1060,25 +1557,13 @@ sub OWXLCD_SetLine($$$) { #-- issue the match ROM command \x55 and the write scratchpad command \x4E # followed by LCD page address and the text $select=sprintf("\x4E%c",$lcdpage[$line]).$msgA; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res = OWX_Execute( $master, "set.line.1", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- issue the copy scratchpad to LCD command \x48 $select="\x48"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res3 = OWX_Execute( $master, "set.line.2", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res3=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res3=OWX_Complex($master,$owx_dev,$select,0); #-- if second string available: if( defined($msgB) ) { @@ -1086,25 +1571,13 @@ sub OWXLCD_SetLine($$$) { #-- issue the match ROM command \x55 and the write scratchpad command \x4E # followed by LCD page address and the text $select=sprintf("\x4E%c",$lcdpage[$line]+16).$msgB; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res2 = OWX_Execute( $master, "set.line.3", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res2=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res2=OWX_Complex($master,$owx_dev,$select,0); #-- issue the copy scratchpad to LCD command \x48 $select="\x48"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res3 = OWX_Execute( $master, "set.line.4", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res3=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res3=OWX_Complex($master,$owx_dev,$select,0); } #-- process results @@ -1116,6 +1589,112 @@ sub OWXLCD_SetLine($$$) { } +######################################################################################## +# +# OWXLCD_PT_SetLine - set one of the display lines async +# +# Parameter hash = hash of device addressed +# line = line number (0..3) +# msg = data string to be written +# +######################################################################################## + +sub OWXLCD_PT_SetLine($$$) { + + my ($thread,$hash,$line,$msg) = @_; + + my ($select, $i, $msgA, $msgB); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + $line = int($line); + + PT_BEGIN($thread); + + $msg = defined($msg) ? $msg : ""; + $msg = OWXLCD_Trans($msg); + + #-- split if longer than 16 bytes, fill each with blanks + # has already been checked to be <= $lcdchars + if( $lcdchars > 16 ){ + if( length($msg) > 16 ) { + $msgA = substr($msg,0,16); + $msgB = substr($msg,16,length($msg)-16); + for($i = 0;$i<$lcdchars-length($msg);$i++){ + $msgB .= "\x20"; + } + } else { + $msgA = $msg; + for($i = 0;$i<16-length($msg);$i++){ + $msgA .= "\x20"; + } + for($i = 0;$i<$lcdchars-16;$i++){ + $msgB .= "\x20"; + } + } + }else{ + $msgA = $msg; + for($i = 0;$i<$lcdchars-length($msg);$i++){ + $msgA .= "\x20"; + } + $msgB = undef; + } + $thread->{msgB} = $msgB; + + #-- issue the match ROM command \x55 and the write scratchpad command \x4E + # followed by LCD page address and the text + $select=sprintf("\x4E%c",$lcdpage[$line]).$msgA; + #"set.line.1" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- issue the copy scratchpad to LCD command \x48 + $select="\x48"; + #"set.line.2" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- if second string available: + if( defined($thread->{msgB}) ) { + #select(undef,undef,undef,0.005); + #-- issue the match ROM command \x55 and the write scratchpad command \x4E + # followed by LCD page address and the text + $select=sprintf("\x4E%c",$lcdpage[$line]+16).$thread->{msgB}; + #"set.line.3" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- issue the copy scratchpad to LCD command \x48 + $select="\x48"; + #"set.line.4" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + } + PT_END; +} + ######################################################################################## # # OWXLCD_Trans - String translation helper @@ -1178,26 +1757,14 @@ sub OWXLCD_SetMemory($$$) { # followed by LCD page address and the text #Log 1," page written is ".$page; $select=sprintf("\x4E\%c",$page).$msgA; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res = OWX_Execute( $master, "set.memory.page", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,0); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,0); #-- issue the copy scratchpad to EEPROM command \x39 $select = "\x39"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - $res2 = OWX_Execute( $master, "set.memory.copy", 1, $owx_dev, $select, 0, 0 ); - #-- synchronous mode - } else { - OWX_Reset($master); - $res2=OWX_Complex($master,$owx_dev,$select,0); - } - + OWX_Reset($master); + $res2=OWX_Complex($master,$owx_dev,$select,0); + #-- process results if( ($res eq 0) || ($res2 eq 0) ){ return "OWLCD: Device $owx_dev not accessible for writing"; @@ -1207,6 +1774,63 @@ sub OWXLCD_SetMemory($$$) { } +######################################################################################## +# +# OWXLCD_PT_SetMemory - set internal nonvolatile memory async +# +# Parameter hash = hash of device addressed +# page = page number (0..14) +# msg = data string to be written +# +######################################################################################## + +sub OWXLCD_PT_SetMemory($$$) { + + my ($thread,$hash,$page,$msg) = @_; + + my ($select, $i, $msgA); + + #-- ID of the device, hash of the busmaster + my $owx_dev = $hash->{ROM_ID}; + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + $page = int($page); + $msg = defined($msg) ? $msg : ""; + + #-- fillup with blanks + $msgA = $msg; + for($i = 0;$i<16-length($msg);$i++){ + $msgA .= "\x20"; + } + + #-- issue the match ROM command \x55 and the write scratchpad command \x4E + # followed by LCD page address and the text + #Log 1," page written is ".$page; + $select=sprintf("\x4E\%c",$page).$msgA; + #"set.memory.page" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + + #-- issue the copy scratchpad to EEPROM command \x39 + $select = "\x39"; + #"set.memory.copy" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0 )) { + PT_EXIT("Device $owx_dev not accessible for writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWLCD: Device $owx_dev error writing"); + } + PT_END; +} + sub OWXLCD_BinValues($$$$$$$$) { my ($hash, $cmd, $success, $reset, $owx_dev, $command, $numread, $res) = @_; diff --git a/fhem/FHEM/21_OWMULTI.pm b/fhem/FHEM/21_OWMULTI.pm index cde763765..55ebefcb0 100644 --- a/fhem/FHEM/21_OWMULTI.pm +++ b/fhem/FHEM/21_OWMULTI.pm @@ -68,9 +68,21 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use ProtoThreads; +no warnings 'deprecated'; + sub Log($$); -my $owx_version="5.13"; +my $owx_version="5.14"; #-- flexible channel name my $owg_channel; @@ -118,7 +130,6 @@ sub OWMULTI_Initialize ($) { $hash->{UndefFn} = "OWMULTI_Undef"; $hash->{GetFn} = "OWMULTI_Get"; $hash->{SetFn} = "OWMULTI_Set"; - $hash->{AfterExecuteFn} = "OWXMULTI_BinValues"; $hash->{AttrFn} = "OWMULTI_Attr"; #tempOffset = a temperature offset added to the temperature reading for correction @@ -134,8 +145,6 @@ sub OWMULTI_Initialize ($) { $hash->{owg_val}->[2] = undef; $hash->{owg_val}->[1] = undef; - #-- this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXMULTI_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -165,7 +174,7 @@ sub OWMULTI_Attr(@) { $hash->{INTERVAL} = $value; if ($init_done) { RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 1); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0); } last; }; @@ -471,10 +480,14 @@ sub OWMULTI_Get($@) { #-- for the other readings we need a new reading #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- not different from getting all values .. - $ret = OWXMULTI_GetValues($hash,1); - #ASYNC: Need to wait for some return + $ret = OWXMULTI_GetValues($hash); + }elsif( $interface eq "OWX_ASYNC"){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXMULTI_PT_GetValues); + while ($task->PT_SCHEDULE($hash)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface not yet implemented }elsif( $interface eq "OWServer" ){ $ret = OWFSMULTI_GetValues($hash); @@ -529,18 +542,23 @@ sub OWMULTI_GetValues($) { #-- restart timer for updates RemoveInternalTimer($hash); - InternalTimer(time()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 1); + InternalTimer(time()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0); #-- Get values according to interface type my $interface= $hash->{IODev}->{TYPE}; - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- max 3 tries for(my $try=0; $try<3; $try++){ $ret = OWXMULTI_GetValues($hash); #ASYNC: Need to wait for some result return if( !defined($ret) ); - } + } + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXMULTI_PT_GetValues),$hash ); + }; + $ret = GP_Catch($@) if $@; }elsif( $interface eq "OWServer" ){ $ret = OWFSMULTI_GetValues($hash); }else{ @@ -613,7 +631,7 @@ sub OWMULTI_Set($@) { # update timer $hash->{INTERVAL} = $value; RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 1); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0); return undef; } @@ -632,8 +650,13 @@ sub OWMULTI_Set($@) { $a[2] = int($value/$factor-$offset); #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXMULTI_SetValues($hash,@a); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXMULTI_PT_SetValues),$hash,@a ); + }; + $ret = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSMULTI_SetValues($hash,@a); @@ -827,9 +850,9 @@ sub OWXMULTI_BinValues($$$$$$$$) { # ######################################################################################## -sub OWXMULTI_GetValues($@) { +sub OWXMULTI_GetValues($) { - my ($hash,$sync) = @_; + my ($hash) = @_; my ($i,$j,$k,$res,$res2); @@ -843,186 +866,98 @@ sub OWXMULTI_GetValues($@) { #------------------------------------------------------------------------------------ #-- switch the device to current measurement off, VDD only #-- issue the match ROM command \x55 and the write scratchpad command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.writestatusvdd", 1, $owx_dev, "\x4E\x00\x08", 0, undef )) { - return "$owx_dev write status failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x4E\x00\x08",0) eq 0 ){ - return "$owx_dev write status failed"; - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x4E\x00\x08",0) eq 0 ){ + return "$owx_dev write status failed"; } #-- copy scratchpad to register #-- issue the match ROM command \x55 and the copy scratchpad command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.copyscratchpadvdd", 1, $owx_dev, "\x48\x00", 0, undef )) { - return "$owx_dev copy scratchpad failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){ - return "$owx_dev copy scratchpad failed"; - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){ + return "$owx_dev copy scratchpad failed"; } #-- initiate temperature conversion #-- conversion needs some 12 ms ! #-- issue the match ROM command \x55 and the start conversion command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.temperaturconversionvdd", 1, $owx_dev, "\x44", 0, 12 )) { - return "$owx_dev temperature conversion failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){ - return "$owx_dev temperature conversion failed"; - } - select(undef,undef,undef,0.012); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){ + return "$owx_dev temperature conversion failed"; + } + select(undef,undef,undef,0.012); #-- initiate voltage conversion #-- conversion needs some 6 ms ! #-- issue the match ROM command \x55 and the start conversion command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.voltageconversionvdd", 1, $owx_dev, "\xB4", 0, 6 )) { - return "$owx_dev voltage conversion failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\xB4",0) eq 0 ){ - return "$owx_dev voltage conversion failed"; - } - select(undef,undef,undef,0.006); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\xB4",0) eq 0 ){ + return "$owx_dev voltage conversion failed"; + } + select(undef,undef,undef,0.006); #-- from memory to scratchpad #-- copy needs some 12 ms ! #-- issue the match ROM command \x55 and the recall memory command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.recallmemoryvdd", 1, $owx_dev, "\xB8\x00", 0, 12 )) { - return "$owx_dev recall memory failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\xB8\x00",0) eq 0 ){ - return "$owx_dev recall memory failed"; - } - select(undef,undef,undef,0.012); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\xB8\x00",0) eq 0 ){ + return "$owx_dev recall memory failed"; + } + select(undef,undef,undef,0.012); #-- NOW ask the specific device #-- issue the match ROM command \x55 and the read scratchpad command \xBE #-- reading 9 + 2 + 9 data bytes = 20 bytes - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.getvdd", 1, $owx_dev, "\xBE\x00", 9, undef )) { - return "$owx_dev not accessible in 2nd step"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9); - #Log 1,"OWXMULTI: data length from reading device is ".length($res)." bytes"; - return "$owx_dev not accessible in 2nd step" - if( $res eq 0 ); - return "$owx_dev has returned invalid data" - if( length($res)!=20); - OWXMULTI_BinValues($hash,"ds2438.getvdd",1,undef,$owx_dev,undef,undef,substr($res,11)); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9); + #Log 1,"OWXMULTI: data length from reading device is ".length($res)." bytes"; + return "$owx_dev not accessible in 2nd step" + if( $res eq 0 ); + return "$owx_dev has returned invalid data" + if( length($res)!=20); + OWXMULTI_BinValues($hash,"ds2438.getvdd",1,undef,$owx_dev,undef,undef,substr($res,11)); #------------------------------------------------------------------------------------ #-- switch the device to current measurement off, V external only #-- issue the match ROM command \x55 and the write scratchpad command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.writestatusvad", 1, $owx_dev, "\x4E\x00\x00", 0, undef )) { - return "$owx_dev write status failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x4E\x00\x00",0) eq 0 ){ - return "$owx_dev write status failed"; - } - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x4E\x00\x00",0) eq 0 ){ + return "$owx_dev write status failed"; + } #-- copy scratchpad to register #-- issue the match ROM command \x55 and the copy scratchpad command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.copyscratchpadvad", 1, $owx_dev, "\x48\x00", 0, undef )) { + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){ return "$owx_dev copy scratchpad failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){ - return "$owx_dev copy scratchpad failed"; - } } #-- initiate voltage conversion #-- conversion needs some 6 ms ! #-- issue the match ROM command \x55 and the start conversion command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.voltageconversionvad", 1, $owx_dev, "\xB4", 0, 6 )) { - return "$owx_dev voltage conversion failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\xB4",0) eq 0 ){ - return "$owx_dev voltage conversion failed"; - } - select(undef,undef,undef,0.006); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\xB4",0) eq 0 ){ + return "$owx_dev voltage conversion failed"; + } + select(undef,undef,undef,0.006); #-- from memory to scratchpad #-- copy needs some 12 ms ! #-- issue the match ROM command \x55 and the recall memory command - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "ds2438.recallmemoryvad", 1, $owx_dev, "\xB8\x00", 0, 12 )) { - return "$owx_dev recall memory failed"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\xB8\x00",0) eq 0 ){ - return "$owx_dev recall memory failed"; - } - select(undef,undef,undef,0.012); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\xB8\x00",0) eq 0 ){ + return "$owx_dev recall memory failed"; + } + select(undef,undef,undef,0.012); #-- NOW ask the specific device #-- issue the match ROM command \x55 and the read scratchpad command \xBE #-- reading 9 + 2 + 9 data bytes = 20 bytes my $context = "ds2438.getvad"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, $context, 1, $owx_dev, "\xBE\x00", 9, undef ) or ($sync and !OWX_AwaitExecuteResponse($master,$context,$owx_dev))) { - return "$owx_dev not accessible in 2nd step"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9); - #-- process results - return "$owx_dev not accessible in 2nd step" - if( $res eq 0 ); - return "$owx_dev has returned invalid data" - if( length($res)!=20); - OWXMULTI_BinValues($hash,$context,1,undef,$owx_dev,undef,undef,substr($res,11)); - } + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9); + #-- process results + return "$owx_dev not accessible in 2nd step" + if( $res eq 0 ); + return "$owx_dev has returned invalid data" + if( length($res)!=20); + OWXMULTI_BinValues($hash,$context,1,undef,$owx_dev,undef,undef,substr($res,11)); return undef; } @@ -1059,23 +994,235 @@ sub OWXMULTI_SetValues($@) { # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM my $select=sprintf("\x4E%c%c\x48",0,0); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "setvalues", 1, $owx_dev, $select, 0, undef )) { - return "OWXMULTI: Device $owx_dev not accessible"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - my $res=OWX_Complex($master,$owx_dev,$select,0); - if( $res eq 0 ){ - return "OWXMULTI: Device $owx_dev not accessible"; - } - } + OWX_Reset($master); + my $res=OWX_Complex($master,$owx_dev,$select,0); + if( $res eq 0 ){ + return "OWXMULTI: Device $owx_dev not accessible"; + } return undef; } +######################################################################################## +# +# OWXMULTI_PT_GetValues - Get reading from one device async +# +# Parameter hash = hash of device addressed +# final= 1 if FormatValues is to be called +# +######################################################################################## + +sub OWXMULTI_PT_GetValues($) { + + my ($thread,$hash) = @_; + + my ($i,$j,$k,$res,$res2,$response); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + #-- reset presence + $hash->{PRESENT} = 0; + #------------------------------------------------------------------------------------ + #-- switch the device to current measurement off, VDD only + #-- issue the match ROM command \x55 and the write scratchpad command + #"ds2438.writestatusvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x4E\x00\x08", 0)) { + PT_EXIT("$owx_dev not accessible for writing status"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev write status failed"); + } + + #-- copy scratchpad to register + #-- issue the match ROM command \x55 and the copy scratchpad command + #"ds2438.copyscratchpadvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x48\x00", 0)) { + PT_EXIT("$owx_dev not accessible to copy scratchpad"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev copy scratchpad failed"); + } + + #-- initiate temperature conversion + #-- conversion needs some 12 ms ! + #-- issue the match ROM command \x55 and the start conversion command + #"ds2438.temperaturconversionvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x44", 0)) { + PT_EXIT("$owx_dev not accessible for temperature conversion"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev temperature conversion failed"); + } + #TODO implement async wait + select(undef,undef,undef,0.012); + + #-- initiate voltage conversion + #-- conversion needs some 6 ms ! + #-- issue the match ROM command \x55 and the start conversion command + #"ds2438.voltageconversionvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xB4", 0)) { + PT_EXIT("$owx_dev not accessible for voltage conversion"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev voltage conversion failed"); + } + #TODO implement async wait + select(undef,undef,undef,0.006); + + #-- from memory to scratchpad + #-- copy needs some 12 ms ! + #-- issue the match ROM command \x55 and the recall memory command + #"ds2438.recallmemoryvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xB8\x00", 0)) { + PT_EXIT("$owx_dev not accessible for recall memory"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev recall memory failed"); + } + #TODO implement async wait + select(undef,undef,undef,0.012); + #-- NOW ask the specific device + #-- issue the match ROM command \x55 and the read scratchpad command \xBE + #-- reading 9 + 2 + 9 data bytes = 20 bytes + #"ds2438.getvdd" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xBE\x00", 9)) { + PT_EXIT("$owx_dev not accessible in 2nd step"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev not accessible in 2nd step"); + } + $res = $response->{readdata}; + unless (defined $res and length($res)==9) { + PT_EXIT("$owx_dev has returned invalid data"); + } + OWXMULTI_BinValues($hash,"ds2438.getvdd",1,undef,$owx_dev,undef,undef,$res); + #------------------------------------------------------------------------------------ + #-- switch the device to current measurement off, V external only + #-- issue the match ROM command \x55 and the write scratchpad command + #"ds2438.writestatusvad" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x4E\x00\x00", 0)) { + PT_EXIT("$owx_dev not accessible to write status"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev write status failed"); + } + #-- copy scratchpad to register + #-- issue the match ROM command \x55 and the copy scratchpad command + #"ds2438.copyscratchpadvad" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\x48\x00", 0)) { + PT_EXIT("$owx_dev not accessible to copy scratchpad"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev copy scratchpad failed"); + } + #-- initiate voltage conversion + #-- conversion needs some 6 ms ! + #-- issue the match ROM command \x55 and the start conversion command + #"ds2438.voltageconversionvad" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xB4", 0)) { + PT_EXIT("$owx_dev not accessible for voltage conversion"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev voltage conversion failed"); + } + #TODO implement async wait + select(undef,undef,undef,0.006); + + #-- from memory to scratchpad + #-- copy needs some 12 ms ! + #-- issue the match ROM command \x55 and the recall memory command + #"ds2438.recallmemoryvad" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xB8\x00", 0)) { + PT_EXIT("$owx_dev not accessible to recall memory"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("$owx_dev recall memory failed"); + } + #TODO implement async wait + select(undef,undef,undef,0.012); + + #-- NOW ask the specific device + #-- issue the match ROM command \x55 and the read scratchpad command \xBE + #-- reading 9 + 2 + 9 data bytes = 20 bytes + #"ds2438.getvad" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xBE\x00", 9)) { + PT_EXIT("$owx_dev not accessible in 2nd step"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev not accessible in 2nd step"); + } + #-- process results + $res = $response->{readdata}; + unless (defined $res and length($res)==9) { + PT_EXIT("$owx_dev has returned invalid data"); + } + OWXMULTI_BinValues($hash,"ds2438.getvad",1,undef,$owx_dev,undef,undef,$res); + PT_END; +} + +####################################################################################### +# +# OWXMULTI_PT_SetValues - Set values in device async +# +# Parameter hash = hash of device addressed +# a = argument array +# +######################################################################################## + +sub OWXMULTI_PT_SetValues($@) { + my ($thread,$hash, @a) = @_; + + my ($i,$j,$k); + + my $name = $hash->{NAME}; + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + #-- define vars + my $key = $a[1]; + my $value = $a[2]; + + #-- issue the match ROM command \x55 and the write scratchpad command \x4E, + # followed by the write EEPROM command \x48 + # + # so far writing the EEPROM does not work properly. + # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM + # 2. \x48 appended to match ROM => command not ok. + # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM + + my $select=sprintf("\x4E%c%c\x48",0,0); + #"setvalues" + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 0)) { + PT_EXIT("OWXMULTI: Device $owx_dev not accessible"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("OWXMULTI: error setting values in $owx_dev"); + } + + PT_END; +} + 1; =pod diff --git a/fhem/FHEM/21_OWSWITCH.pm b/fhem/FHEM/21_OWSWITCH.pm index 056d72757..3b711791f 100644 --- a/fhem/FHEM/21_OWSWITCH.pm +++ b/fhem/FHEM/21_OWSWITCH.pm @@ -74,9 +74,22 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use ProtoThreads; +no warnings 'deprecated'; + sub Log($$); -my $owx_version="5.12"; +my $owx_version="5.14"; #-- fixed raw channel name, flexible channel name my @owg_fixed = ("A","B","C","D","E","F","G","H"); my @owg_channel = ("A","B","C","D","E","F","G","H"); @@ -149,8 +162,6 @@ sub OWSWITCH_Initialize ($) { $hash->{owg_val} = []; $hash->{owg_vax} = []; - #-- ASYNC this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXSWITCH_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -288,7 +299,7 @@ sub OWSWITCH_Attr(@) { $hash->{INTERVAL} = $value; if ($init_done) { RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 1); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 0); } last; }; @@ -492,8 +503,13 @@ sub OWSWITCH_Get($@) { if( !defined($fnd) ); #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret = OWXSWITCH_GetState($hash,1); + if( $interface eq "OWX" ){ + $ret = OWXSWITCH_GetState($hash); + }elsif( $interface eq "OWX_ASYNC") { + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXSWITCH_PT_GetState); + while ($task->PT_SCHEDULE($hash)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWFS" ){ $ret = OWFSSWITCH_GetState($hash); @@ -509,8 +525,13 @@ sub OWSWITCH_Get($@) { return "OWSWITCH: Get needs no parameter when reading gpio" if( int(@a)==1 ); - if( $interface =~ /^OWX/ ){ - $ret = OWXSWITCH_GetState($hash,1); + if( $interface eq "OWX" ){ + $ret = OWXSWITCH_GetState($hash); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXSWITCH_PT_GetState); + while ($task->PT_SCHEDULE($hash)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); }elsif( $interface eq "OWServer" ){ $ret = OWFSSWITCH_GetState($hash); }else{ @@ -546,16 +567,22 @@ sub OWSWITCH_GetValues($) { #-- restart timer for updates RemoveInternalTimer($hash); - InternalTimer(time()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 1); + InternalTimer(time()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 0); #-- Get readings according to interface type my $interface= $hash->{IODev}->{TYPE}; - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- max 3 tries for(my $try=0; $try<3; $try++){ $ret = OWXSWITCH_GetState($hash); return if( !defined($ret) ); - } + } + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXSWITCH_PT_GetState),$hash ); + }; + return unless $@; + $ret = GP_Catch($@); }elsif( $interface eq "OWServer" ){ $ret = OWFSSWITCH_GetState($hash); }else{ @@ -650,7 +677,7 @@ sub OWSWITCH_Set($@) { # update timer $hash->{INTERVAL} = $value; RemoveInternalTimer($hash); - InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 1); + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWSWITCH_GetValues", $hash, 0); return undef; } @@ -709,8 +736,8 @@ sub OWSWITCH_Set($@) { } #-- OWX interface - if( $interface =~ /^OWX/ ){ - $ret1 = OWXSWITCH_GetState($hash,1); + if( $interface eq "OWX" ){ + $ret1 = OWXSWITCH_GetState($hash); $value = 0; #-- vax or val ? for (my $i=0;$i<$cnumber{$attr{$name}{"model"}};$i++){ @@ -719,7 +746,12 @@ sub OWSWITCH_Set($@) { $value += ($nval<<$i) if( $i == $fnd ); } - $ret2 = OWXSWITCH_SetState($hash,$value); + $ret2 = OWXSWITCH_SetState($hash,$value); + }elsif( $interface eq "OWX_ASYNC"){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXSWITCH_PT_SetOutput),$hash,$fnd,$nval ); + }; + $ret2 = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret1 = OWFSSWITCH_GetState($hash); @@ -751,8 +783,13 @@ sub OWSWITCH_Set($@) { return "OWSWITCH: Set with wrong value for gpio port, must be 0 <= gpio <= ".((1 << $cnumber{$attr{$name}{"model"}})-1) if( ! ((int($value) >= 0) && (int($value) <= ((1 << $cnumber{$attr{$name}{"model"}})-1 ))) ); - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXSWITCH_SetState($hash,int($value)); + }elsif( $interface eq "OWX_ASYNC" ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXSWITCH_PT_SetState),$hash,int($value) ); + }; + $ret = GP_Catch($@) if $@; }elsif( $interface eq "OWServer" ){ $ret = OWFSSWITCH_SetState($hash,int($value)); }else{ @@ -932,8 +969,6 @@ sub OWFSSWITCH_SetState($$) { # ######################################################################################## -sub OWXSWITCH_BinValues($$$$$$$$); #define prototype for recursive call; - sub OWXSWITCH_BinValues($$$$$$$$) { my ($hash, $context, $success, $reset, $owx_dev, $command, $numread, $res) = @_; @@ -994,36 +1029,7 @@ sub OWXSWITCH_BinValues($$$$$$$$) { #-- Now for context setstate }elsif ( $context =~ /.*setstate.*/){ #-- family = 12 => DS2406 ------------------------------------------------------- - #-- first step - if( ($context =~ /setstate\.ds2406\.1\..*/) or ($context =~ /ds2406\.setstate\.1\..*/) ) { - $value = substr($context,-1); - my $stat = ord(substr($res,0,1)); - my $statneu = ( $stat & 159 ) | (($value<<5) & 96) ; - #-- call the second step - #-- issue the match ROM command \x55 and the write status command - # \x55 at address TA1 = \x07 TA2 = \x00 - #-- reading 9 + 4 + 2 data bytes = 15 bytes - my $select=sprintf("\x55\x07\x00%c",$statneu); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (OWX_Execute( $master, "setstate.ds2406.2.".$value, 1, $owx_dev, $select, 2, undef )) { - return undef; - } else { - return "device $owx_dev not accessible in writing"; - } - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,2); - if( $res eq 0 ){ - return "device $owx_dev not accessible in writing"; - } - OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2406.setstate.2.".$value,1,undef,$owx_dev,$select,undef,substr($res,13)); - } - #-- family = 12 => DS2406 ------------------------------------------------------- - #-- second step from above - }elsif( ($context =~ /setstate\.ds2406\.2\..*/) or ($context =~ /ds2406\.setstate\.2\..*/) ) { + if( ($context =~ /setstate\.ds2406\..*/) or ($context =~ /ds2406\.setstate\..*/) ) { $value = substr($context,-1); @data=split(//,$res); if( int(@data) != 2){ @@ -1031,13 +1037,11 @@ sub OWXSWITCH_BinValues($$$$$$$$) { } Log 1,"invalid CRC" if (OWX_CRC16($command,$data[0],$data[1]) == 0); - #-- put into local buffer $hash->{owg_val}->[0] = $value % 2; $hash->{owg_vax}->[0] = $value % 2; $hash->{owg_val}->[1] = int($value / 2); $hash->{owg_vax}->[1] = int($value / 2); - #-- family = 29 => DS2408 ------------------------------------------------------- }elsif( ($context eq "setstate.ds2408") or ($context eq "ds2408.setstate") ) { @data=split(//,$res); @@ -1100,23 +1104,14 @@ sub OWXSWITCH_GetState($@) { # \xF5 plus the two byte channel control and the value #-- reading 9 + 3 + 2 data bytes + 2 CRC bytes = 16 bytes $select=sprintf("\xF5\xDD\xFF"); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "getstate.ds2406", 1, $owx_dev, $select, 4, undef ) or ($sync and !OWX_AwaitExecuteResponse($master,"getstate.ds2406",$owx_dev))) { - return "not accessible for reading"; - } - return undef; - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,4); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,4); return "$owx_dev not accessible in reading" if( $res eq 0 ); return "$owx_dev has returned invalid data" if( length($res)!=16); - OWX_Reset($master); - OWXSWITCH_BinValues($hash,"ds2406.getstate",1,undef,$owx_dev,substr($res,9,3),undef,substr($res,12)); - } + OWX_Reset($master); + OWXSWITCH_BinValues($hash,"ds2406.getstate",1,undef,$owx_dev,substr($res,9,3),undef,substr($res,12)); #-- family = 29 => DS2408 }elsif( $hash->{OW_FAMILY} eq "29" ) { #=============== get gpio values =============================== @@ -1124,46 +1119,28 @@ sub OWXSWITCH_GetState($@) { # \xF5 plus the two byte channel target address #-- reading 9 + 3 + 8 data bytes + 2 CRC bytes = 22 bytes $select=sprintf("\xF0\x88\x00"); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "getstate.ds2408", 1, $owx_dev, $select, 10, undef ) or ($sync and !OWX_AwaitExecuteResponse($master,"getstate.ds2408",$owx_dev))) { - return "not accessible for reading"; - } - return undef; - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,10); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,10); return "$owx_dev not accessible in reading" if( $res eq 0 ); return "$owx_dev has returned invalid data" if( length($res)!=22); - OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2408.getstate",1,undef,$owx_dev,substr($res,9,3),undef,substr($res,12)); - } + OWX_Reset($master); + return OWXSWITCH_BinValues($hash,"ds2408.getstate",1,undef,$owx_dev,substr($res,9,3),undef,substr($res,12)); #-- family = 3A => DS2413 }elsif( $hash->{OW_FAMILY} eq "3A" ) { #=============== get gpio values =============================== #-- issue the match ROM command \x55 and the read gpio command # \xF5 plus 2 empty bytes #-- reading 9 + 1 + 2 data bytes = 12 bytes - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute( $master, "getstate.ds2413", 1, $owx_dev, "\xF5", 2, undef ) or ($sync and !OWX_AwaitExecuteResponse($master,"getstate.ds2413",$owx_dev))) { - return "not accessible for reading"; - } - return undef; - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,"\xF5",2); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,"\xF5",2); return "$owx_dev not accessible in reading" if( $res eq 0 ); return "$owx_dev has returned invalid data" if( length($res)!=12); - #OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2413.getstate",1,undef,$owx_dev,substr($res,9,1),undef,substr($res,10)); - } + #OWX_Reset($master); + return OWXSWITCH_BinValues($hash,"ds2413.getstate",1,undef,$owx_dev,substr($res,9,1),undef,substr($res,10)); } else { return "unknown device family $hash->{OW_FAMILY}\n"; } @@ -1203,23 +1180,220 @@ sub OWXSWITCH_SetState($$) { #-- issue the match ROM command \x55 and the read status command # \xAA at address TA1 = \x07 TA2 = \x00 #-- reading 9 + 3 + 1 data bytes + 2 CRC bytes = 15 bytes - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (OWX_Execute( $master, "setstate.ds2406.1.".$value, 1, $owx_dev, "\xAA\x07\x00", 3, undef )) { - return undef; - } else { - return "not accessible in writing"; - } - #-- synchronous mode - }else{ - OWX_Reset($master); - $res = OWX_Complex($master,$owx_dev,"\xAA\x07\x00",3); - if( $res eq 0 ){ - return "device $owx_dev not accessible in writing"; - } - OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2406.setstate.1.".$value,1,undef,$owx_dev,undef,undef,substr($res,12)); + OWX_Reset($master); + $res = OWX_Complex($master,$owx_dev,"\xAA\x07\x00",3); + if( $res eq 0 ){ + return "device $owx_dev not accessible in writing"; } + OWX_Reset($master); + + my $stat = ord(substr($res,12,1)); + my $statneu = ( $stat & 159 ) | (($value<<5) & 96) ; + #-- call the second step + #-- issue the match ROM command \x55 and the write status command + # \x55 at address TA1 = \x07 TA2 = \x00 + #-- reading 9 + 4 + 2 data bytes = 15 bytes + my $select=sprintf("\x55\x07\x00%c",$statneu); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,2); + if( $res eq 0 ){ + return "device $owx_dev not accessible in writing"; + } + OWX_Reset($master); + return OWXSWITCH_BinValues($hash,"ds2406.setstate.$value",1,undef,$owx_dev,undef,undef,substr($res,13)); + #-- family = 29 => DS2408 + } elsif( $hash->{OW_FAMILY} eq "29" ) { + #=============== set gpio values =============================== + #-- issue the match ROM command \x55 and the write gpio command + # \x5A plus the value byte and its complement + $select=sprintf("\x5A%c%c",$value,255-$value); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,1); + if( $res eq 0 ){ + return "device $owx_dev not accessible in writing"; + } + OWX_Reset($master); + return OWXSWITCH_BinValues($hash,"ds2408.setstate",1,undef,$owx_dev,undef,undef,substr($res,12)); + #-- family = 3A => DS2413 + } elsif( $hash->{OW_FAMILY} eq "3A" ) { + #=============== set gpio values =============================== + #-- issue the match ROM command \x55 and the write gpio command + # \x5A plus the value byte and its complement + $select=sprintf("\x5A%c%c",252+$value,3-$value); + OWX_Reset($master); + $res=OWX_Complex($master,$owx_dev,$select,1); + if( $res eq 0 ){ + return "device $owx_dev not accessible in writing"; + } + OWX_Reset($master); + return OWXSWITCH_BinValues($hash,"ds2413.setstate",1,undef,$owx_dev,undef,undef,substr($res,12)); + }else { + return "unknown device family $hash->{OW_FAMILY}\n"; + } +} + +######################################################################################## +# +# OWXSWITCH_PT_GetState - Get gpio ports from device asynchronous +# +# Parameter hash = hash of device addressed +# +######################################################################################## + +sub OWXSWITCH_PT_GetState($) { + my ($thread,$hash) = @_; + + my ($select, $res, $res2, $res3, @data, $response); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + #-- reset presence + $hash->{PRESENT} = 0; + + my ($i,$j,$k); + + #-- family = 12 => DS2406 + if( $hash->{OW_FAMILY} eq "12" ) { + #=============== get gpio values =============================== + #-- issue the match ROM command \x55 and the access channel command + # \xF5 plus the two byte channel control and the value + #-- reading 9 + 3 + 2 data bytes + 2 CRC bytes = 16 bytes + $select=sprintf("\xF5\xDD\xFF"); + unless(OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 4)) { + PT_EXIT("device $owx_dev not accessible in reading"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev has returned invalid data"); + } + unless (length($response->{readdata}) == 4) { + PT_EXIT("$owx_dev has returned invalid data"); + } + OWXSWITCH_BinValues($hash,"ds2406.getstate",1,1,$owx_dev,$response->{writedata},4,$response->{readdata}); + #-- family = 29 => DS2408 + }elsif( $hash->{OW_FAMILY} eq "29" ) { + #=============== get gpio values =============================== + #-- issue the match ROM command \x55 and the read PIO rtegisters command + # \xF5 plus the two byte channel target address + #-- reading 9 + 3 + 8 data bytes + 2 CRC bytes = 22 bytes + $select=sprintf("\xF0\x88\x00"); + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 10)) { + PT_EXIT("device $owx_dev not accessible in reading"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev has returned invalid data"); + } + unless (length($response->{readdata}) == 10) { + PT_EXIT("$owx_dev has returned invalid data") + }; + OWXSWITCH_BinValues($hash,"ds2408.getstate",1,1,$owx_dev,$response->{writedata},10,$response->{readdata}); + #-- family = 3A => DS2413 + }elsif( $hash->{OW_FAMILY} eq "3A" ) { + #=============== get gpio values =============================== + #-- issue the match ROM command \x55 and the read gpio command + # \xF5 plus 2 empty bytes + #-- reading 9 + 1 + 2 data bytes = 12 bytes + $select = "\xF5"; + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 2)) { + PT_EXIT("device $owx_dev not accessible in reading"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev has returned invalid data"); + } + unless (length($response->{readdata}) == 2) { + PT_EXIT("$owx_dev has returned invalid data"); + } + OWXSWITCH_BinValues($hash,"ds2413.getstate",1,1,$owx_dev,$response->{writedata},2,$response->{readdata}); + } else { + PT_EXIT("unknown device family $hash->{OW_FAMILY}\n"); + } + PT_END; +} + +######################################################################################## +# +# OWXSWITCH_PT_SetState - Set gpio ports of device asynchronous +# +# Parameter hash = hash of device addressed +# value = integer value for device outputs +# +######################################################################################## + +sub OWXSWITCH_PT_SetState($$) { + + my ($thread,$hash,$value) = @_; + + my ($select,$res,@data); + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + #-- family = 12 => DS2406 + if( $hash->{OW_FAMILY} eq "12" ) { + #=============== set gpio values =============================== + # Writing the output state via the access channel command does + # not work contrary to documentation. Using the write status command + #-- issue the match ROM command \x55 and the read status command + # \xAA at address TA1 = \x07 TA2 = \x00 + #-- reading 9 + 3 + 1 data bytes + 2 CRC bytes = 15 bytes + + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, "\xAA\x07\x00", 3)) { + PT_EXIT("device $owx_dev not accessible in writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("state could not be set for device $owx_dev"); + } + $res = $thread->{ExecuteResponse}->{readdata}; + + #-- first step + my $stat = ord(substr($res,0,1)); + my $statneu = ( $stat & 159 ) | (($value<<5) & 96) ; + #-- call the second step + #-- issue the match ROM command \x55 and the write status command + # \x55 at address TA1 = \x07 TA2 = \x00 + #-- reading 9 + 4 + 2 data bytes = 15 bytes + $select=sprintf("\x55\x07\x00%c",$statneu); + + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 2)) { + PT_EXIT("device $owx_dev not accessible in writing"); + } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("state could not be set for device $owx_dev"); + } + $res = $thread->{ExecuteResponse}->{readdata}; + my $command = $thread->{ExecuteResponse}->{writedata}; + + #-- second step from above + @data=split(//,$res); + if( int(@data) != 2){ + PT_EXIT("state could not be set for device $owx_dev"); + } + Log 1,"invalid CRC" + if (OWX_CRC16($command,$data[0],$data[1]) == 0); + + #-- put into local buffer + $hash->{owg_val}->[0] = $value % 2; + $hash->{owg_vax}->[0] = $value % 2; + $hash->{owg_val}->[1] = int($value / 2); + $hash->{owg_vax}->[1] = int($value / 2); #-- family = 29 => DS2408 } elsif( $hash->{OW_FAMILY} eq "29" ) { @@ -1227,49 +1401,82 @@ sub OWXSWITCH_SetState($$) { #-- issue the match ROM command \x55 and the write gpio command # \x5A plus the value byte and its complement $select=sprintf("\x5A%c%c",$value,255-$value); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (OWX_Execute( $master, "setstate.ds2408", 1, $owx_dev, $select, 1, undef )) { - return undef; - } else { - return "device $owx_dev not accessible in writing"; - } - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,1); - if( $res eq 0 ){ - return "device $owx_dev not accessible in writing"; - } - OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2408.setstate",1,undef,$owx_dev,undef,undef,substr($res,12)); + + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 1)) { + PT_EXIT("device $owx_dev not accessible in writing"); } + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("state could not be set for device $owx_dev"); + } + $res = $thread->{ExecuteResponse}->{readdata}; + + @data=split(//,$res); + if (@data != 1) { + PT_EXIT("invalid data length, ".int(@data)." instead of 1 bytes"); + } + if( $data[0] ne "\xAA") { + PT_EXIT("state could not be set for device $owx_dev"); + } + #-- family = 3A => DS2413 } elsif( $hash->{OW_FAMILY} eq "3A" ) { #=============== set gpio values =============================== #-- issue the match ROM command \x55 and the write gpio command # \x5A plus the value byte and its complement $select=sprintf("\x5A%c%c",252+$value,3-$value); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (OWX_Execute( $master, "setstate.ds2413", 1, $owx_dev, $select, 1, undef )) { - return undef; - } else { - return "device $owx_dev not accessible in writing"; - } - #-- synchronous mode - }else{ - OWX_Reset($master); - $res=OWX_Complex($master,$owx_dev,$select,1); - if( $res eq 0 ){ - return "device $owx_dev not accessible in writing"; - } - OWX_Reset($master); - return OWXSWITCH_BinValues($hash,"ds2413.setstate",1,undef,$owx_dev,undef,undef,substr($res,12)); + unless (OWX_ASYNC_Execute( $master, $thread, 1, $owx_dev, $select, 1)) { + PT_EXIT("device $owx_dev not accessible in writing"); } - }else { - return "unknown device family $hash->{OW_FAMILY}\n"; + PT_WAIT_UNTIL($thread->{ExecuteResponse}); + unless ($thread->{ExecuteResponse}->{success}) { + PT_EXIT("state could not be set for device $owx_dev"); + } + $res = $thread->{ExecuteResponse}->{readdata}; + + @data=split(//,$res); + if (@data != 1) { + PT_EXIT("invalid data length, ".int(@data)." instead of 1 bytes"); + } + if( $data[0] ne "\xAA") { + PT_EXIT("state could not be set for device $owx_dev"); + } + } else { + PT_EXIT("unknown device family $hash->{OW_FAMILY}\n"); } + PT_END; +} + +sub OWXSWITCH_PT_SetOutput($$$) { + + my ($thread,$hash,$fnd,$nval) = @_; + + my ($ret,$value); + + PT_BEGIN($thread); + + $thread->{task} = PT_THREAD(\&OWXSWITCH_PT_GetState); + PT_WAIT_THREAD($thread->{task},$hash); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + $value = 0; + #-- vax or val ? + for (my $i=0;$i<$cnumber{$attr{$hash->{NAME}}{"model"}};$i++){ + $value += ($hash->{owg_vax}->[$i]<<$i) + if( $i != $fnd ); + $value += ($nval<<$i) + if( $i == $fnd ); + } + $thread->{value} = $value; + $thread->{task} = PT_THREAD(\&OWXSWITCH_PT_SetState); + PT_WAIT_THREAD($thread->{task},$hash,$thread->{value}); + $ret = $thread->{task}->PT_RETVAL(); + if ($ret) { + PT_EXIT($ret); + } + PT_END; } 1; diff --git a/fhem/FHEM/21_OWTHERM.pm b/fhem/FHEM/21_OWTHERM.pm index 89bcd2a62..927ea58b2 100755 --- a/fhem/FHEM/21_OWTHERM.pm +++ b/fhem/FHEM/21_OWTHERM.pm @@ -70,10 +70,23 @@ package main; use vars qw{%attr %defs %modules $readingFnAttributes $init_done}; use strict; use warnings; +use Time::HiRes qw( gettimeofday tv_interval usleep ); + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if (!grep(/FHEM\/lib$/,@INC)) { + foreach my $inc (grep(/FHEM$/,@INC)) { + push @INC,$inc."/lib"; + }; + }; +}; + +use ProtoThreads; +no warnings 'deprecated'; sub Log3($$$); sub AttrVal($$$); -my $owx_version="5.14"; +my $owx_version="5.16"; my %gets = ( "id" => "", @@ -132,8 +145,6 @@ sub OWTHERM_Initialize ($) { "tempConv:onkick,onread tempLow tempHigh ". "resolution:9,10,11,12 interval ". $readingFnAttributes; - #-- ASYNC this function is needed for asynchronous execution of the device reads - $hash->{AfterExecuteFn} = "OWXTHERM_BinValues"; #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer main::LoadModule("OWX"); } @@ -235,13 +246,13 @@ sub OWTHERM_Define ($$) { return "OWTHERM: Warning, no 1-Wire I/O device found for $name."; #-- if coupled, test if ASYNC or not } else { - $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; + $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; } $modules{OWTHERM}{defptr}{$id} = $hash; #-- readingsSingleUpdate($hash,"state","defined",1); - Log3 $name, 3, "OWTHERM: Device $name defined."; + Log3 $name, 3, "OWTHERM: Device $name defined."; #-- Start timer for updates InternalTimer(time()+10, "OWTHERM_GetValues", $hash, 0); @@ -277,21 +288,22 @@ sub OWTHERM_Attr(@) { RemoveInternalTimer($hash); InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWTHERM_GetValues", $hash, 1); } - last; - }; - #-- resolution modified at runtime - $key eq "resolution" and do { + last; + }; + #-- resolution modified at runtime + $key eq "resolution" and do { $hash->{owg_cf} = $value; last; - }; - #-- alarm settings modified at runtime - $key =~ m/(.*)(Low|High)/ and do { - #-- safeguard against uninitialized devices - return undef - if( $hash->{READINGS}{"state"}{VAL} eq "defined" ); - $ret = OWTHERM_Set($hash,($name,$key,$value)); - }; - $key eq "IODev" and do { + }; + #-- alarm settings modified at runtime + $key =~ m/(.*)(Low|High)/ and do { + #-- safeguard against uninitialized devices + return undef + if( $hash->{READINGS}{"state"}{VAL} eq "defined" ); + $ret = OWTHERM_Set($hash,($name,$key,$value)); + last; + }; + $key eq "IODev" and do { AssignIoPort($hash,$value); if( defined($hash->{IODev}) ) { $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; @@ -447,10 +459,14 @@ sub OWTHERM_Get($@) { } #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- not different from getting all values .. - $ret = OWXTHERM_GetValues($hash,1); - #ASYNC: NEED TO WAIT UNTIL DATA IS THERE + $ret = OWXTHERM_GetValues($hash); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXTHERM_PT_GetValues); + while ($task->PT_SCHEDULE($hash)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSTHERM_GetValues($hash); @@ -502,13 +518,21 @@ sub OWTHERM_GetValues($@) { #-- Get values according to interface type my $interface= $hash->{IODev}->{TYPE}; - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ #-- max 3 tries for(my $try=0; $try<3; $try++){ $ret = OWXTHERM_GetValues($hash); last if( !defined($ret) ); - } + } + }elsif( $interface eq "OWX_ASYNC" ){ + #-- skip, if the conversion is driven by master + unless ( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){ + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXTHERM_PT_GetValues),$hash ); + }; + $ret = GP_Catch($@) if $@; + } }elsif( $interface eq "OWServer" ){ $ret = OWFSTHERM_GetValues($hash); }else{ @@ -604,8 +628,13 @@ sub OWTHERM_InitializeDevice($) { #-- put into device #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXTHERM_SetValues($hash,$args); + }elsif( $interface eq "OWX_ASYNC" ){ + #TODO use OWX_ASYNC_Schedule instead + my $task = PT_THREAD(\&OWXTHERM_PT_SetValues); + while ($task->PT_SCHEDULE($hash,$args)) { OWX_ASYNC_Poll($hash->{IODev}); }; + $ret = $task->PT_RETVAL(); #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSTHERM_SetValues($hash,$args); @@ -694,8 +723,14 @@ sub OWTHERM_Set($@) { } #-- put into device #-- OWX interface - if( $interface =~ /^OWX/ ){ + if( $interface eq "OWX" ){ $ret = OWXTHERM_SetValues($hash,$args); + }elsif( $interface eq "OWX_ASYNC" ){ + $args->{format} = 1; + eval { + OWX_ASYNC_Schedule( $hash, PT_THREAD(\&OWXTHERM_PT_SetValues),$hash,$args ); + }; + $ret = GP_Catch($@) if $@; #-- OWFS interface }elsif( $interface eq "OWServer" ){ $ret = OWFSTHERM_SetValues($hash,$args); @@ -931,9 +966,9 @@ sub OWXTHERM_BinValues($$$$$$$$) { # ######################################################################################## -sub OWXTHERM_GetValues($@) { +sub OWXTHERM_GetValues($) { - my ($hash,$sync) = @_; + my ($hash) = @_; #-- For default, perform the conversion now my $con=1; @@ -956,38 +991,23 @@ sub OWXTHERM_GetValues($@) { #-- if the conversion has not been called before if( $con==1 ){ #-- issue the match ROM command \x55 and the start conversion command \x44 - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute($master,"ds182x.convert",1,$owx_dev,"\x44",0,$convtimes{AttrVal($name,"resolution",12)}); - #-- synchronous mode - }else{ - OWX_Reset($master); - if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){ - return "$owx_dev not accessible"; - } - #-- conversion needs some 950 ms - but we may also do it in shorter time ! - select(undef,undef,undef,$convtimes{AttrVal($name,"resolution",12)}*0.001); - } + OWX_Reset($master); + if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){ + return "$owx_dev not accessible"; + } + #-- conversion needs some 950 ms - but we may also do it in shorter time ! + select(undef,undef,undef,$convtimes{AttrVal($name,"resolution",12)}*0.001); } #-- NOW ask the specific device #-- issue the match ROM command \x55 and the read scratchpad command \xBE #-- reading 9 + 1 + 8 data bytes and 1 CRC byte = 19 bytes - my $context = "ds182x.reading"; - #-- asynchronous mode - if( $hash->{ASYNC} ){ - if (!OWX_Execute($master,$context,1,$owx_dev,"\xBE",9,undef) or ($sync and !OWX_AwaitExecuteResponse($master,$context,$owx_dev))) { - return "$owx_dev not accessible in reading"; - } - #-- synchronous mode - } else { - OWX_Reset($master); - my $res=OWX_Complex($master,$owx_dev,"\xBE",9); - return "$owx_dev not accessible in reading" - if( $res eq 0 ); - return "$owx_dev has returned invalid data" - if( length($res)!=19); - return OWXTHERM_BinValues($hash,$context,1,undef,$owx_dev,undef,undef,substr($res,10,9)); - } + OWX_Reset($master); + my $res=OWX_Complex($master,$owx_dev,"\xBE",9); + return "$owx_dev not accessible in reading" + if( $res eq 0 ); + return "$owx_dev has returned invalid data" + if( length($res)!=19); + return OWXTHERM_BinValues($hash,"ds182x.reading",1,undef,$owx_dev,undef,undef,substr($res,10,9)); return undef; } @@ -1037,21 +1057,147 @@ sub OWXTHERM_SetValues($$) { # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM my $select=sprintf("\x4E%c%c%c",$thp,$tlp,$cfg); - #-- asynchronous mode - if( $hash->{ASYNC} ){ - OWX_Execute($master,"setvalues",1,$owx_dev,$select,3,undef); - #-- synchronous mode - }else{ - OWX_Reset($master); - my $res=OWX_Complex($master,$owx_dev,$select,3); - if( $res eq 0 ){ - return "OWXTHERM: Device $owx_dev not accessible"; - } - } + OWX_Reset($master); + my $res=OWX_Complex($master,$owx_dev,$select,3); + if( $res eq 0 ){ + return "OWXTHERM: Device $owx_dev not accessible"; + } return undef; } +######################################################################################## +# +# OWXTHERM_GetValues - Trigger reading from one device +# +# Parameter hash = hash of device addressed +# +######################################################################################## + +sub OWXTHERM_PT_GetValues($@) { + + my ($thread,$hash) = @_; + + #-- For default, perform the conversion now + my $con=1; + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + + #-- hash of the busmaster + my $master = $hash->{IODev}; + my $name = $hash->{NAME}; + + PT_BEGIN($thread); + + #-- reset presence + $hash->{PRESENT} = 0; + + #-- check, if the conversion has been called before for all sensors + if( defined($attr{$name}{tempConv}) && ( $attr{$name}{tempConv} eq "onkick") ){ + $con=0; + } + + #-- if the conversion has not been called before + if( $con==1 ){ + #-- issue the match ROM command \x55 and the start conversion command \x44 + unless (OWX_ASYNC_Execute($master,$thread,1,$owx_dev,"\x44",0)) { + PT_EXIT("$owx_dev not accessible for convert"); + } + my ($seconds,$micros) = gettimeofday; + my $delay = $convtimes{AttrVal($name,"resolution",12)}; + my $len = length ($delay); #delay is millis, tv_address works with [sec,micros] + if ($len>3) { + $seconds += substr($delay,0,$len-3); + $micros += (substr ($delay,-3)*1000); + } else { + $micros += ($delay*1000); + } + $thread->{execute_delayed} = [$seconds,$micros]; + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + PT_YIELD_UNTIL(tv_interval($thread->{execute_delayed})>=0); + } + #-- NOW ask the specific device + #-- issue the match ROM command \x55 and the read scratchpad command \xBE + #-- reading 9 + 1 + 8 data bytes and 1 CRC byte = 19 bytes + unless (OWX_ASYNC_Execute($master,$thread,1,$owx_dev,"\xBE",9)) { + PT_EXIT("$owx_dev not accessible in reading"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + my $response = $thread->{ExecuteResponse}; + unless ($response->{success}) { + PT_EXIT("$owx_dev read not successful"); + } + my $res = OWXTHERM_BinValues($hash,"ds182x.reading",1,1,$owx_dev,undef,$response->{numread},$response->{readdata}); + if ($res) { + PT_EXIT($res); + } + PT_END; +} + +####################################################################################### +# +# OWXTHERM_PT_SetValues - Implements SetFn function async +# +# Parameter hash = hash of device addressed +# a = argument array +# +######################################################################################## + +sub OWXTHERM_PT_SetValues($$) { + my ($thread, $hash, $args) = @_; + + my ($i,$j,$k); + + my $name = $hash->{NAME}; + + #-- ID of the device + my $owx_dev = $hash->{ROM_ID}; + #-- hash of the busmaster + my $master = $hash->{IODev}; + + PT_BEGIN($thread); + + unless (defined $args->{resolution} or defined $args->{tempLow} or defined $args->{tempHigh}) { + PT_EXIT; + } + + #-- $owg_tl and $owg_th are preset and may be changed here + foreach my $key (keys %$args) { + $hash->{owg_tl} = $args->{$key} if( lc($key) eq "templow"); + $hash->{owg_th} = $args->{$key} if( lc($key) eq "temphigh"); + $hash->{owg_cf} = $args->{$key} if( lc($key) eq "resolution"); + } + + #-- put into 2's complement formed (signed byte) + my $tlp = $hash->{owg_tl} < 0 ? 128 - $hash->{owg_tl} : $hash->{owg_tl}; + my $thp = $hash->{owg_th} < 0 ? 128 - $hash->{owg_th} : $hash->{owg_th}; + #-- resolution is defined in bits 5+6 of configuration register + my $cfg = defined $hash->{owg_cf} ? (($hash->{owg_cf}-9) << 5) | 0x1f : 0x7f; + + #-- issue the match ROM command \x55 and the write scratchpad command \x4E, + # followed by 3 bytes of data (alarm_temp_high, alarm_temp_low, config) + # config-byte of 0x7F means 12 bit resolution (750ms convert time) + # + # so far writing the EEPROM does not work properly. + # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM + # 2. \x48 appended to match ROM => command not ok. + # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM + + my $select=sprintf("\x4E%c%c%c",$thp,$tlp,$cfg); + unless (OWX_ASYNC_Execute($master,$thread,1,$owx_dev,$select,3)) { + PT_EXIT("OWXTHERM: Device $owx_dev not accessible"); + } + PT_WAIT_UNTIL(defined $thread->{ExecuteResponse}); + + #-- process results + $hash->{PRESENT} = 1; + if ($args->{format}) { + OWTHERM_FormatValues($hash); + } + PT_END; +} + 1; =pod diff --git a/fhem/FHEM/OWX_Executor.pm b/fhem/FHEM/OWX_Executor.pm index e77ea15e8..bf1c21189 100644 --- a/fhem/FHEM/OWX_Executor.pm +++ b/fhem/FHEM/OWX_Executor.pm @@ -213,7 +213,7 @@ sub nextItem($) { }; if ($item) { if($item->{context}) { - main::Log3 $hash->{NAME},5,"OWX_Executor: item $item->{context} for $item->{address} eligible to run"; + main::Log3 $hash->{NAME},5,"OWX_Executor: item $item->{context} for ".(defined $item->{address} ? $item->{address} : "---")." eligible to run"; } else { main::Log3 $hash->{NAME},5,"OWX_Executor: command $item->{command} eligible to run"; } diff --git a/fhem/FHEM/OWX_SER.pm b/fhem/FHEM/OWX_SER.pm index d4152bc03..63865fb24 100644 --- a/fhem/FHEM/OWX_SER.pm +++ b/fhem/FHEM/OWX_SER.pm @@ -189,9 +189,9 @@ sub pt_execute($$$$$$$) { $rom_id[$i]=hex(substr($dev,2*$i,2)); } $select=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id).$data; - #-- has no match ROM part + #-- has no match ROM part, issue skip ROM command (0xCC:) } else { - $select=$data; + $select="\xCC".$data; } #-- has receive data part if( $numread >0 ){