mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
11_OWX_CCC.pm: Interfacemodul für OWX Next Generation
11_OWX_FRM.pm: Interfacemodul für OWX Next Generation 11_OWX_SER.pm: Interfacemodul für OWX Next Generation git-svn-id: https://svn.fhem.de/fhem/trunk@15347 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
b47471f723
commit
cbee7a8c5b
601
fhem/FHEM/11_OWX_CCC.pm
Normal file
601
fhem/FHEM/11_OWX_CCC.pm
Normal file
@ -0,0 +1,601 @@
|
||||
########################################################################################
|
||||
#
|
||||
# OWX_CCC.pm
|
||||
#
|
||||
# FHEM module providing hardware dependent functions for the COC/CUNO interface of OWX
|
||||
#
|
||||
# Prof. Dr. Peter A. Henning
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
########################################################################################
|
||||
#
|
||||
# Provides the following methods for OWX
|
||||
#
|
||||
# Define
|
||||
# Detect
|
||||
# Alarms
|
||||
# Complex
|
||||
# Discover
|
||||
# Init
|
||||
# Read
|
||||
# ReadLow
|
||||
# Ready
|
||||
# Reset
|
||||
# Verify
|
||||
# Write
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
package OWX_CCC;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub new($) {
|
||||
my ($class,$hash) = @_;
|
||||
|
||||
return bless {
|
||||
hash => $hash,
|
||||
#-- module version
|
||||
version => "7.01"
|
||||
}, $class;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Define - Implements Define method
|
||||
#
|
||||
# Parameter def = definition string
|
||||
#
|
||||
# Return undef if ok, otherwise error message
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Define($) {
|
||||
my ($self,$def) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
#-- check syntax
|
||||
if(int(@a) < 3){
|
||||
return "OWX_CCC::Define Syntax error - must be define <name> OWX <cuno/coc-device>"
|
||||
}
|
||||
|
||||
my $name = $a[0];
|
||||
$hash->{NAME} = $name;
|
||||
my $dev = $a[2];
|
||||
$hash->{DeviceName} = $dev;
|
||||
|
||||
|
||||
#-- Second step in case of CUNO: See if we can open it
|
||||
my $msg = "OWX_CCC::Define COC/CUNO device $dev";
|
||||
#-- hash des COC/CUNO
|
||||
my $hwdevice = $main::defs{$dev};
|
||||
if(!$hwdevice){
|
||||
main::Log3 $name,1, $msg." not defined";
|
||||
return $msg." not defined";
|
||||
}
|
||||
|
||||
main::Log(1,$msg." defined");
|
||||
|
||||
#-- store with OWX device
|
||||
$hash->{DeviceName} = $dev;
|
||||
$hash->{ASYNCHRONOUS} = 0;
|
||||
$hash->{INTERFACE} = "COC/CUNO";
|
||||
$hash->{HWDEVICE} = $hwdevice;
|
||||
|
||||
#-- loop for some time until the state is "Initialized"
|
||||
for(my $i=0;$i<6;$i++){
|
||||
last if( $hwdevice->{STATE} eq "Initialized");
|
||||
main::Log(1,"OWX_CCC::Define Waiting, at t=$i ".$dev." is still ".$hwdevice->{STATE});
|
||||
select(undef,undef,undef,3);
|
||||
}
|
||||
main::Log(1, "OWX_CCC::Define Can't open ".$dev) if( $hwdevice->{STATE} ne "Initialized");
|
||||
#-- reset the 1-Wire system in COC/CUNO
|
||||
main::CUL_SimpleWrite($hwdevice, "Oi");
|
||||
|
||||
#-- module version
|
||||
$hash->{version} = "7.0beta2";
|
||||
main::Log3 $name,1,"OWX_CCC::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
|
||||
if( $hash->{version} ne $main::owx_version);
|
||||
|
||||
#-- call low level init function for the device
|
||||
$self->Init();
|
||||
return undef;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Detect - Find out if we have the proper interface
|
||||
#
|
||||
# Return 1 if ok, otherwise 0
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Detect () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my ($ret,$ress);
|
||||
my $name = $hash->{NAME};
|
||||
my $ress0 = "OWX_CCC::Detect: 1-Wire bus $name interface ";
|
||||
$ress = $ress0;
|
||||
|
||||
#-- get the interface
|
||||
my $interface;
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
select(undef,undef,undef,2);
|
||||
#-- type of interface
|
||||
main::CUL_SimpleWrite($hwdevice, "V");
|
||||
select(undef,undef,undef,0.01);
|
||||
my ($err,$ob) = ReadLow($hwdevice);
|
||||
#my $ob = CallFn($owx_hwdevice->{NAME}, "GetFn", $owx_hwdevice, (" ", "raw", "V"));
|
||||
#-- process result for detection
|
||||
if( !defined($ob)){
|
||||
$ob="";
|
||||
$ret=0;
|
||||
#-- COC
|
||||
}elsif( $ob =~ m/.*CSM.*/){
|
||||
$interface="COC";
|
||||
$ress .= "DS2482 / COC detected in $hwdevice->{NAME}";
|
||||
$ret=1;
|
||||
#-- CUNO
|
||||
}elsif( $ob =~ m/.*CUNO.*/){
|
||||
$interface="CUNO";
|
||||
$ress .= "DS2482 / CUNO detected in $hwdevice->{NAME}";
|
||||
$ret=1;
|
||||
#-- something else
|
||||
} else {
|
||||
$ret=0;
|
||||
}
|
||||
#-- treat the failure cases
|
||||
if( $ret == 0 ){
|
||||
$interface=undef;
|
||||
$ress .= "in $hwdevice->{NAME} could not be addressed, return was $ob";
|
||||
}
|
||||
#-- store with OWX device
|
||||
$hash->{INTERFACE} = $interface;
|
||||
main::Log(1, $ress);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
|
||||
#
|
||||
# Return 0 because not implemented here.
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Alarms () {
|
||||
my ($self) = @_;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Complex - Send match ROM, data block and receive bytes as response
|
||||
#
|
||||
# Parameter dev = ROM ID of device
|
||||
# data = string to send
|
||||
# numread = number of bytes to receive
|
||||
#
|
||||
# Return response, if OK
|
||||
# 0 if not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Complex ($$$) {
|
||||
my ($self,$dev,$data,$numread) =@_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my $select;
|
||||
my $res = "";
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
#-- has match ROM part
|
||||
if( $dev ){
|
||||
#-- ID of the device
|
||||
my $owx_rnf = substr($dev,3,12);
|
||||
my $owx_f = substr($dev,0,2);
|
||||
|
||||
#-- 8 byte 1-Wire device address
|
||||
my @rom_id =(0,0,0,0 ,0,0,0,0);
|
||||
#-- from search string to reverse string id
|
||||
$dev=~s/\.//g;
|
||||
for(my $i=0;$i<8;$i++){
|
||||
$rom_id[7-$i]=substr($dev,2*$i,2);
|
||||
}
|
||||
$select=sprintf("Om%s%s%s%s%s%s%s%s",@rom_id);
|
||||
main::Log3 $name,5,"OWX_CCC::Complex: sending match ROM to COC/CUNO ".$select;
|
||||
#--
|
||||
main::CUL_SimpleWrite($hwdevice, $select);
|
||||
my ($err,$ob) = ReadLow($hwdevice);
|
||||
#-- padding first 9 bytes into result string, since we have this
|
||||
# in the serial interfaces as well
|
||||
$res .= "000000000";
|
||||
}
|
||||
#-- has data part
|
||||
if ( $data ){
|
||||
$self->Write($data,0);
|
||||
$res .= $data;
|
||||
}
|
||||
#-- has receive part
|
||||
if( $numread > 0 ){
|
||||
#$numread += length($data);
|
||||
main::Log3 $name,5,"OWX_CCC::Complex: COC/CUNO is expected to deliver $numread bytes";
|
||||
$res.=$self->Read($numread);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Discover - Discover devices on the 1-Wire bus via internal firmware
|
||||
#
|
||||
# Return 0 : error
|
||||
# 1 : OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Discover () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my $res;
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
#-- zero the array
|
||||
@{$hash->{DEVS}}=();
|
||||
#-- reset the busmaster
|
||||
$self->Init();
|
||||
#-- get the devices
|
||||
main::CUL_SimpleWrite($hwdevice, "Oc");
|
||||
select(undef,undef,undef,0.5);
|
||||
my ($err,$ob) = ReadLow($hwdevice);
|
||||
if( $ob ){
|
||||
# main::Log(3,"OWX_CCC::Discover: ".$hwdevice->{NAME}." device search returns ".$ob);
|
||||
foreach my $dx (split(/\n/,$ob)){
|
||||
next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
|
||||
$dx =~ s/\d+\://;
|
||||
my $ddx = substr($dx,14,2).".";
|
||||
#-- reverse data from culfw
|
||||
for( my $i=1;$i<7;$i++){
|
||||
$ddx .= substr($dx,14-2*$i,2);
|
||||
}
|
||||
$ddx .= ".".substr($dx,0,2);
|
||||
push (@{$hash->{DEVS}},$ddx);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
main::Log3 $name,1, "OWX_CCC::Discover No answer to device search";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Init - Low Level Init of the 1-wire device
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Init () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $dev = $hash->{DeviceName};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
#main::Log3 $name,1,"OWX_CCC::Init called on device $dev for bus $name, state is ".$hash->{STATE};
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORm"));
|
||||
#--
|
||||
# main::CUL_SimpleWrite($hwdevice, "ORm");
|
||||
# select(undef,undef,undef,0.01);
|
||||
# my ($err,$ob) = ReadLow($hwdevice);
|
||||
#main::Log3 $name,1,"OWX_CCC::Init gives ob=$ob ";
|
||||
if( !defined($ob) ){
|
||||
#main::Log 1,"empty ORm";
|
||||
return "empty ORm";
|
||||
}elsif( length($ob) < 13){
|
||||
#main::Log 1,"short ORm of length ".length($ob);
|
||||
return "short ORm of length ".length($ob);
|
||||
}elsif( substr($ob,9,4) eq "OK" ){
|
||||
#main::Log 1,"=====> OK";
|
||||
$hash->{STATE} = "opened";
|
||||
return undef;
|
||||
}else{
|
||||
#main::Log 1,"=====> ORm empty -> still OK ???";
|
||||
$hash->{STATE} = "opened";
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Read - Implement the Read function
|
||||
#
|
||||
# Parameter numexp = expected number of bytes
|
||||
#
|
||||
#######################################################################################
|
||||
|
||||
sub Read(@) {
|
||||
my ($self,$numexp) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $state = $hash->{STATE};
|
||||
my $buffer = "";
|
||||
my $numget = 0;
|
||||
my ($err,$ob);
|
||||
my $try;
|
||||
my $maxtry = $numexp;
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
for($try=0;$try<$maxtry;$try++){
|
||||
#main::Log(1, "Sending $hwdevice->{NAME}: OrB";
|
||||
#my $ob = CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "OrB"));
|
||||
main::CUL_SimpleWrite($hwdevice, "OrB");
|
||||
($err,$ob) = main::CUL_ReadAnswer($hwdevice,$name,0,undef);
|
||||
#select(undef,undef,undef,0.01);
|
||||
#($err,$ob) = ReadLow($hwdevice);
|
||||
|
||||
#-- process results
|
||||
if( !(defined($ob)) ){
|
||||
return "";
|
||||
#-- four bytes received makes one byte of result
|
||||
}elsif( length($ob) == 4 ){
|
||||
$buffer .= sprintf("%c",hex(substr($ob,0,2)));
|
||||
$numget++;
|
||||
#-- 11 bytes received makes one byte of result
|
||||
}elsif( length($ob) == 11 ){
|
||||
$buffer .= sprintf("%c",hex(substr($ob,9,2)));
|
||||
$numget++;
|
||||
#-- 18 bytes received from CUNO
|
||||
}elsif( length($ob) == 18 ){
|
||||
main::OWX_WDBGL($name,4,"OWX_CCC::Read 18 bytes from CUNO: ",$ob);
|
||||
#-- 20 bytes received = leftover from match
|
||||
}elsif( length($ob) == 20 ){
|
||||
$maxtry++;
|
||||
}else{
|
||||
main::Log3 $name,1,"OWX_CCC::Read unexpected number of ".length($ob)." bytes on bus ".$hwdevice->{NAME};
|
||||
}
|
||||
}
|
||||
if( $numget >= $numexp){
|
||||
main::OWX_WDBGL($name,1,"OWX_CCC::Read from CUNO with error=$err: ",$buffer);
|
||||
return $buffer
|
||||
#-- ultimate failure
|
||||
}else{
|
||||
main::Log3 $name, 1,"OWX_CCC::Read $name: $numget of $numexp bytes with error=$err at state $state, this is an unrecoverable error";
|
||||
#main::DevIo_Disconnected($hwdevice);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# ReadLow - Replacement for CUL_ReadAnswer for better control
|
||||
#
|
||||
# Parameter: hash = hash of bus master
|
||||
#
|
||||
# Return: string received
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub ReadLow($)
|
||||
{
|
||||
my ($hwdevice) = @_;
|
||||
|
||||
my $type = $hwdevice->{TYPE};
|
||||
my $name = $hwdevice->{NAME};
|
||||
|
||||
my $arg ="";
|
||||
my $anydata=0;
|
||||
my $regexp =undef;
|
||||
|
||||
my ($mculdata, $rin) = ("", '');
|
||||
my $buf;
|
||||
my $to = 3; # 3 seconds timeout
|
||||
$to = $hwdevice->{RA_Timeout} if($hwdevice->{RA_Timeout}); # ...or less
|
||||
for(;;) {
|
||||
return ("Device lost when reading answer for get $arg", undef)
|
||||
if(!$hwdevice->{FD});
|
||||
|
||||
vec($rin, $hwdevice->{FD}, 1) = 1;
|
||||
my $nfound = select($rin, undef, undef, $to);
|
||||
if($nfound < 0) {
|
||||
next if ($! == EAGAIN() || $! == EINTR() || $! == 0);
|
||||
my $err = $!;
|
||||
#main::DevIo_Disconnected($hwdevice);
|
||||
main::Log 1,"============================> DISCOINNECTING";
|
||||
return("ReadLow $arg: $err", undef);
|
||||
}
|
||||
return ("Timeout reading answer for get $arg", undef)
|
||||
if($nfound == 0);
|
||||
$buf = main::DevIo_SimpleRead($hwdevice);
|
||||
return ("No data", undef) if(!defined($buf));
|
||||
|
||||
if($buf) {
|
||||
main::Log3 $name,5, "OWX_CCC::ReadLow $buf";
|
||||
$mculdata .= $buf;
|
||||
}
|
||||
|
||||
# \n\n is socat special
|
||||
if($mculdata =~ m/\r\n/ || $anydata || $mculdata =~ m/\n\n/ ) {
|
||||
if($regexp && $mculdata !~ m/$regexp/) {
|
||||
main::CUL_Parse($hwdevice, $hwdevice, $hwdevice->{NAME}, $mculdata, $hwdevice->{initString});
|
||||
} else {
|
||||
return (undef, $mculdata)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Ready - Implement the Ready function
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Ready () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $success;
|
||||
|
||||
$success = main::DevIo_OpenDev($hash,1,"main::OWX_Init")
|
||||
if($hash->{STATE} eq "disconnected");
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Reset - Reset the 1-Wire bus
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Reset () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
my $ob = main::CallFn($hwdevice->{NAME}, "GetFn", $hwdevice, (" ", "raw", "ORb"));
|
||||
|
||||
if( substr($ob,9,4) eq "OK:1" ){
|
||||
return 1;
|
||||
}else{
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Verify - Verify a particular device on the 1-Wire bus
|
||||
#
|
||||
# Parameter dev = 8 Byte ROM ID of device to be tested
|
||||
#
|
||||
# Return 1 : device found
|
||||
# 0 : device not
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Verify ($) {
|
||||
my ($self,$dev) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my $i;
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
#-- Ask the COC/CUNO
|
||||
main::CUL_SimpleWrite($hwdevice, "OCf");
|
||||
#-- sleeping for some time
|
||||
select(undef,undef,undef,3);
|
||||
main::CUL_SimpleWrite($hwdevice, "Oc");
|
||||
select(undef,undef,undef,0.5);
|
||||
my ($err,$ob) = $self->($hwdevice);
|
||||
if( $ob ){
|
||||
foreach my $dx (split(/\n/,$ob)){
|
||||
next if ($dx !~ /^\d\d?\:[0-9a-fA-F]{16}/);
|
||||
$dx =~ s/\d+\://;
|
||||
my $ddx = substr($dx,14,2).".";
|
||||
#-- reverse data from culfw
|
||||
for( my $i=1;$i<7;$i++){
|
||||
$ddx .= substr($dx,14-2*$i,2);
|
||||
}
|
||||
$ddx .= ".".substr($dx,0,2);
|
||||
return 1 if( $dev eq $ddx);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Write - Implement the write function
|
||||
#
|
||||
# Parameter cmd = string to be sent
|
||||
# reset = 1 if initial bus reset has to be done
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Write(@) {
|
||||
my ($self,$cmd, $reset) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $count_out;
|
||||
|
||||
if($hash->{STATE} eq "disconnected"){
|
||||
main::Log3 $name,4,"OWX_CCC::Write attempted to disconnected device $name";
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-- if necessary, perform a reset operation
|
||||
$self->Reset()
|
||||
if( $reset );
|
||||
|
||||
my ($i,$j,$k);
|
||||
my $res = "";
|
||||
|
||||
#-- get the interface
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
for( $i=0;$i<length($cmd);$i++){
|
||||
$j = int(ord(substr($cmd,$i,1))/16);
|
||||
$k = ord(substr($cmd,$i,1))%16;
|
||||
$res = sprintf "OwB%1x%1x ",$j,$k;
|
||||
main::CUL_SimpleWrite($hwdevice, $res);
|
||||
}
|
||||
main::OWX_WDBGL($name,5,"OWX_CCC::Write Sending out ",$cmd);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=item helper
|
||||
=item summary to address an OWX interface device via COC/CUNO
|
||||
=item summary_DE zur Adressierung eines OWX Interface Device mit COC/CUNO
|
||||
=begin html
|
||||
|
||||
<a name="OWX_CCC"></a>
|
||||
<h3>OWX_CCC</h3>
|
||||
See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="OWX_CCC"></a>
|
||||
<h3>OWX_CCC</h3>
|
||||
<a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
=end html_DE
|
635
fhem/FHEM/11_OWX_FRM.pm
Normal file
635
fhem/FHEM/11_OWX_FRM.pm
Normal file
@ -0,0 +1,635 @@
|
||||
########################################################################################
|
||||
#
|
||||
# OWX_FRM.pm
|
||||
#
|
||||
# FHEM module providing hardware dependent functions for the FRM interface of OWX
|
||||
#
|
||||
# Prof. Dr. Peter A. Henning
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
########################################################################################
|
||||
#
|
||||
# Provides the following methods for OWX
|
||||
#
|
||||
# Define
|
||||
# Detect
|
||||
# Alarms
|
||||
# Complex
|
||||
# Discover
|
||||
# Read
|
||||
# Ready
|
||||
# Verify
|
||||
# Write
|
||||
# observer
|
||||
# device_to_firmata
|
||||
# firmata_to_device
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
package OWX_FRM;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Data::Dumper;
|
||||
|
||||
use Device::Firmata::Constants qw/ :all /;
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub new($) {
|
||||
my ($class,$hash) = @_;
|
||||
|
||||
return bless {
|
||||
hash => $hash
|
||||
}, $class;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Define - Implements Define method
|
||||
#
|
||||
# Parameter def = definition string
|
||||
#
|
||||
# Return undef if ok, otherwise error message
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Define($) {
|
||||
my ($self,$def) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
if (!defined($main::modules{FRM})) {
|
||||
my $ret = "OWX_FRM::Define module FRM not yet loaded, please define an FRM device first.";
|
||||
main::Log3 $hash->{NAME},1,$ret;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
my @a = split( "[ \t][ \t]*", $def );
|
||||
my $u = "wrong syntax: define <name> FRM_XXX pin";
|
||||
return $u unless int(@a) > 0;
|
||||
|
||||
$self->{pin} = $a[2];
|
||||
$self->{id} = 0;
|
||||
$self->{name} = $hash->{NAME};
|
||||
$self->{hash} = $hash;
|
||||
|
||||
#-- when the specified device name contains @<digits>, remove these.
|
||||
#my $dev =~ s/\@\d*//;
|
||||
#main::AssignIoPort($hash);
|
||||
|
||||
#-- store with OWX device
|
||||
#$hash->{DeviceName} = $dev;
|
||||
$hash->{INTERFACE} = "firmata";
|
||||
$hash->{HWDEVICE} = $a[2];
|
||||
$hash->{ASYNCHRONOUS} = 0;
|
||||
|
||||
#-- module version
|
||||
$hash->{version} = "7.01";
|
||||
main::Log3 $hash->{NAME},1,"OWX_FRM::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
|
||||
if( $hash->{version} ne $main::owx_version);
|
||||
|
||||
#-- call low level init function for the device
|
||||
main::InternalTimer(time()+55, "OWX_FRM::Init", $self,0);
|
||||
return undef;
|
||||
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Detect - Find out if we have the proper interface
|
||||
#
|
||||
# Return 1 if ok, otherwise 0
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Detect () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my $ret;
|
||||
my $name = $hash->{NAME};
|
||||
my $ress = "OWX: 1-Wire bus $name: interface ";
|
||||
|
||||
my $iodev = $hash->{IODev};
|
||||
if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
|
||||
$ret=1;
|
||||
$ress .= "Firmata detected in $iodev->{NAME}";
|
||||
} else {
|
||||
$ret=0;
|
||||
$ress .= defined $iodev ? "$iodev->{NAME} is not connected to Firmata" : "not associated to any FRM device";
|
||||
}
|
||||
main::Log(1, $ress);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
|
||||
#
|
||||
# Return number of alarmed devices
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Alarms() {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
#-- get the interface
|
||||
my $frm = $hash->{IODev};
|
||||
return 0 unless defined $frm;
|
||||
my $firmata = $frm->{FirmataDevice};
|
||||
my $pin = $hash->{PIN};
|
||||
return 0 unless ( defined $firmata and defined $pin );
|
||||
$hash->{ALARMDEVS} = undef;
|
||||
$firmata->onewire_search_alarms($hash->{PIN});
|
||||
my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
|
||||
for (my $i=0;$i<$times;$i++) {
|
||||
if (main::FRM_poll($hash->{IODev})) {
|
||||
if (defined $hash->{ALARMDEVS}) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
select (undef,undef,undef,0.05);
|
||||
}
|
||||
}
|
||||
$hash->{ALARMDEVS} = [];
|
||||
return 1;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Init - Initialize the 1-wire device
|
||||
#
|
||||
# Parameter hash = hash of bus master
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Init() {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $dev = $hash->{DeviceName};
|
||||
my $name = $hash->{NAME};
|
||||
my $msg;
|
||||
|
||||
#FRM_OWX_Init($hash,$pin);
|
||||
|
||||
main::Log 1,"==================> STARTING INIT of 11_OWX_FRM";
|
||||
|
||||
my @args = (9);
|
||||
$hash->{PIN} = 9;
|
||||
|
||||
my $ret = main::FRM_Init_Pin_Client($hash,\@args,PIN_ONEWIRE);
|
||||
if (defined $ret){
|
||||
$msg = "Error ".$ret;
|
||||
main::Log3 $name,1,"OWX_FRM::Init ".$msg;
|
||||
return $msg;
|
||||
}
|
||||
|
||||
my $firmata = main::FRM_Client_FirmataDevice($hash);
|
||||
|
||||
my $pin = $hash->{PIN};
|
||||
$hash->{FRM_OWX_CORRELATIONID} = 0;
|
||||
$firmata->observe_onewire($pin,\&observer,$hash);
|
||||
|
||||
$hash->{FRM_OWX_REPLIES} = {};
|
||||
$hash->{DEVS} = [];
|
||||
if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
|
||||
$firmata->onewire_config($pin,1);
|
||||
}
|
||||
|
||||
$hash->{STATE}="Initialized";
|
||||
main::InternalTimer(main::gettimeofday()+10, "OWX_Discover", $hash,0);
|
||||
return undef;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Complex - Send match ROM, data block and receive bytes as response
|
||||
#
|
||||
# Parameter hash = hash of bus master,
|
||||
# owx_dev = ROM ID of device
|
||||
# data = string to send
|
||||
# numread = number of bytes to receive
|
||||
#
|
||||
# Return response, if OK
|
||||
# 0 if not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Complex ($$$$) {
|
||||
my ($self,$owx_dev,$data,$numread) =@_;
|
||||
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $res = "";
|
||||
|
||||
#-- get the interface
|
||||
my $frm = $hash->{IODev};
|
||||
return 0 unless defined $frm;
|
||||
my $firmata = $frm->{FirmataDevice};
|
||||
my $pin = $hash->{PIN};
|
||||
return 0 unless ( defined $firmata and defined $pin );
|
||||
|
||||
my $ow_command = {};
|
||||
|
||||
#-- has match ROM part
|
||||
if ($owx_dev) {
|
||||
$ow_command->{"select"} = device_to_firmata($owx_dev);
|
||||
|
||||
#-- padding first 9 bytes into result string, since we have this
|
||||
# in the serial interfaces as well
|
||||
$res .= "000000000";
|
||||
}
|
||||
|
||||
#-- has data part
|
||||
if ($data) {
|
||||
my @data = unpack "C*", $data;
|
||||
$ow_command->{"write"} = \@data;
|
||||
$res.=$data;
|
||||
}
|
||||
|
||||
#-- has receive part
|
||||
if ( $numread > 0 ) {
|
||||
$ow_command->{"read"} = $numread;
|
||||
#Firmata sends 0-address on read after skip
|
||||
$owx_dev = '00.000000000000.00' unless defined $owx_dev;
|
||||
my $id = $hash->{FRM_OWX_CORRELATIONID};
|
||||
$ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
|
||||
$hash->{FRM_OWX_REQUESTS}->{$id} = {
|
||||
command => $ow_command,
|
||||
device => $owx_dev
|
||||
};
|
||||
delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
|
||||
$hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
|
||||
}
|
||||
|
||||
$firmata->onewire_command_series( $pin, $ow_command );
|
||||
|
||||
if ($numread) {
|
||||
my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
|
||||
for (my $i=0;$i<$times;$i++) {
|
||||
if (main::FRM_poll($hash->{IODev})) {
|
||||
if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) {
|
||||
$res .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
|
||||
main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving inside loop no. $i ",$res);
|
||||
return $res;
|
||||
}
|
||||
} else {
|
||||
select (undef,undef,undef,0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main::OWX_WDBGL($name,5,"OWX_FRM::Complex receiving outside loop ",$res);
|
||||
return $res;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Discover - Discover devices on the 1-Wire bus via internal firmware
|
||||
#
|
||||
# Parameter hash = hash of bus master
|
||||
#
|
||||
# Return 0 : error
|
||||
# 1 : OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Discover ($) {
|
||||
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
#main::Log 1,"======================> FRM Discover called";
|
||||
|
||||
#-- get the interface
|
||||
my $frm = $hash->{IODev};
|
||||
return 0 unless defined $frm;
|
||||
my $firmata = $frm->{FirmataDevice};
|
||||
my $pin = $hash->{PIN};
|
||||
return 0 unless ( defined $firmata and defined $pin );
|
||||
my $old_devices = $hash->{DEVS};
|
||||
$hash->{DEVS} = undef;
|
||||
my $res = $firmata->onewire_search($hash->{PIN});
|
||||
#main::Log 1,"=============> result from search is $res, iodev is ".$hash->{IODev};
|
||||
my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
|
||||
#main::Log 1,"===========> olddevices = $old_devices, tries =$times";
|
||||
for (my $i=0;$i<$times;$i++) {
|
||||
if (main::FRM_poll($hash->{IODev})) {
|
||||
if (defined $hash->{DEVS}) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
select (undef,undef,undef,0.05);
|
||||
}
|
||||
}
|
||||
#main::Log 1,"===========> olddevices restored";
|
||||
$hash->{DEVS} = $old_devices;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Read - Implement the Read function
|
||||
#
|
||||
# Parameter numexp = expected number of bytes
|
||||
#
|
||||
#######################################################################################
|
||||
|
||||
sub Read(@) {
|
||||
my ($self,$numexp) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $buffer = $hash->{PREBUFFER};
|
||||
|
||||
my $owx_dev = $hash->{FRM_OWX_CURRDEV};
|
||||
|
||||
my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
|
||||
|
||||
#-- first read
|
||||
$buffer .= $hash->{FRM_OWX_REPLIES}->{$owx_dev};
|
||||
main::OWX_WDBGL($name,5,"OWX_FRM::Read receiving in first read ",$buffer);
|
||||
return $buffer;
|
||||
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Ready - Implement the Ready function
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Ready () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $success= 0;
|
||||
|
||||
main::Log3 $name,1,"OWX_FRM::Ready function called for bus $name. STATE=".$hash->{STATE};
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Reset - Reset the 1-Wire bus
|
||||
#
|
||||
# Parameter hash = hash of bus master
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Reset() {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
#-- get the interface
|
||||
my $frm = $hash->{IODev};
|
||||
return undef unless defined $frm;
|
||||
my $firmata = $frm->{FirmataDevice};
|
||||
my $pin = $hash->{PIN};
|
||||
return undef unless ( defined $firmata and defined $pin );
|
||||
|
||||
$firmata->onewire_reset($pin);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Verify - Verify a particular device on the 1-Wire bus
|
||||
#
|
||||
# Parameter hash = hash of bus master, dev = 8 Byte ROM ID of device to be tested
|
||||
#
|
||||
# Return 1 : device found
|
||||
# 0 : device not
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Verify($) {
|
||||
my ($self,$dev) = @_;
|
||||
my $hash = $self->{hash};
|
||||
foreach my $found ($hash->{DEVS}) {
|
||||
if ($dev eq $found) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Write - Implement the write function
|
||||
#
|
||||
#
|
||||
# Parameter cmd = string to be sent
|
||||
# reset = 1 if initial bus reset has to be done
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Write(@) {
|
||||
my ($self,$cmd, $reset) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $res = "";
|
||||
|
||||
#-- get the interface
|
||||
my $frm = $hash->{IODev};
|
||||
unless(defined $frm){
|
||||
main::Log3 $name,1,"OWX_FRM::Write attempted to undefined device $name";
|
||||
return 0
|
||||
}
|
||||
|
||||
my $firmata = $frm->{FirmataDevice};
|
||||
my $pin = $hash->{PIN};
|
||||
unless ( defined $firmata and defined $pin ){
|
||||
main::Log3 $name,1,"OWX_FRM::Write attempted to ill-defined device $name";
|
||||
return 0
|
||||
}
|
||||
|
||||
#-- if necessary, perform a reset operation
|
||||
$self->Reset()
|
||||
if( $reset );
|
||||
|
||||
main::OWX_WDBGL($name,5,"OWX_FRM::Write Sending out ",$cmd);
|
||||
|
||||
my $cmd2 = $cmd;
|
||||
my $owx_dev ="";
|
||||
my $ow_command = {};
|
||||
|
||||
#-- take away trailing 0xFF
|
||||
my $takeoff=0;
|
||||
my $tchar;
|
||||
for( my $i=length($cmd)-1; $i>=0; $i--){
|
||||
$tchar = substr($cmd,$i,1);
|
||||
if( ord($tchar) == 0xff ){
|
||||
$takeoff++;
|
||||
}else{
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
$cmd2 = substr($cmd,0,length($cmd)-$takeoff);
|
||||
|
||||
#-- has match ROM part - need to extract this
|
||||
$tchar = substr($cmd2,0,1);
|
||||
if( ord($tchar) == 0x55 ){
|
||||
#-- ID of the device. Careful, because hash is the hash of busmaster
|
||||
for(my $i=0;$i<8;$i++){
|
||||
my $j=int(ord(substr($cmd2,$i+1,1))/16);
|
||||
my $k=ord(substr($cmd,$i+1,1))%16;
|
||||
$owx_dev.=sprintf "%1x%1x",$j,$k;
|
||||
$owx_dev.="."
|
||||
if($i==0 || $i==6);
|
||||
}
|
||||
$owx_dev=uc($owx_dev);
|
||||
$cmd2 = substr($cmd2,9);
|
||||
$ow_command->{"select"} = device_to_firmata($owx_dev);
|
||||
|
||||
#-- padding first 9 bytes into result string, since we have this
|
||||
# in the serial interfaces as well
|
||||
$res .= "000000000";
|
||||
}
|
||||
|
||||
#-- has data part
|
||||
if ($cmd2) {
|
||||
my @data = unpack "C*", $cmd2;
|
||||
$ow_command->{"write"} = \@data;
|
||||
$res.=$cmd2;
|
||||
}
|
||||
|
||||
#-- pre-content of result buffer
|
||||
$hash->{PREBUFFER} = $res;
|
||||
|
||||
#-- always receive part ??
|
||||
# if ( $numread > 0 ) {
|
||||
$ow_command->{"read"} = length($cmd);
|
||||
#Firmata sends 0-address on read after skip
|
||||
$owx_dev = '00.000000000000.00' unless defined $owx_dev;
|
||||
my $id = $hash->{FRM_OWX_CORRELATIONID};
|
||||
$ow_command->{"id"} = $hash->{FRM_OWX_CORRELATIONID};
|
||||
$hash->{FRM_OWX_REQUESTS}->{$id} = {
|
||||
command => $ow_command,
|
||||
device => $owx_dev
|
||||
};
|
||||
delete $hash->{FRM_OWX_REPLIES}->{$owx_dev};
|
||||
$hash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
|
||||
#}
|
||||
|
||||
$firmata->onewire_command_series( $pin, $ow_command );
|
||||
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# observer function for listening to the FRM device
|
||||
#
|
||||
#######################################################################################
|
||||
|
||||
sub observer
|
||||
{
|
||||
my ( $data,$hash ) = @_;
|
||||
my $command = $data->{command};
|
||||
COMMAND_HANDLER: {
|
||||
$command eq "READ_REPLY" and do {
|
||||
my $id = $data->{id};
|
||||
my $request = (defined $id) ? $hash->{FRM_OWX_REQUESTS}->{$id} : undef;
|
||||
unless (defined $request) {
|
||||
return unless (defined $data->{device});
|
||||
my $owx_device = firmata_to_device($data->{device});
|
||||
my %requests = %{$hash->{FRM_OWX_REQUESTS}};
|
||||
foreach my $key (keys %requests) {
|
||||
if ($requests{$key}->{device} eq $owx_device) {
|
||||
$request = $requests{$key};
|
||||
$id = $key;
|
||||
last;
|
||||
};
|
||||
};
|
||||
};
|
||||
return unless (defined $request);
|
||||
my $owx_data = pack "C*",@{$data->{data}};
|
||||
my $owx_device = $request->{device};
|
||||
$hash->{FRM_OWX_REPLIES}->{$owx_device} = $owx_data;
|
||||
##
|
||||
$hash->{FRM_OWX_CURRDEV} = $owx_device;
|
||||
delete $hash->{FRM_OWX_REQUESTS}->{$id};
|
||||
|
||||
return main::OWX_Read($hash);
|
||||
|
||||
};
|
||||
($command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY") and do {
|
||||
my @owx_devices = ();
|
||||
foreach my $device (@{$data->{devices}}) {
|
||||
push @owx_devices, firmata_to_device($device);
|
||||
}
|
||||
if ($command eq "SEARCH_REPLY") {
|
||||
$hash->{DEVS} = \@owx_devices;
|
||||
#$main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices;
|
||||
} else {
|
||||
$hash->{ALARMDEVS} = \@owx_devices;
|
||||
}
|
||||
last;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# translation of strings
|
||||
#
|
||||
#######################################################################################
|
||||
|
||||
sub device_to_firmata
|
||||
{
|
||||
my @device;
|
||||
foreach my $hbyte ( unpack "A2xA2A2A2A2A2A2xA2", shift ) {
|
||||
push @device, hex $hbyte;
|
||||
}
|
||||
return {
|
||||
family => shift @device,
|
||||
crc => pop @device,
|
||||
identity => \@device,
|
||||
}
|
||||
}
|
||||
|
||||
sub firmata_to_device
|
||||
{
|
||||
my $device = shift;
|
||||
return sprintf( "%02X.%02X%02X%02X%02X%02X%02X.%02X", $device->{family}, @{ $device->{identity} }, $device->{crc} );
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
=pod
|
||||
=item helper
|
||||
=item summary to address an OWX interface device via Arduino Firmata
|
||||
=item summary_DE zur Adressierung eines OWX Interface Device mit Arduino Firmata
|
||||
=begin html
|
||||
|
||||
<a name="OWX_FRM"></a>
|
||||
<h3>OWX_FRM</h3>
|
||||
See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="OWX_FRM"></a>
|
||||
<h3>OWX_FRM</h3>
|
||||
<a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
=end html_DE
|
881
fhem/FHEM/11_OWX_SER.pm
Normal file
881
fhem/FHEM/11_OWX_SER.pm
Normal file
@ -0,0 +1,881 @@
|
||||
########################################################################################
|
||||
#
|
||||
# OWX_SER.pm
|
||||
#
|
||||
# FHEM module providing hardware dependent functions for the serial (USB) interface of OWX
|
||||
#
|
||||
# Prof. Dr. Peter A. Henning
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
########################################################################################
|
||||
#
|
||||
# Provides the following methods for OWX
|
||||
#
|
||||
# Define
|
||||
# Detect
|
||||
# Alarms
|
||||
# Complex
|
||||
# Discover
|
||||
# Init
|
||||
# Read
|
||||
# Ready
|
||||
# Reset
|
||||
# Verify
|
||||
# Write
|
||||
# Query
|
||||
# First
|
||||
# Next
|
||||
# Search
|
||||
# SearchLow
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
package OWX_SER;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use DevIo;
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Constructor
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub new($) {
|
||||
my ($class,$hash) = @_;
|
||||
|
||||
return bless {
|
||||
#-- OWX device
|
||||
hash => $hash,
|
||||
#-- baud rate serial interface
|
||||
baud => 9600,
|
||||
#-- 16 byte search string
|
||||
search => [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0],
|
||||
ROM_ID => [0,0,0,0 ,0,0,0,0],
|
||||
#-- search state for 1-Wire bus search
|
||||
LastDiscrepancy => 0,
|
||||
LastFamilyDiscrepancy => 0,
|
||||
LastDeviceFlag => 0,
|
||||
}, $class;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Define - Implements Define method
|
||||
#
|
||||
# Parameter def = definition string
|
||||
#
|
||||
# Return undef if ok, otherwise error message
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Define ($) {
|
||||
my ($self,$def) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
my $dev = $a[2];
|
||||
|
||||
#-- when the specified device name contains @<digits>, remove these.
|
||||
$dev =~ s/\@\d*//;
|
||||
|
||||
#-- store with OWX device
|
||||
$hash->{DeviceName} = $dev;
|
||||
$hash->{INTERFACE} = "serial";
|
||||
$hash->{ASYNCHRONOUS} = 0;
|
||||
|
||||
#-- module version
|
||||
$hash->{version} = "7.01";
|
||||
main::Log3 $hash->{NAME},1,"OWX_SER::Define warning: version ".$hash->{version}." not identical to OWX version ".$main::owx_version
|
||||
if( $hash->{version} ne $main::owx_version);
|
||||
|
||||
#-- call low level init function for the device
|
||||
$self->Init();
|
||||
return undef;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Detect - Find out if we have the proper interface
|
||||
#
|
||||
# Return 1 if ok, otherwise 0
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Detect () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my ($i,$j,$k,$l,$res,$ret,$ress);
|
||||
my $name = $hash->{NAME};
|
||||
my $ress0 = "OWX_SER::Detect 1-Wire bus $name: interface ";
|
||||
$ress = $ress0;
|
||||
|
||||
my $interface;
|
||||
|
||||
#-- timing byte for DS2480
|
||||
$self->Query("\xC1",1);
|
||||
|
||||
#-- Max 4 tries to detect an interface
|
||||
for($l=0;$l<4;$l++) {
|
||||
#-- write 1-Wire bus (Fig. 2 of Maxim AN192)
|
||||
$res = $self->Query("\x17\x45\x5B\x0F\x91",5);
|
||||
|
||||
#-- process 4/5-byte string for detection
|
||||
if( !defined($res)){
|
||||
$res="";
|
||||
$ret=0;
|
||||
}elsif( ($res eq "\x16\x44\x5A\x00") || ($res eq "\x16\x44\x5A\x00\x90") || ($res eq "\x16\x44\x5A\x00\x93")){
|
||||
$ress .= "master DS2480 detected for the first time";
|
||||
$interface="DS2480";
|
||||
$ret=1;
|
||||
} elsif( ($res eq "\x17\x45\x5B\x0F\x91") || ($res eq "\x17\x45\x1B\x0F\x91")){
|
||||
$ress .= "master DS2480 re-detected";
|
||||
$interface="DS2480";
|
||||
$ret=1;
|
||||
} else {
|
||||
$ret=0;
|
||||
}
|
||||
last
|
||||
if( $ret==1 );
|
||||
main::OWX_WDBGL($name,4,$ress."not found, answer was ",$res);
|
||||
$ress = $ress0;
|
||||
}
|
||||
if( $ret == 0 ){
|
||||
$interface=undef;
|
||||
main::OWX_WDBGL($name,4,$ress."not detected, answer was ",$res);
|
||||
} else {
|
||||
main::OWX_WDBGL($name,3,$ress,undef);
|
||||
}
|
||||
$hash->{INTERFACE} = $interface;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
|
||||
#
|
||||
# Return number of alarmed devices => DOES NOT WORK PROPERLY, WHY ????
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Alarms () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
#-- Discover all alarmed devices on the 1-Wire bus
|
||||
my $res = $self->First("alarm");
|
||||
while( $self->{LastDeviceFlag}==0 && $res != 0){
|
||||
$res = $res & $self->SER_Next("alarm");
|
||||
}
|
||||
if( $hash->{ALARMDEVS} ) {
|
||||
main::Log3 $name, 1, " Alarms = ".join(' ',@{$hash->{ALARMDEVS}});
|
||||
return( int(@{$hash->{ALARMDEVS}}) );
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Complex - Send match ROM, data block and receive bytes as response
|
||||
#
|
||||
# Parameter hash = hash of bus master,
|
||||
# owx_dev = ROM ID of device
|
||||
# data = string to send
|
||||
# numread = number of bytes to receive
|
||||
#
|
||||
# Return response, if OK
|
||||
# 0 if not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Complex ($$$$) {
|
||||
my ($self,$dev,$data,$numread) =@_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
my $select;
|
||||
my $res;
|
||||
|
||||
#-- get the interface
|
||||
my $interface = $hash->{INTERFACE};
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
#-- has match ROM part
|
||||
if( $dev ){
|
||||
#-- ID of the device
|
||||
my $owx_rnf = substr($dev,3,12);
|
||||
my $owx_f = substr($dev,0,2);
|
||||
|
||||
#-- 8 byte 1-Wire device address
|
||||
my @rom_id =(0,0,0,0,0,0,0,0);
|
||||
#-- from search string to byte id
|
||||
$dev=~s/\.//g;
|
||||
for(my $i=0;$i<8;$i++){
|
||||
$rom_id[$i]=hex(substr($dev,2*$i,2));
|
||||
}
|
||||
$select=sprintf("\x55%c%c%c%c%c%c%c%c",@rom_id).$data;
|
||||
#-- has no match ROM part
|
||||
} else {
|
||||
$select=$data;
|
||||
}
|
||||
#-- has receive data part
|
||||
if( $numread >0 ){
|
||||
#$numread += length($data);
|
||||
for( my $i=0;$i<$numread;$i++){
|
||||
$select .= "\xFF";
|
||||
};
|
||||
}
|
||||
|
||||
main::OWX_WDBGL($name,5,"OWX_SER::Complex sending ",$select);
|
||||
|
||||
#-- send data block (Fig. 6 of Maxim AN192)
|
||||
my $data2="";
|
||||
my $retlen = length($select);
|
||||
|
||||
#-- if necessary, prepend E1 character for data mode
|
||||
if( substr($select,0,1) ne '\xE1') {
|
||||
$data2 = "\xE1";
|
||||
}
|
||||
#-- all E3 characters have to be duplicated
|
||||
for(my $i=0;$i<length($select);$i++){
|
||||
my $newchar = substr($select,$i,1);
|
||||
$data2=$data2.$newchar;
|
||||
if( $newchar eq '\xE3'){
|
||||
$data2=$data2.$newchar;
|
||||
}
|
||||
}
|
||||
#-- write 1-Wire bus as a single string
|
||||
$res =$self->Query($data2,$retlen);
|
||||
main::OWX_WDBGL($name,5,"OWX_SER::Complex receiving ",$res);
|
||||
return $res
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Discover - Find devices on the 1-Wire bus
|
||||
#
|
||||
# Return 1, if alarmed devices found, 0 otherwise.
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Discover () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
#-- zero the array
|
||||
@{$hash->{DEVS}}=();
|
||||
#-- Discover all devices on the 1-Wire bus
|
||||
my $res = $self->First("discover");
|
||||
while( $self->{LastDeviceFlag}==0 && $res!=0 ){
|
||||
$res = $res & $self->Next("discover");
|
||||
}
|
||||
return( @{$hash->{DEVS}} == 0);
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Init - Initialize the 1-wire device
|
||||
#
|
||||
# Return undef if ok
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Init() {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $dev = $hash->{DeviceName};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
main::Log3 $name,5,"OWX_SER::Init called on device $dev for bus $name, state is ".$hash->{STATE};
|
||||
|
||||
#if($hash->{STATE} ne "opened"){
|
||||
#XXX
|
||||
#main::DevIo_CloseDev($hash);
|
||||
main::DevIo_OpenDev($hash,0,undef);
|
||||
#}
|
||||
my $hwdevice = $hash->{USBDev};
|
||||
|
||||
if( !($hwdevice)){
|
||||
main::Log3 $name,1, "OWX_SER: Can't open serial device $dev: $!";
|
||||
}else{
|
||||
main::Log3 $name,3, "OWX_SER: opened serial device $dev: $!";
|
||||
$hwdevice->reset_error();
|
||||
$hwdevice->baudrate(9600);
|
||||
$hwdevice->databits(8);
|
||||
$hwdevice->parity('none');
|
||||
$hwdevice->stopbits(1);
|
||||
$hwdevice->handshake('none');
|
||||
$hwdevice->write_settings;
|
||||
#-- store with OWX device
|
||||
$hash->{HWDEVICE} = $hwdevice;
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Read - Implement the Read function
|
||||
#
|
||||
# Parameter numexp = expected number of bytes
|
||||
#
|
||||
#######################################################################################
|
||||
|
||||
sub Read(@) {
|
||||
my ($self,$numexp) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $buffer = "";
|
||||
|
||||
#-- first try to read things
|
||||
$buffer = main::DevIo_SimpleRead($hash);
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Ready - Implement the Ready function
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Ready () {
|
||||
my ($self) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $success;
|
||||
|
||||
$success = main::DevIo_OpenDev($hash,1,"main::OWX_Init")
|
||||
if($hash->{STATE} eq "disconnected");
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Reset - Reset the 1-Wire bus (Fig. 4 of Maxim AN192)
|
||||
#
|
||||
# Return 1 : OK
|
||||
# 0 : not OK
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Reset () {
|
||||
my ($self)=@_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $interface = $hash->{TYPE};
|
||||
my $asynchronous = $hash->{ASYNCHRONOUS};
|
||||
|
||||
return 0
|
||||
if( $hash->{STATE} eq "disconnected" );
|
||||
|
||||
my ($res,$r1,$r2);
|
||||
#-- Reset command
|
||||
my $cmd = "\xE3\xC5";
|
||||
|
||||
#-- OWX interface
|
||||
if( $interface eq "OWX" ){
|
||||
$res = $self->Query($cmd,1);
|
||||
$res = "" if( !$res );
|
||||
#-- OWX_ASYNC
|
||||
}elsif( $interface eq "OWX_ASYNC"){
|
||||
$res = $self->Query($cmd,1);
|
||||
$res = "" if( !$res );
|
||||
}
|
||||
|
||||
#-- process result
|
||||
$r1 = ord(substr($res,0,1)) & 192;
|
||||
if( $r1 != 192){
|
||||
main::OWX_WDBGL($name,4,"OWX_SER::Reset failure on bus $name ",$res);
|
||||
return 0;
|
||||
}
|
||||
$hash->{ALARMED} = "no";
|
||||
|
||||
$r2 = ord(substr($res,0,1)) & 3;
|
||||
|
||||
if( $r2 == 3 ){
|
||||
main::Log3($name,4, "OWX_SER::Reset $name detects no presence");
|
||||
return 1;
|
||||
}elsif( $r2 ==2 ){
|
||||
main::Log3($name,3, "OWX_SER::Reset Alarm presence detected on bus $name");
|
||||
$hash->{ALARMED} = "yes";
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Query - Synchronously write to and read from the 1-Wire bus
|
||||
#
|
||||
# Parameter: cmd = string to send to the 1-Wire bus
|
||||
# retlen = expected length of return string
|
||||
# Return: string received from the 1-Wire bus
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Query ($$) {
|
||||
|
||||
my ($self,$cmd,$numexp) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $state = $hash->{STATE};
|
||||
my $timeout = $hash->{timeout};
|
||||
my $numget = 0;
|
||||
my $buffer = "";
|
||||
my $try;
|
||||
|
||||
if($state ne "opened") {
|
||||
main::Log3 $name, 4, "OWX_SER::Query $name: attempted to write to $state device $name";
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-- write operation
|
||||
main::OWX_WDBGL($name,5,"OWX_SER::Query $name: Sending out",$cmd);
|
||||
return undef unless defined(main::DevIo_SimpleWrite($hash, $cmd, 0));
|
||||
|
||||
#-- sleeping for some time
|
||||
select(undef,undef,undef,0.04);
|
||||
|
||||
#-- first try to read things
|
||||
$buffer = main::DevIo_SimpleRead($hash);
|
||||
$numget = (defined($buffer))?length($buffer):0;
|
||||
|
||||
#-- first try ok
|
||||
if( $numget >= $numexp){
|
||||
main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in first attempt and state $state";
|
||||
return $buffer;
|
||||
#-- several tries to read from slow device
|
||||
}elsif( ($numget>0) && ($numget<$numexp) ){
|
||||
for($try=0;$try<3;$try++) {
|
||||
$buffer .= main::DevIo_SimpleRead($hash);
|
||||
$numget = length($buffer);
|
||||
last
|
||||
if( $numget>=$numexp );
|
||||
select(undef,undef,undef,0.01);
|
||||
}
|
||||
main::Log3 $name, 5, "OWX_SER::Query $name: $numget of $numexp bytes in attempt $try and state $state"
|
||||
if( $numget < $numexp );
|
||||
return $buffer
|
||||
if( $numget >= $numexp);
|
||||
}
|
||||
|
||||
#if( $numget >= $numexp){
|
||||
# main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt and state $state";
|
||||
# return $buffer;
|
||||
#-- ultimate failure
|
||||
#}else{
|
||||
main::Log3 $name, 1,"OWX_SER::Query $name: $numget of $numexp bytes in last attempt and state $state, this is an unrecoverable error";
|
||||
main::DevIo_Disconnected($hash);
|
||||
main::InternalTimer(main::gettimeofday()+$timeout, "main::OWX_Ready", $hash,0);
|
||||
return "";
|
||||
#}
|
||||
|
||||
|
||||
#--reopen device
|
||||
main::Log3 $name, 4, "OWX_SER::Query $name: trying to close and open the device";
|
||||
|
||||
#-- the next two lines are required to avoid a deadlock when the remote end
|
||||
# closes the connection upon DevIo_OpenDev, as e.g. netcat -l <port> does.
|
||||
main::DevIo_CloseDev($hash);
|
||||
main::DevIo_OpenDev($hash, 0, undef);
|
||||
|
||||
#-- second try to read things - with timeout
|
||||
#$buffer .= main::DevIo_SimpleReadWithTimeout($hash, $timeout);
|
||||
$buffer .= main::DevIo_SimpleRead($hash);
|
||||
$numget = (defined($buffer))?length($buffer):0;
|
||||
|
||||
#-- second try ok
|
||||
if( $numget >= $numexp){
|
||||
main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes after reopening and state $state";
|
||||
return $buffer;
|
||||
#-- several tries to read from slow device
|
||||
}elsif( ($numget>0) && ($numget<$numexp) ){
|
||||
for($try=0;$try<4;$try++) {
|
||||
$buffer .= main::DevIo_SimpleRead($hash);
|
||||
$numget = length($buffer);
|
||||
last
|
||||
if( $numget>=$numexp );
|
||||
select(undef,undef,undef,0.01);
|
||||
}
|
||||
main::Log3 $name, 5, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt try $try and state $state"
|
||||
if( $numget < $numexp );
|
||||
}
|
||||
|
||||
if( $numget >= $numexp){
|
||||
main::Log3 $name, 4, "OWX_SER::Query $name: $numget of $numexp bytes in 2nd attempt and state $state";
|
||||
return $buffer;
|
||||
#-- ultimate failure
|
||||
}else{
|
||||
main::Log3 $name, 1,"OWX_SER::Query $name: $numget of $numexp bytes in last attempt and state $state, this is an unrecoverable error";
|
||||
main::DevIo_Disconnected($hash);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Verify - Verify a particular device on the 1-Wire bus
|
||||
#
|
||||
# Parameter dev = 8 Byte ROM ID of device to be tested
|
||||
#
|
||||
# Return 1 : device found
|
||||
# 0 : device not
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Verify ($) {
|
||||
my ($self,$dev) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my @rom_id;
|
||||
my $i;
|
||||
|
||||
#-- from search string to byte id
|
||||
my $devs=$dev;
|
||||
$devs=~s/\.//g;
|
||||
for($i=0;$i<8;$i++){
|
||||
$rom_id[$i]=hex(substr($devs,2*$i,2));
|
||||
}
|
||||
@{$self->{ROM_ID}}=@rom_id;
|
||||
#-- reset the search state
|
||||
$self->{LastDiscrepancy} = 64;
|
||||
$self->{LastDeviceFlag} = 0;
|
||||
#-- now do the search
|
||||
my $res=$self->Search("verify");
|
||||
my $dev2=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}});
|
||||
#-- reset the search state
|
||||
$self->{LastDiscrepancy} = 0;
|
||||
$self->{LastDeviceFlag} = 0;
|
||||
#-- check result
|
||||
if ($dev eq $dev2){
|
||||
return 1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Write - Implement the write function
|
||||
#
|
||||
# Parameter cmd = string to be sent
|
||||
# reset = 1 if initial bus reset has to be done
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Write(@) {
|
||||
my ($self,$cmd, $reset) = @_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
if($hash->{STATE} eq "disconnected"){
|
||||
main::Log3 $name,4,"OWX_SER::Write attempted to disconnected device $name";
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-- if necessary, perform a reset operation
|
||||
$self->Reset()
|
||||
if( $reset );
|
||||
|
||||
#-- if necessary, prepend E1 character for data mode of DS2480
|
||||
my $cmd2 = ( substr($cmd,0,1) ne '\xE1')?"\xE1":"";
|
||||
|
||||
#-- all E3 characters have to be duplicated in DS2480
|
||||
for(my $i=0;$i<length($cmd);$i++){
|
||||
my $newchar = substr($cmd,$i,1);
|
||||
$cmd2=$cmd2.$newchar;
|
||||
if( $newchar eq '\xE3'){
|
||||
$cmd2=$cmd2.$newchar;
|
||||
}
|
||||
}
|
||||
|
||||
main::OWX_WDBGL($name,4,"OWX_SER::Write Sending out ",$cmd2);
|
||||
|
||||
main::DevIo_SimpleWrite($hash, $cmd2, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# First - Find the 'first' devices on the 1-Wire bus
|
||||
#
|
||||
# Parameter mode
|
||||
#
|
||||
# Return 1 : device found, ROM number pushed to list
|
||||
# 0 : no device present
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub First ($) {
|
||||
my ($self,$mode) = @_;
|
||||
#-- clear 16 byte of search data
|
||||
@{$self->{search}} = (0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0);
|
||||
#-- reset the search state
|
||||
$self->{LastDiscrepancy} = 0;
|
||||
$self->{LastDeviceFlag} = 0;
|
||||
$self->{LastFamilyDiscrepancy} = 0;
|
||||
#-- now do the search
|
||||
return $self->Search($mode);
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# Next - Find the 'next' devices on the 1-Wire bus
|
||||
#
|
||||
# Parameter hash = hash of bus master, mode
|
||||
#
|
||||
# Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
|
||||
# or only in owx_ROM_ID (LastDeviceFlag=1)
|
||||
# 0 : device not found, or ot searched at all
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Next ($) {
|
||||
my ($self,$mode) = @_;
|
||||
|
||||
#-- now do the search
|
||||
return $self->Search($mode);
|
||||
}
|
||||
|
||||
#######################################################################################
|
||||
#
|
||||
# Search - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
|
||||
# search state.
|
||||
#
|
||||
# Parameter mode=alarm,discover or verify
|
||||
#
|
||||
# Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
|
||||
# or only in owx_ROM_ID (LastDeviceFlag=1)
|
||||
# 0 : device not found, or ot searched at all
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub Search ($) {
|
||||
my ($self,$mode)=@_;
|
||||
my $hash = $self->{hash};
|
||||
my $name = $hash->{NAME};
|
||||
my $interface = $hash->{INTERFACE};
|
||||
my $hwdevice = $hash->{HWDEVICE};
|
||||
|
||||
my @owx_fams=();
|
||||
|
||||
return 0 unless (defined $hwdevice);
|
||||
|
||||
#-- if the last call was the last one, no search
|
||||
if ($self->{LastDeviceFlag}==1){
|
||||
return 0;
|
||||
}
|
||||
#-- 1-Wire reset
|
||||
if ($self->Reset()==0){
|
||||
#-- reset the search
|
||||
main::Log(1, "OWX_SER::Search reset failed on bus $name");
|
||||
$self->{LastDiscrepancy} = 0;
|
||||
$self->{LastDeviceFlag} = 0;
|
||||
$self->{LastFamilyDiscrepancy} = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-- Here we call the device dependent part
|
||||
$self->SearchLow($mode);
|
||||
|
||||
#--check if we really found a device
|
||||
if( main::OWX_CRC($self->{ROM_ID})!= 0){
|
||||
#-- reset the search
|
||||
main::Log(1, "OWX_SER::Search CRC failed on bus $name");
|
||||
$self->{LastDiscrepancy} = 0;
|
||||
$self->{LastDeviceFlag} = 0;
|
||||
$self->{LastFamilyDiscrepancy} = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-- character version of device ROM_ID, first byte = family
|
||||
my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}});
|
||||
|
||||
#-- for some reason this does not work - replaced by another test, see below
|
||||
#if( $self->{LastDiscrepancy}==0 ){
|
||||
# $self->{LastDeviceFlag}=1;
|
||||
#}
|
||||
#--
|
||||
if( $self->{LastDiscrepancy}==$self->{LastFamilyDiscrepancy} ){
|
||||
$self->{LastFamilyDiscrepancy}=0;
|
||||
}
|
||||
|
||||
#-- mode was to verify presence of a device
|
||||
if ($mode eq "verify") {
|
||||
main::Log(5, "OWX_SER::Search verified $dev on bus $name");
|
||||
return 1;
|
||||
#-- mode was to discover devices
|
||||
} elsif( $mode eq "discover" ){
|
||||
#-- check families
|
||||
my $famfnd=0;
|
||||
foreach (@owx_fams){
|
||||
if( substr($dev,0,2) eq $_ ){
|
||||
#-- if present, set the fam found flag
|
||||
$famfnd=1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
push(@owx_fams,substr($dev,0,2)) if( !$famfnd );
|
||||
foreach (@{$hash->{DEVS}}){
|
||||
if( $dev eq $_ ){
|
||||
#-- if present, set the last device found flag
|
||||
$self->{LastDeviceFlag}=1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if( $self->{LastDeviceFlag}!=1 ){
|
||||
#-- push to list
|
||||
push(@{$hash->{DEVS}},$dev);
|
||||
main::Log(5, "OWX_SER::Search new device found $dev on bus $name");
|
||||
}
|
||||
return 1;
|
||||
|
||||
#-- mode was to discover alarm devices
|
||||
} elsif( $hash->{ALARMDEVS} ) {
|
||||
for(my $i=0;$i<@{$hash->{ALARMDEVS}};$i++){
|
||||
if( $dev eq ${$hash->{ALARMDEVS}}[$i] ){
|
||||
#-- if present, set the last device found flag
|
||||
$self->{LastDeviceFlag}=1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
if( $self->{LastDeviceFlag}!=1 ){
|
||||
#--push to list
|
||||
push(@{$hash->{ALARMDEVS}},$dev);
|
||||
main::Log(5, "OWX_SER::Search new alarm device found $dev on bus $name");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SearchLow - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
|
||||
# search state.
|
||||
#
|
||||
# Parameter mode=alarm,discover or verify
|
||||
#
|
||||
# Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
|
||||
# or only in owx_ROM_ID (LastDeviceFlag=1)
|
||||
# 0 : device not found, or ot searched at all
|
||||
#
|
||||
########################################################################################
|
||||
|
||||
sub SearchLow ($) {
|
||||
my ($self,$mode)=@_;
|
||||
my $hash = $self->{hash};
|
||||
|
||||
my ($sp1,$sp2,$response,$search_direction,$id_bit_number);
|
||||
|
||||
#-- Response search data parsing operates bytewise
|
||||
$id_bit_number = 1;
|
||||
|
||||
#-- used to be 0.5
|
||||
select(undef,undef,undef,0.05);
|
||||
|
||||
#-- clear 16 byte of search data
|
||||
@{$self->{search}}=(0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0);
|
||||
#-- Output search data construction (Fig. 9 of Maxim AN192)
|
||||
# operates on a 16 byte search response = 64 pairs of two bits
|
||||
while ( $id_bit_number <= 64) {
|
||||
#-- address single bits in a 16 byte search string
|
||||
my $newcpos = int(($id_bit_number-1)/4);
|
||||
my $newimsk = ($id_bit_number-1)%4;
|
||||
#-- address single bits in a 8 byte id string
|
||||
my $newcpos2 = int(($id_bit_number-1)/8);
|
||||
my $newimsk2 = ($id_bit_number-1)%8;
|
||||
|
||||
if( $id_bit_number <= $self->{LastDiscrepancy}){
|
||||
#-- first use the ROM ID bit to set the search direction
|
||||
if( $id_bit_number < $self->{LastDiscrepancy} ) {
|
||||
$search_direction = (@{$self->{ROM_ID}}[$newcpos2]>>$newimsk2) & 1;
|
||||
#-- at the last discrepancy search into 1 direction anyhow
|
||||
} else {
|
||||
$search_direction = 1;
|
||||
}
|
||||
#-- fill into search data;
|
||||
@{$self->{search}}[$newcpos]+=$search_direction<<(2*$newimsk+1);
|
||||
}
|
||||
#--increment number
|
||||
$id_bit_number++;
|
||||
}
|
||||
#-- issue data mode \xE1, the normal search command \xF0 or the alarm search command \xEC
|
||||
# and the command mode \xE3 / start accelerator \xB5
|
||||
if( $mode ne "alarm" ){
|
||||
$sp1 = "\xE1\xF0\xE3\xB5";
|
||||
} else {
|
||||
$sp1 = "\xE1\xEC\xE3\xB5";
|
||||
}
|
||||
#-- issue data mode \xE1, device ID, command mode \xE3 / end accelerator \xA5
|
||||
$sp2=sprintf("\xE1%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\xE3\xA5",@{$self->{search}});
|
||||
$response = $self->Query($sp1,1);
|
||||
$response = $self->Query($sp2,16);
|
||||
|
||||
#-- interpret the return data
|
||||
if( length($response)!=16 ) {
|
||||
main::Log(3, "OWX_SER::Search 2nd return has wrong parameter with length = ".length($response)."");
|
||||
return 0;
|
||||
}
|
||||
#-- Response search data parsing (Fig. 11 of Maxim AN192)
|
||||
# operates on a 16 byte search response = 64 pairs of two bits
|
||||
$id_bit_number = 1;
|
||||
#-- clear 8 byte of device id for current search
|
||||
@{$self->{ROM_ID}} =(0,0,0,0 ,0,0,0,0);
|
||||
|
||||
while ( $id_bit_number <= 64) {
|
||||
#-- adress single bits in a 16 byte string
|
||||
my $newcpos = int(($id_bit_number-1)/4);
|
||||
my $newimsk = ($id_bit_number-1)%4;
|
||||
|
||||
#-- retrieve the new ROM_ID bit
|
||||
my $newchar = substr($response,$newcpos,1);
|
||||
|
||||
#-- these are the new bits
|
||||
my $newibit = (( ord($newchar) >> (2*$newimsk) ) & 2) / 2;
|
||||
my $newdbit = ( ord($newchar) >> (2*$newimsk) ) & 1;
|
||||
|
||||
#-- output for test purpose
|
||||
#print "id_bit_number=$id_bit_number => newcpos=$newcpos, newchar=0x".int(ord($newchar)/16).
|
||||
# ".".int(ord($newchar)%16)." r$id_bit_number=$newibit d$id_bit_number=$newdbit\n";
|
||||
|
||||
#-- discrepancy=1 and ROM_ID=0
|
||||
if( ($newdbit==1) and ($newibit==0) ){
|
||||
$self->{LastDiscrepancy}=$id_bit_number;
|
||||
if( $id_bit_number < 9 ){
|
||||
$self->{LastFamilyDiscrepancy}=$id_bit_number;
|
||||
}
|
||||
}
|
||||
#-- fill into device data; one char per 8 bits
|
||||
@{$self->{ROM_ID}}[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8);
|
||||
|
||||
#-- increment number
|
||||
$id_bit_number++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
||||
=pod
|
||||
=item helper
|
||||
=item summary to address an OWX interface device via USB/Serial Interface
|
||||
=item summary_DE zur Adressierung eines OWX Interface Device via USB/Serial Interface
|
||||
=begin html
|
||||
|
||||
<a name="OWX_SER"></a>
|
||||
<h3>OWX_SER</h3>
|
||||
See <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="OWX_SER"></a>
|
||||
<h3>OWX_SER</h3>
|
||||
<a href="http://fhemwiki.de/wiki/Interfaces_f%C3%BCr_1-Wire">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#OWX">OWX</a>
|
||||
=end html_DE
|
Loading…
x
Reference in New Issue
Block a user