OWX_ASYNC: refactor using protothreads

Merge branch 'owx_async_protothreads'

Conflicts:
	fhem/FHEM/21_OWTHERM.pm

git-svn-id: svn://svn.code.sf.net/p/fhem/code/trunk@5789 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
ntruchsess
2014-05-08 20:17:42 +00:00
parent b6187e046c
commit ce46678067
11 changed files with 3429 additions and 900 deletions

View File

@@ -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

512
fhem/FHEM/11_OWX_CCC.pm Normal file
View File

@@ -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 <name> OWX <cuno/coc-device>"
}
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<length($ob);$i++){
my $j=int(ord(substr($ob,$i,1))/16);
my $k=ord(substr($ob,$i,1))%16;
$res.=sprintf "0x%1x%1x ",$j,$k;
}
main::Log(3, $res);
#$numread++;
#-- 20 bytes received = leftover from match
}elsif( length($ob) == 20 ){
$numread++;
}else{
main::Log(1,"OWX_CCC::Receive: unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{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<length($data);$i++){
$j=int(ord(substr($data,$i,1))/16);
$k=ord(substr($data,$i,1))%16;
$res =sprintf "OwB%1x%1x ",$j,$k;
$res2.=sprintf "0x%1x%1x ",$j,$k;
main::CUL_SimpleWrite($hwdevice, $res);
}
main::Log(3,"OWX_CCC::Send to COC/CUNO $res2")
if( $main::owx_debug > 1);
}
1;

404
fhem/FHEM/11_OWX_DS9097.pm Normal file
View File

@@ -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; $i<length($data);$i++){
$res = $self->TouchByte_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<length($cmd);$i++){
$j=int(ord(substr($cmd,$i,1))/16);
$k=ord(substr($cmd,$i,1))%16;
$res.=sprintf "0x%1x%1x ",$j,$k;
}
main::Log3($self->{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;

View File

@@ -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<int(@owg_fixed);$i++){
$select .= sprintf "%c\xFF\xFF\xFF",int($hash->{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<int(@owg_fixed);$i++){
#if( $owg_mode[$i] eq "input" ){
if( 1 > 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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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";
}

View File

@@ -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 ){