mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-03-10 03:06:37 +00:00
OWX_ASYNC: add OWX_DS2480 busmaster support
git-svn-id: https://svn.fhem.de/fhem/trunk@5440 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
3d49870dd2
commit
1fa8ce3a5a
421
fhem/FHEM/11_OWX_DS2480.pm
Normal file
421
fhem/FHEM/11_OWX_DS2480.pm
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# OWX_DS2480.pm
|
||||||
|
#
|
||||||
|
# FHEM module providing hardware dependent functions for the DS2480 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_DS2480;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Time::HiRes qw( gettimeofday tv_interval usleep );
|
||||||
|
|
||||||
|
use vars qw/@ISA/;
|
||||||
|
@ISA='OWX_SER';
|
||||||
|
|
||||||
|
sub new($) {
|
||||||
|
my ($class,$serial) = @_;
|
||||||
|
|
||||||
|
return bless $serial,$class;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# The following subroutines in alphabetical order are only for a DS2480 bus interface
|
||||||
|
#
|
||||||
|
#########################################################################################
|
||||||
|
#
|
||||||
|
# Block_2480 - Send data block (Fig. 6 of Maxim AN192)
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master, data = string to send
|
||||||
|
#
|
||||||
|
# Return response, if OK
|
||||||
|
# 0 if not OK
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub block ($) {
|
||||||
|
my ($serial,$data) =@_;
|
||||||
|
my $data2="";
|
||||||
|
|
||||||
|
my $len = length($data);
|
||||||
|
|
||||||
|
#-- if necessary, prepend E1 character for data mode
|
||||||
|
if( substr($data,0,1) ne '\xE1') {
|
||||||
|
$data2 = "\xE1";
|
||||||
|
}
|
||||||
|
#-- all E3 characters have to be duplicated
|
||||||
|
for(my $i=0;$i<$len;$i++){
|
||||||
|
my $newchar = substr($data,$i,1);
|
||||||
|
$data2=$data2.$newchar;
|
||||||
|
if( $newchar eq '\xE3'){
|
||||||
|
$data2=$data2.$newchar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#-- write 1-Wire bus as a single string
|
||||||
|
$serial->query($data2,$len);
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# query - Write to the 1-Wire bus
|
||||||
|
#
|
||||||
|
# Parameter: cmd = string to send to the 1-Wire bus, retlen = expected len of the response
|
||||||
|
#
|
||||||
|
# Return: 1 when write was successful. undef otherwise
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub query ($$$) {
|
||||||
|
|
||||||
|
my ($serial,$cmd,$retlen) = @_;
|
||||||
|
my ($i,$j,$k,$l,$m,$n);
|
||||||
|
|
||||||
|
#-- get hardware device
|
||||||
|
my $hwdevice = $serial->{hwdevice};
|
||||||
|
|
||||||
|
die "OWX_DS2480: query with no hwdevice" unless (defined $hwdevice);
|
||||||
|
|
||||||
|
$hwdevice->baudrate($serial->{baud});
|
||||||
|
$hwdevice->write_settings;
|
||||||
|
|
||||||
|
if( $main::owx_debug > 2){
|
||||||
|
main::Log3($serial->{name},3, "OWX_DS2480.query sending out: ".unpack ("H*",$cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
my $count_out = $hwdevice->write($cmd);
|
||||||
|
|
||||||
|
die "OWX_DS2480: Write incomplete ".(defined $count_out ? $count_out : "undefined")." not equal ".(length($cmd))."" if (!(defined $count_out) or ($count_out ne length($cmd)));
|
||||||
|
|
||||||
|
$serial->{retlen} += $retlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# read - read response from the 1-Wire bus
|
||||||
|
# to be called from OWX ReadFn.
|
||||||
|
#
|
||||||
|
# Parameter: -
|
||||||
|
#
|
||||||
|
# Return: 1 when at least 1 byte was read. undef otherwise
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub read() {
|
||||||
|
my $serial = shift;
|
||||||
|
my ($i,$j,$k);
|
||||||
|
|
||||||
|
#-- get hardware device
|
||||||
|
my $hwdevice = $serial->{hwdevice};
|
||||||
|
return undef unless (defined $hwdevice);
|
||||||
|
|
||||||
|
#-- read the data - looping for slow devices suggested by Joachim Herold
|
||||||
|
my ($count_in, $string_part) = $hwdevice->read(48);
|
||||||
|
return undef if (not defined $count_in or not defined $string_part);
|
||||||
|
$serial->{string_in} .= $string_part;
|
||||||
|
$serial->{retcount} += $count_in;
|
||||||
|
$serial->{num_reads}++;
|
||||||
|
if( $main::owx_debug > 2){
|
||||||
|
main::Log3($serial->{name},3, "OWX_DS2480 read: Loop no. $serial->{num_reads}");
|
||||||
|
}
|
||||||
|
if( $main::owx_debug > 2){
|
||||||
|
main::Log3($serial->{name},3, "OWX_DS2480 read: Receiving in loop no. $serial->{num_reads} ".unpack("H*",$string_part));
|
||||||
|
}
|
||||||
|
return $count_in > 0 ? 1 : undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub response_ready() {
|
||||||
|
my ($serial) = @_;
|
||||||
|
return 1 if ($serial->{retcount} >= $serial->{retlen});
|
||||||
|
die "OWX_DS2480 read timeout, bytes read: $serial->{retcount}, expected: $serial->{retlen}" if (($serial->{num_reads} > 1) and (tv_interval($serial->{starttime}) > $serial->{timeout}));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start_query() {
|
||||||
|
my ($serial) = @_;
|
||||||
|
#read and discard any outstanding data from previous commands:
|
||||||
|
while($serial->read()) {};
|
||||||
|
|
||||||
|
$serial->{string_in} = "";
|
||||||
|
$serial->{num_reads} = 0;
|
||||||
|
$serial->{retlen} = 0;
|
||||||
|
$serial->{retcount} = 0;
|
||||||
|
$serial->{starttime} = [gettimeofday];
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# reset - Reset the 1-Wire bus (Fig. 4 of Maxim AN192)
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master
|
||||||
|
#
|
||||||
|
# Return 1 : OK
|
||||||
|
# 0 : not OK
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub reset() {
|
||||||
|
|
||||||
|
my ($serial) = @_;
|
||||||
|
my ($res,$r1,$r2);
|
||||||
|
my $name = $serial->{name};
|
||||||
|
$serial->start_query();
|
||||||
|
|
||||||
|
#-- if necessary, prepend \xE3 character for command mode
|
||||||
|
#-- Reset command \xC5
|
||||||
|
#-- write 1-Wire bus
|
||||||
|
$serial->query("\xE3\xC5",1);
|
||||||
|
#-- sleeping for some time (value of 0.07 taken from original OWX_Query_DS2480)
|
||||||
|
select(undef,undef,undef,0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reset_response() {
|
||||||
|
my ($serial) = @_;
|
||||||
|
|
||||||
|
my $res = ord(substr($serial->{string_in},0,1));
|
||||||
|
my $name = $serial->{name};
|
||||||
|
|
||||||
|
if( !($res & 192) ) {
|
||||||
|
main::Log3($name,3, "OWX_DS2480 reset failure on bus $name");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ($res & 3) == 2 ) {
|
||||||
|
main::Log3($name,1, "OWX_DS2480 reset Alarm presence detected on bus $name");
|
||||||
|
$serial->{ALARMED} = "yes";
|
||||||
|
} else {
|
||||||
|
$serial->{ALARMED} = "no";
|
||||||
|
}
|
||||||
|
$serial->{string_in} = substr($serial->{string_in},1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Search_2480 - Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing
|
||||||
|
# search state.
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master, mode=alarm,discover or verify
|
||||||
|
#
|
||||||
|
# Return 1 : device found, ROM number in owx_ROM_ID and pushed to list (LastDeviceFlag=0)
|
||||||
|
# or only in owx_ROM_ID (LastDeviceFlag=1)
|
||||||
|
# 0 : device not found, or ot searched at all
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub search ($) {
|
||||||
|
my ($serial,$mode)=@_;
|
||||||
|
|
||||||
|
my ($sp1,$sp2,$search_direction,$id_bit_number);
|
||||||
|
|
||||||
|
#-- Response search data parsing operates bytewise
|
||||||
|
$id_bit_number = 1;
|
||||||
|
|
||||||
|
#select(undef,undef,undef,0.5);
|
||||||
|
|
||||||
|
#-- clear 16 byte of search data
|
||||||
|
@{$serial->{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 <= $serial->{LastDiscrepancy}){
|
||||||
|
#-- first use the ROM ID bit to set the search direction
|
||||||
|
if( $id_bit_number < $serial->{LastDiscrepancy} ) {
|
||||||
|
$search_direction = ($serial->{ROM_ID}->[$newcpos2]>>$newimsk2) & 1;
|
||||||
|
#-- at the last discrepancy search into 1 direction anyhow
|
||||||
|
} else {
|
||||||
|
$search_direction = 1;
|
||||||
|
}
|
||||||
|
#-- fill into search data;
|
||||||
|
@{$serial->{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",@{$serial->{search}});
|
||||||
|
$serial->reset();
|
||||||
|
$serial->query($sp1,1);
|
||||||
|
$serial->query($sp2,16);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub search_response($) {
|
||||||
|
my ($serial) = @_;
|
||||||
|
|
||||||
|
return undef unless $serial->reset_response();
|
||||||
|
|
||||||
|
my $response = substr($serial->{string_in},1);
|
||||||
|
#-- interpret the return data
|
||||||
|
if( length($response)!=16 ) {
|
||||||
|
main::Log3($serial->{name},3, "OWX_DS2480: 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
|
||||||
|
my $id_bit_number = 1;
|
||||||
|
#-- clear 8 byte of device id for current search
|
||||||
|
$serial->{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) ){
|
||||||
|
$serial->{LastDiscrepancy}=$id_bit_number;
|
||||||
|
if( $id_bit_number < 9 ){
|
||||||
|
$serial->{LastFamilyDiscrepancy}=$id_bit_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#-- fill into device data; one char per 8 bits
|
||||||
|
$serial->{ROM_ID}->[int(($id_bit_number-1)/8)]+=$newibit<<(($id_bit_number-1)%8);
|
||||||
|
|
||||||
|
#-- increment number
|
||||||
|
$id_bit_number++;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Level_2480 - Change power level (Fig. 13 of Maxim AN192)
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master, newlevel = "normal" or something else
|
||||||
|
#
|
||||||
|
# Return 1 : OK
|
||||||
|
# 0 : not OK
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub Level_2480 ($) {
|
||||||
|
my ($self,$newlevel) =@_;
|
||||||
|
my $cmd="";
|
||||||
|
my $retlen=0;
|
||||||
|
#-- if necessary, prepend E3 character for command mode
|
||||||
|
$cmd = "\xE3";
|
||||||
|
|
||||||
|
#-- return to normal level
|
||||||
|
if( $newlevel eq "normal" ){
|
||||||
|
$cmd=$cmd."\xF1\xED\xF1";
|
||||||
|
$retlen+=3;
|
||||||
|
#-- write 1-Wire bus
|
||||||
|
my $res = $self->Query_2480($cmd,$retlen);
|
||||||
|
return undef if (not defined $res);
|
||||||
|
#-- process result
|
||||||
|
my $r1 = ord(substr($res,0,1)) & 236;
|
||||||
|
my $r2 = ord(substr($res,1,1)) & 236;
|
||||||
|
if( ($r1 eq 236) && ($r2 eq 236) ){
|
||||||
|
main::Log3($self->{name},5, "OWX_SER: Level change to normal OK");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
main::Log3($self->{name},3, "OWX_SER: Failed to change to normal level");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#-- start pulse
|
||||||
|
} else {
|
||||||
|
$cmd=$cmd."\x3F\xED";
|
||||||
|
$retlen+=2;
|
||||||
|
#-- write 1-Wire bus
|
||||||
|
my $res = $self->Query_2480($cmd,$retlen);
|
||||||
|
return undef if (not defined $res);
|
||||||
|
#-- process result
|
||||||
|
if( $res eq "\x3E" ){
|
||||||
|
main::Log3($self->{name},5, "OWX_SER: Level change OK");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
main::Log3($self->{name},3, "OWX_SER: Failed to change level");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# WriteBytePower_2480 - Send byte to bus with power increase (Fig. 16 of Maxim AN192)
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master, dbyte = byte to send
|
||||||
|
#
|
||||||
|
# Return 1 : OK
|
||||||
|
# 0 : not OK
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub WriteBytePower_2480 ($) {
|
||||||
|
|
||||||
|
my ($self,$dbyte) =@_;
|
||||||
|
|
||||||
|
my $cmd="\x3F";
|
||||||
|
my $ret="\x3E";
|
||||||
|
#-- if necessary, prepend \xE3 character for command mode
|
||||||
|
$cmd = "\xE3".$cmd;
|
||||||
|
|
||||||
|
#-- distribute the bits of data byte over several command bytes
|
||||||
|
for (my $i=0;$i<8;$i++){
|
||||||
|
my $newbit = (ord($dbyte) >> $i) & 1;
|
||||||
|
my $newchar = 133 | ($newbit << 4);
|
||||||
|
my $newchar2 = 132 | ($newbit << 4) | ($newbit << 1) | $newbit;
|
||||||
|
#-- last command byte still different
|
||||||
|
if( $i == 7){
|
||||||
|
$newchar = $newchar | 2;
|
||||||
|
}
|
||||||
|
$cmd = $cmd.chr($newchar);
|
||||||
|
$ret = $ret.chr($newchar2);
|
||||||
|
}
|
||||||
|
#-- write 1-Wire bus
|
||||||
|
my $res = $self->Query($cmd);
|
||||||
|
#-- process result
|
||||||
|
if( $res eq $ret ){
|
||||||
|
main::Log3($self->{name},5, "OWX_SER::WriteBytePower OK");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
main::Log3($self->{name},3, "OWX_SER::WriteBytePower failure");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
240
fhem/FHEM/11_OWX_Executor.pm
Normal file
240
fhem/FHEM/11_OWX_Executor.pm
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
package OWX_Executor;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use constant {
|
||||||
|
DISCOVER => 1,
|
||||||
|
ALARMS => 2,
|
||||||
|
VERIFY => 3,
|
||||||
|
EXECUTE => 4,
|
||||||
|
EXIT => 5,
|
||||||
|
LOG => 6
|
||||||
|
};
|
||||||
|
|
||||||
|
sub new() {
|
||||||
|
my $class = shift;
|
||||||
|
my $self = {};
|
||||||
|
$self->{worker} = OWX_Worker->new($self);
|
||||||
|
return bless $self,$class;
|
||||||
|
};
|
||||||
|
|
||||||
|
sub discover($) {
|
||||||
|
my ($self,$hash) = @_;
|
||||||
|
if($self->{worker}->submit( { command => DISCOVER }, $hash )) {
|
||||||
|
$self->poll($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub alarms($) {
|
||||||
|
my ($self,$hash) = @_;
|
||||||
|
if($self->{worker}->submit( { command => ALARMS }, $hash )) {
|
||||||
|
$self->poll($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub verify($$) {
|
||||||
|
my ($self,$hash,$device) = @_;
|
||||||
|
if($self->{worker}->submit( { command => VERIFY, address => $device }, $hash )) {
|
||||||
|
$self->poll($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub execute($$$$$$$) {
|
||||||
|
my ( $self, $hash, $context, $reset, $owx_dev, $data, $numread, $delay ) = @_;
|
||||||
|
if($self->{worker}->submit( {
|
||||||
|
command => EXECUTE,
|
||||||
|
context => $context,
|
||||||
|
reset => $reset,
|
||||||
|
address => $owx_dev,
|
||||||
|
writedata => $data,
|
||||||
|
numread => $numread,
|
||||||
|
delay => $delay
|
||||||
|
}, $hash )) {
|
||||||
|
$self->poll($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
};
|
||||||
|
|
||||||
|
sub exit($) {
|
||||||
|
my ( $self,$hash ) = @_;
|
||||||
|
if($self->{worker}->submit( { command => EXIT }, $hash )) {
|
||||||
|
$self->poll($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub poll($) {
|
||||||
|
my ( $self,$hash ) = @_;
|
||||||
|
$self->read();
|
||||||
|
$self->{worker}->PT_SCHEDULE($hash);
|
||||||
|
$self->{worker}->scheduleNext($hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
# start of worker code
|
||||||
|
|
||||||
|
package OWX_Worker;
|
||||||
|
|
||||||
|
use Time::HiRes qw( gettimeofday tv_interval usleep );
|
||||||
|
use ProtoThreads;
|
||||||
|
no warnings 'deprecated';
|
||||||
|
|
||||||
|
use vars qw/@ISA/;
|
||||||
|
@ISA='ProtoThreads';
|
||||||
|
|
||||||
|
sub new($) {
|
||||||
|
my ($class,$owx) = @_;
|
||||||
|
|
||||||
|
my $worker = PT_THREAD(\&pt_main);
|
||||||
|
|
||||||
|
$worker->{commands} = [];
|
||||||
|
$worker->{delayed} = {};
|
||||||
|
$worker->{owx} = $owx;
|
||||||
|
|
||||||
|
return bless $worker,$class;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub submit($$) {
|
||||||
|
my ($self,$command,$hash) = @_;
|
||||||
|
push @{$self->{commands}}, $command;
|
||||||
|
$self->PT_SCHEDULE($hash);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub pt_main($) {
|
||||||
|
my ( $self, $hash ) = @_;
|
||||||
|
my $item = $self->{item};
|
||||||
|
PT_BEGIN($self);
|
||||||
|
PT_YIELD_UNTIL($item = $self->nextItem($hash));
|
||||||
|
$self->{item} = $item;
|
||||||
|
|
||||||
|
REQUEST_HANDLER: {
|
||||||
|
my $command = $item->{command};
|
||||||
|
|
||||||
|
$command eq OWX_Executor::DISCOVER and do {
|
||||||
|
PT_WAIT_THREAD($self->{owx}->{pt_discover},$self->{owx});
|
||||||
|
my $devices = $self->{owx}->{pt_discover}->PT_RETVAL();
|
||||||
|
if (defined $devices) {
|
||||||
|
main::OWX_ASYNC_AfterSearch($hash,$devices);
|
||||||
|
}
|
||||||
|
PT_EXIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
$command eq OWX_Executor::ALARMS and do {
|
||||||
|
PT_WAIT_THREAD($self->{owx}->{pt_alarms},$self->{owx});
|
||||||
|
my $devices = $self->{owx}->{pt_alarms}->PT_RETVAL();
|
||||||
|
if (defined $devices) {
|
||||||
|
main::OWX_ASYNC_AfterAlarms($hash,$devices);
|
||||||
|
}
|
||||||
|
PT_EXIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
$command eq OWX_Executor::VERIFY and do {
|
||||||
|
PT_WAIT_THREAD($self->{owx}->{pt_verify},$self->{owx},$item->{address});
|
||||||
|
my $devices = $self->{owx}->{pt_verify}->PT_RETVAL();
|
||||||
|
if (defined $devices) {
|
||||||
|
main::OWX_ASYNC_AfterVerify($hash,$devices);
|
||||||
|
}
|
||||||
|
PT_EXIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
$command eq OWX_Executor::EXECUTE and do {
|
||||||
|
PT_WAIT_THREAD($self->{owx}->{pt_execute},$self->{owx},$hash,$item->{context},$item->{reset},$item->{address},$item->{writedata},$item->{numread});
|
||||||
|
my $res = $self->{owx}->{pt_execute}->PT_RETVAL();
|
||||||
|
unless (defined $res) {
|
||||||
|
main::OWX_ASYNC_AfterExecute($hash,$item->{context},undef,$item->{reset},$item->{address},$item->{writedata},$item->{numread},undef);
|
||||||
|
PT_EXIT;
|
||||||
|
}
|
||||||
|
my $writelen = defined $item->{writedata} ? split (//,$item->{writedata}) : 0;
|
||||||
|
my @result = split (//, $res);
|
||||||
|
my $readdata = 9+$writelen < @result ? substr($res,9+$writelen) : "";
|
||||||
|
main::OWX_ASYNC_AfterExecute($hash,$item->{context},1,$item->{reset},$item->{address},$item->{writedata},$item->{numread},$readdata);
|
||||||
|
if (my $delay = $item->{delay}) {
|
||||||
|
my ($seconds,$micros) = gettimeofday;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my $address = $item->{address}) {
|
||||||
|
my $delayed = $self->{delayed};
|
||||||
|
unless ($delayed->{$address}) {
|
||||||
|
$delayed->{$address} = { items => [] };
|
||||||
|
}
|
||||||
|
$delayed->{$address}->{'until'} = [$seconds,$micros];
|
||||||
|
main::Log3 $hash->{NAME},5,"delay after $item->{context} until: $seconds,$micros"
|
||||||
|
} else {
|
||||||
|
$self->{execute_delayed} = [$seconds,$micros];
|
||||||
|
PT_YIELD_UNTIL(tv_interval($self->{execute_delayed})>=0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PT_EXIT;
|
||||||
|
};
|
||||||
|
|
||||||
|
$command eq OWX_Executor::EXIT and do {
|
||||||
|
main::OWX_ASYNC_Disconnected($hash);
|
||||||
|
PT_EXIT;
|
||||||
|
};
|
||||||
|
main::Log3($hash->{NAME},3,"OWX_Executor: unexpected command: "+$command);
|
||||||
|
};
|
||||||
|
PT_END;
|
||||||
|
};
|
||||||
|
|
||||||
|
sub nextItem($) {
|
||||||
|
my ( $self,$hash ) = @_;
|
||||||
|
my ($item,$nexttime,$nextaddress);
|
||||||
|
my $delayed = $self->{delayed};
|
||||||
|
foreach my $address (keys %$delayed) {
|
||||||
|
next if (tv_interval($delayed->{$address}->{'until'}) < 0);
|
||||||
|
my $delayed_items = $delayed->{$address}->{'items'};
|
||||||
|
$item = shift @$delayed_items;
|
||||||
|
delete $delayed->{$address} unless @$delayed_items;
|
||||||
|
last;
|
||||||
|
};
|
||||||
|
unless ($item) {
|
||||||
|
$item = shift @{$self->{commands}};
|
||||||
|
if ($item and my $address = $item->{address}) {
|
||||||
|
if ($delayed->{$address}) {
|
||||||
|
push @{$delayed->{$address}->{'items'}},$item;
|
||||||
|
return undef;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if ($item) {
|
||||||
|
if($item->{context}) {
|
||||||
|
main::Log3 $hash->{NAME},5,"OWX_Executor: item $item->{context} for $item->{address} eligible to run";
|
||||||
|
} else {
|
||||||
|
main::Log3 $hash->{NAME},5,"OWX_Executor: command $item->{command} eligible to run";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub scheduleNext($) {
|
||||||
|
my ($self,$hash) = @_;
|
||||||
|
my $delayed = $self->{delayed};
|
||||||
|
my $nexttime;
|
||||||
|
foreach my $address (keys %$delayed) {
|
||||||
|
if (my $until = $delayed->{$address}->{'until'}) {
|
||||||
|
$nexttime = $until unless ($nexttime and tv_interval($nexttime,$until) < 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ($nexttime) {
|
||||||
|
main::RemoveInternalTimer($hash);
|
||||||
|
main::Log3 $hash->{NAME},5,"schedule next item at $nexttime->[0].$nexttime->[1] ".tv_interval($nexttime);
|
||||||
|
main::InternalTimer( "$nexttime->[0].$nexttime->[1]", "OWX_ASYNC_Poll", $hash, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
535
fhem/FHEM/11_OWX_SER.pm
Normal file
535
fhem/FHEM/11_OWX_SER.pm
Normal file
@ -0,0 +1,535 @@
|
|||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# OWX_SER.pm
|
||||||
|
#
|
||||||
|
# FHEM module providing hardware dependent functions for the serial (USB) 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_SER;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use vars qw/@ISA/;
|
||||||
|
@ISA='OWX_Executor';
|
||||||
|
|
||||||
|
use ProtoThreads;
|
||||||
|
no warnings 'deprecated';
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Constructor
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub new() {
|
||||||
|
my $class = shift;
|
||||||
|
|
||||||
|
require "$main::attr{global}{modpath}/FHEM/11_OWX_Executor.pm";
|
||||||
|
|
||||||
|
my $self = OWX_Executor->new();
|
||||||
|
|
||||||
|
$self->{interface} = "serial";
|
||||||
|
#-- baud rate serial interface
|
||||||
|
$self->{baud} = 9600;
|
||||||
|
#-- 16 byte search string
|
||||||
|
$self->{search} = [0,0,0,0 ,0,0,0,0, 0,0,0,0, 0,0,0,0];
|
||||||
|
$self->{ROM_ID} = [0,0,0,0 ,0,0,0,0];
|
||||||
|
#-- search state for 1-Wire bus search
|
||||||
|
$self->{LastDiscrepancy} = 0;
|
||||||
|
$self->{LastFamilyDiscrepancy} = 0;
|
||||||
|
$self->{LastDeviceFlag} = 0;
|
||||||
|
#-- module version
|
||||||
|
$self->{version} = 4.0;
|
||||||
|
$self->{alarmdevs} = [];
|
||||||
|
$self->{devs} = [];
|
||||||
|
$self->{pt_alarms} = PT_THREAD(\&pt_alarms);
|
||||||
|
$self->{pt_discover} = PT_THREAD(\&pt_discover);
|
||||||
|
$self->{pt_verify} = PT_THREAD(\&pt_verify);
|
||||||
|
$self->{pt_execute} = PT_THREAD(\&pt_execute);
|
||||||
|
|
||||||
|
$self->{timeout} = [1,0]; #default timeout 1 sec.
|
||||||
|
|
||||||
|
return bless $self,$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);
|
||||||
|
|
||||||
|
$self->{name} = $hash->{NAME};
|
||||||
|
|
||||||
|
#-- check syntax
|
||||||
|
if(int(@a) < 3){
|
||||||
|
return "OWX_SER: Syntax error - must be define <name> OWX <serial-device>"
|
||||||
|
}
|
||||||
|
my $dev = $a[2];
|
||||||
|
|
||||||
|
#-- when the specified device name contains @<digits> already, use it as supplied
|
||||||
|
if ( $dev !~ m/\@\d*/ ){
|
||||||
|
$hash->{DeviceName} = $dev."\@9600";
|
||||||
|
}
|
||||||
|
$dev = split('@',$dev);
|
||||||
|
#-- let fhem.pl MAIN call OWX_Ready when setup is done.
|
||||||
|
$main::readyfnlist{"$hash->{NAME}.$dev"} = $hash;
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
|
||||||
|
#
|
||||||
|
# Return number of alarmed devices
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub pt_alarms () {
|
||||||
|
my ($thread,$self) = @_;
|
||||||
|
|
||||||
|
PT_BEGIN($thread);
|
||||||
|
$self->{alarmdevs} = [];
|
||||||
|
#-- Discover all alarmed devices on the 1-Wire bus
|
||||||
|
$self->first("alarm");
|
||||||
|
do {
|
||||||
|
$self->next("alarm");
|
||||||
|
PT_WAIT_UNTIL($self->response_ready());
|
||||||
|
PT_EXIT unless $self->next_response("alarm");
|
||||||
|
} while( $self->{LastDeviceFlag}==0 );
|
||||||
|
main::Log3($self->{name},1, " Alarms = ".join(' ',@{$self->{alarmdevs}}));
|
||||||
|
PT_EXIT($self->{alarmdevs});
|
||||||
|
PT_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# 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 pt_execute($$$$$$$) {
|
||||||
|
my ($thread, $self, $hash, $context, $reset, $address, $writedata, $numread) = @_;
|
||||||
|
|
||||||
|
PT_BEGIN($thread);
|
||||||
|
|
||||||
|
#-- get the interface
|
||||||
|
my $interface = $self->{interface};
|
||||||
|
my $hwdevice = $self->{hwdevice};
|
||||||
|
|
||||||
|
PT_EXIT unless (defined $hwdevice);
|
||||||
|
|
||||||
|
$self->reset() if ($reset);
|
||||||
|
|
||||||
|
my $dev = $address;
|
||||||
|
my $data = $writedata;
|
||||||
|
|
||||||
|
my $select;
|
||||||
|
my $res2 = "";
|
||||||
|
my ($i,$j,$k);
|
||||||
|
|
||||||
|
#-- has match ROM part
|
||||||
|
if( $dev ){# command => EXECUTE,
|
||||||
|
# context => $context,
|
||||||
|
# reset => $reset,
|
||||||
|
# address => $owx_dev,
|
||||||
|
# writedata => $data,
|
||||||
|
# numread => $numread,
|
||||||
|
# delay => $delay
|
||||||
|
|
||||||
|
|
||||||
|
#-- 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";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#-- for debugging
|
||||||
|
if( $main::owx_debug > 1){
|
||||||
|
main::Log3($self->{name},3,"OWX_SER::Execute: Sending out ".unpack ("H*",$select));
|
||||||
|
}
|
||||||
|
$self->block($select);
|
||||||
|
|
||||||
|
PT_WAIT_UNTIL($self->response_ready());
|
||||||
|
|
||||||
|
PT_EXIT if ($reset and !$self->reset_response());
|
||||||
|
|
||||||
|
my $res = $self->{string_in};
|
||||||
|
#-- for debugging
|
||||||
|
if( $main::owx_debug > 1){
|
||||||
|
main::Log3($self->{name},3,"OWX_SER::Execute: Receiving ".unpack ("H*",$res));
|
||||||
|
}
|
||||||
|
|
||||||
|
PT_EXIT($res);
|
||||||
|
PT_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Discover - Find devices on the 1-Wire bus
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master
|
||||||
|
#
|
||||||
|
# Return 1, if alarmed devices found, 0 otherwise.
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub pt_discover($) {
|
||||||
|
my ($thread,$self) = @_;
|
||||||
|
PT_BEGIN($thread);
|
||||||
|
#-- Discover all alarmed devices on the 1-Wire bus
|
||||||
|
$self->first("discover");
|
||||||
|
do {
|
||||||
|
$self->next("discover");
|
||||||
|
PT_WAIT_UNTIL($self->response_ready());
|
||||||
|
PT_EXIT unless $self->next_response("discover");
|
||||||
|
} while( $self->{LastDeviceFlag}==0 );
|
||||||
|
PT_EXIT($self->{devs});
|
||||||
|
PT_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# Init - Initialize the 1-wire device
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master
|
||||||
|
#
|
||||||
|
# Return 1 or Errormessage : not OK
|
||||||
|
# 0 or undef : OK
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub initialize($) {
|
||||||
|
my ($self,$hash) = @_;
|
||||||
|
my ($i,$j,$k,$l,$res,$ret,$ress);
|
||||||
|
#-- Second step in case of serial device: open the serial device to test it
|
||||||
|
my $msg = "OWX_SER: Serial device $hash->{DeviceName}";
|
||||||
|
main::DevIo_OpenDev($hash,0,undef);
|
||||||
|
my $hwdevice = $hash->{USBDev};
|
||||||
|
if(!defined($hwdevice)){
|
||||||
|
die $msg." not defined: $!";
|
||||||
|
} else {
|
||||||
|
main::Log3($hash->{NAME},1,$msg." defined");
|
||||||
|
}
|
||||||
|
$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
|
||||||
|
$self->{hwdevice} = $hwdevice;
|
||||||
|
|
||||||
|
#-- Third step detect busmaster on serial interface
|
||||||
|
|
||||||
|
my $name = $self->{name};
|
||||||
|
my $ress0 = "OWX_SER::Detect 1-Wire bus $name: interface ";
|
||||||
|
$ress = $ress0;
|
||||||
|
|
||||||
|
my $interface;
|
||||||
|
|
||||||
|
require "$main::attr{global}{modpath}/FHEM/11_OWX_DS2480.pm";
|
||||||
|
my $ds2480 = OWX_DS2480->new($self);
|
||||||
|
|
||||||
|
#-- timing byte for DS2480
|
||||||
|
$ds2480->start_query();
|
||||||
|
$ds2480->query("\xC1",1);
|
||||||
|
do {
|
||||||
|
$ds2480->read();
|
||||||
|
} while (!$ds2480->response_ready());
|
||||||
|
|
||||||
|
#-- Max 4 tries to detect an interface
|
||||||
|
for($l=0;$l<100;$l++) {
|
||||||
|
#-- write 1-Wire bus (Fig. 2 of Maxim AN192)
|
||||||
|
$ds2480->start_query();
|
||||||
|
$ds2480->query("\x17\x45\x5B\x0F\x91",5);
|
||||||
|
do {
|
||||||
|
$ds2480->read();
|
||||||
|
} while (!$ds2480->response_ready());
|
||||||
|
$res = $ds2480->{string_in};
|
||||||
|
#-- process 4/5-byte string for detection
|
||||||
|
if( !defined($res)){
|
||||||
|
$res="";
|
||||||
|
$ret=1;
|
||||||
|
}elsif( ($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=0;
|
||||||
|
} elsif( $res eq "\x17\x45\x5B\x0F\x91"){
|
||||||
|
$ress .= "master DS2480 re-detected";
|
||||||
|
$interface="DS2480";
|
||||||
|
$ret=0;
|
||||||
|
} elsif( ($res eq "\x17\x0A\x5B\x0F\x02") || ($res eq "\x00\x17\x0A\x5B\x0F\x02") || ($res eq "\x30\xf8\x00") || ($res eq "\x06\x00\x09\x07\x80")){
|
||||||
|
$ress .= "passive DS9097 detected";
|
||||||
|
$interface="DS9097";
|
||||||
|
$ret=0;
|
||||||
|
} else {
|
||||||
|
$ret=1;
|
||||||
|
}
|
||||||
|
last
|
||||||
|
if( $ret==0 );
|
||||||
|
$ress .= "not found, answer was ";
|
||||||
|
for($i=0;$i<length($res);$i++){
|
||||||
|
$j=int(ord(substr($res,$i,1))/16);
|
||||||
|
$k=ord(substr($res,$i,1))%16;
|
||||||
|
$ress.=sprintf "0x%1x%1x ",$j,$k;
|
||||||
|
}
|
||||||
|
main::Log3($hash->{NAME},1, $ress);
|
||||||
|
$ress = $ress0;
|
||||||
|
#-- sleeping for some time
|
||||||
|
select(undef,undef,undef,0.5);
|
||||||
|
}
|
||||||
|
if( $ret == 1 ){
|
||||||
|
$interface=undef;
|
||||||
|
$ress .= "not detected, answer was ";
|
||||||
|
for($i=0;$i<length($res);$i++){
|
||||||
|
$j=int(ord(substr($res,$i,1))/16);
|
||||||
|
$k=ord(substr($res,$i,1))%16;
|
||||||
|
$ress.=sprintf "0x%1x%1x ",$j,$k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$self->{interface} = $interface;
|
||||||
|
if ($interface eq "DS2480") {
|
||||||
|
return $ds2480;
|
||||||
|
} elsif ($interface eq "DS9097") {
|
||||||
|
require "$main::attr{global}{modpath}/FHEM/11_OWX_DS9097.pm";
|
||||||
|
return OWX_DS9097->new($self);
|
||||||
|
} else {
|
||||||
|
die $ress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Disconnect($) {
|
||||||
|
my ($self,$hash) = @_;
|
||||||
|
main::DevIo_Disconnected($hash);
|
||||||
|
delete $self->{hwdevice};
|
||||||
|
$self->{interface} = "serial";
|
||||||
|
}
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
#
|
||||||
|
# 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 pt_verify ($) {
|
||||||
|
my ($thread,$self,$dev) = @_;
|
||||||
|
my $i;
|
||||||
|
PT_BEGIN($thread);
|
||||||
|
#-- from search string to byte id
|
||||||
|
my $devs=$dev;
|
||||||
|
$devs=~s/\.//g;
|
||||||
|
for($i=0;$i<8;$i++){
|
||||||
|
@{$self->{ROM_ID}}[$i]=hex(substr($devs,2*$i,2));
|
||||||
|
}
|
||||||
|
#-- reset the search state
|
||||||
|
$self->{LastDiscrepancy} = 64;
|
||||||
|
$self->{LastDeviceFlag} = 0;
|
||||||
|
|
||||||
|
$self->reset();
|
||||||
|
#-- now do the search
|
||||||
|
$self->next("verify");
|
||||||
|
PT_WAIT_UNTIL($self->response_ready());
|
||||||
|
PT_EXIT unless $self->next_response("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){
|
||||||
|
PT_EXIT(1);
|
||||||
|
}else{
|
||||||
|
PT_EXIT;
|
||||||
|
}
|
||||||
|
PT_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################################
|
||||||
|
#
|
||||||
|
# Private methods
|
||||||
|
#
|
||||||
|
#######################################################################################
|
||||||
|
#
|
||||||
|
# First - Find the 'first' devices on the 1-Wire bus
|
||||||
|
#
|
||||||
|
# Parameter hash = hash of bus master, mode
|
||||||
|
#
|
||||||
|
# Return 1 : device found, ROM number pushed to list
|
||||||
|
# 0 : no device present
|
||||||
|
#
|
||||||
|
########################################################################################
|
||||||
|
|
||||||
|
sub first($) {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
#-- 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#######################################################################################
|
||||||
|
#
|
||||||
|
# Search - 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 next($) {
|
||||||
|
my ($self,$mode)=@_;
|
||||||
|
|
||||||
|
#-- if the last call was the last one, no search
|
||||||
|
return undef if ( $self->{LastDeviceFlag} == 1 );
|
||||||
|
|
||||||
|
#-- now do the search
|
||||||
|
$self->search($mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub next_response($) {
|
||||||
|
my ($self,$mode) = @_;
|
||||||
|
|
||||||
|
#TODO find out where contents of @owx_fams come from:
|
||||||
|
my @owx_fams=();
|
||||||
|
|
||||||
|
return undef unless $self->search_response();
|
||||||
|
|
||||||
|
#-- character version of device ROM_ID, first byte = family
|
||||||
|
my $dev=sprintf("%02X.%02X%02X%02X%02X%02X%02X.%02X",@{$self->{ROM_ID}});
|
||||||
|
|
||||||
|
#--check if we really found a device
|
||||||
|
if( main::OWX_CRC($self->{ROM_ID})!= 0){
|
||||||
|
#-- reset the search
|
||||||
|
main::Log3($self->{name},1, "OWX_SER::Search CRC failed : $dev");
|
||||||
|
$self->{LastDiscrepancy} = 0;
|
||||||
|
$self->{LastDeviceFlag} = 0;
|
||||||
|
$self->{LastFamilyDiscrepancy} = 0;
|
||||||
|
die "OWX_SER::Search CRC failed : $dev";
|
||||||
|
}
|
||||||
|
|
||||||
|
#-- 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::Log3($self->{name},5, "OWX_SER::Search: device verified $dev");
|
||||||
|
#-- 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 (@{$self->{devs}}){
|
||||||
|
if( $dev eq $_ ){
|
||||||
|
#-- if present, set the last device found flag
|
||||||
|
$self->{LastDeviceFlag}=1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( $self->{LastDeviceFlag}!=1 ){
|
||||||
|
#-- push to list
|
||||||
|
push(@{$self->{devs}},$dev);
|
||||||
|
main::Log3($self->{name},5, "OWX_SER::Search: new device found $dev");
|
||||||
|
}
|
||||||
|
#-- mode was to discover alarm devices
|
||||||
|
} else {
|
||||||
|
for(my $i=0;$i<@{$self->{alarmdevs}};$i++){
|
||||||
|
if( $dev eq ${$self->{alarmdevs}}[$i] ){
|
||||||
|
#-- if present, set the last device found flag
|
||||||
|
$self->{LastDeviceFlag}=1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( $self->{LastDeviceFlag}!=1 ){
|
||||||
|
#--push to list
|
||||||
|
push(@{$self->{alarmdevs}},$dev);
|
||||||
|
main::Log3($self->{name},5, "OWX_SER::Search: new alarm device found $dev");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
Loading…
x
Reference in New Issue
Block a user