| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- ########################################################################################
- #
- # OWX_FRM.pm
- #
- # FHEM module providing hardware dependent functions for the FRM interface of OWX
- #
- # Norbert Truchsess
- #
- # $Id: OWX_FRM.pm 6378 2014-08-07 22:01:18Z ntruchsess $
- #
- ########################################################################################
- #
- # Provides the following methods for OWX
- #
- # Define
- # Init
- # Verify #TODO refactor Verify...
- # search
- # alarms
- # execute
- #
- ########################################################################################
- package OWX_FRM;
- use strict;
- use warnings;
- #add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
- BEGIN {
- if ( !grep( /FHEM\/lib$/, @INC ) ) {
- foreach my $inc ( grep( /FHEM$/, @INC ) ) {
- push @INC, $inc . "/lib";
- };
- };
- };
- use Device::Firmata::Constants qw/ :all /;
- use Time::HiRes qw( gettimeofday );
- use ProtoThreads;
- no warnings 'deprecated';
- sub new() {
- my ($class) = @_;
- return bless {
- interface => "firmata",
- #-- module version
- version => 4.2
- }, $class;
- }
- sub Define($$) {
- my ( $self, $hash, $def ) = @_;
- 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;
- return undef;
- }
- ########################################################################################
- #
- # 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 ) = @_;
- main::LoadModule("FRM");
- my $pin = $self->{pin};
- my $hash = $self->{hash};
- my $ret = main::FRM_Init_Pin_Client( $hash, [$pin], PIN_ONEWIRE );
- die $ret if ( defined $ret );
- my $firmata = main::FRM_Client_FirmataDevice($hash);
- $firmata->observe_onewire( $pin, \&FRM_OWX_observer, $self );
- $self->{devs} = [];
- if ( main::AttrVal( $hash->{NAME}, "buspower", "" ) eq "parasitic" ) {
- $firmata->onewire_config( $pin, 1 );
- }
- $firmata->onewire_search($pin);
- return $self;
- }
- sub exit()
- {
- my ($self) = @_;
- #TODO implement deconfigure onewire in firmata.
- };
- sub FRM_OWX_observer
- {
- my ( $data, $self ) = @_;
- my $command = $data->{command};
- COMMAND_HANDLER: {
- $command eq "READ_REPLY" and do {
- $self->{responses}->{$data->{id}} = $data->{data}; # // $data->{device} // "defaultid"}
- main::Log3 ($self->{name},5,"FRM_OWX_observer: READ_REPLY $data->{id}: ".join " ",map sprintf("%02X",$_),@{$data->{data}}) if $self->{debug};
- last;
- };
- ( $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" ) {
- $self->{devs} = \@owx_devices;
- main::Log3 ($self->{name},5,"FRM_OWX_observer: SEARCH_REPLY: ".join ",",@owx_devices) if $self->{debug};
- $self->{devs_timestamp} = gettimeofday();
- #TODO avoid OWX_ASYNC_AfterSearch to be called twice
- main::OWX_ASYNC_AfterSearch($self->{hash},\@owx_devices);
- } else {
- $self->{alarmdevs} = \@owx_devices;
- main::Log3 ($self->{name},5,"FRM_OWX_observer: SEARCH_ALARMS_REPLY: ".join ",",@owx_devices) if $self->{debug};
- $self->{alarmdevs_timestamp} = gettimeofday();
- #TODO avoid OWX_ASYNC_AfterAlarms to be called twice
- main::OWX_ASYNC_AfterAlarms($self->{hash},\@owx_devices);
- };
- last;
- };
- };
- main::OWX_ASYNC_RunTasks($self->{hash});
- };
- ########### functions implementing interface to OWX ##########
- 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} );
- }
- ########################################################################################
- #
- # factory methods for protothreads running discover, search, alarms and execute
- #
- ########################################################################################
- ########################################################################################
- #
- # Discover - Find devices on the 1-Wire bus
- #
- # Parameter hash = hash of bus master
- #
- # Return 1, if alarmed devices found, 0 otherwise.
- #
- ########################################################################################
- sub get_pt_discover() {
- my ($self) = @_;
- return PT_THREAD(sub {
- my ($thread) = @_;
- PT_BEGIN($thread);
- delete $self->{devs};
- main::FRM_Client_FirmataDevice($self->{hash})->onewire_search($self->{pin});
- main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
- PT_WAIT_UNTIL(defined $self->{devs});
- PT_EXIT($self->{devs});
- PT_END;
- });
- }
- ########################################################################################
- #
- # Alarms - Find devices on the 1-Wire bus, which have the alarm flag set
- #
- # Return number of alarmed devices
- #
- ########################################################################################
- sub get_pt_alarms() {
- my ($self) = @_;
- return PT_THREAD(sub {
- my ($thread) = @_;
- PT_BEGIN($thread);
- delete $self->{alarmdevs};
- main::FRM_Client_FirmataDevice($self->{hash})->onewire_search_alarms($self->{pin});
- main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
- PT_WAIT_UNTIL(defined $self->{alarmdevs});
- PT_EXIT($self->{alarmdevs});
- PT_END;
- });
- }
- sub get_pt_verify($) {
- my ($self,$dev) = @_;
- return PT_THREAD(sub {
- my ($thread) = @_;
- PT_BEGIN($thread);
- delete $self->{devs};
- main::FRM_Client_FirmataDevice($self->{hash})->onewire_search($self->{pin});
- main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
- PT_WAIT_UNTIL(defined $self->{devs});
- PT_EXIT(scalar(grep {$dev eq $_} @{$self->{devs}}));
- 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 get_pt_execute($$$$) {
- my ($self, $reset, $owx_dev, $writedata, $numread) = @_;
- return PT_THREAD(sub {
- my ($thread) = @_;
-
- PT_BEGIN($thread);
- if ( my $firmata = main::FRM_Client_FirmataDevice($self->{hash}) and my $pin = $self->{pin} ) {
- my @data = unpack "C*", $writedata if defined $writedata;
- my $id = $self->{id};
- my $ow_command = {
- 'reset' => $reset,
- 'skip' => defined($owx_dev) ? undef : 1,
- 'select' => defined($owx_dev) ? device_to_firmata($owx_dev) : undef,
- 'read' => $numread,
- 'write' => @data ? \@data : undef,
- 'delay' => undef,
- 'id' => $numread ? $id : undef
- };
- main::Log3 ($self->{name},5,"FRM_OWX_Execute: $id: $owx_dev [".join(" ",(map sprintf("%02X",$_),@data))."] numread: ".(defined $numread ? $numread : 0)) if $self->{debug};
- $firmata->onewire_command_series( $pin, $ow_command );
- if ($numread) {
- $thread->{id} = $id;
- $self->{id} = ( $id + 1 ) & 0xFFFF;
- delete $self->{responses}->{$id};
- main::OWX_ASYNC_TaskTimeout($self->{hash},gettimeofday+main::AttrVal($self->{name},"timeout",2));
- PT_WAIT_UNTIL(defined $self->{responses}->{$thread->{id}});
- my $ret = pack "C*", @{$self->{responses}->{$thread->{id}}};
- delete $self->{responses}->{$thread->{id}};
- PT_EXIT($ret);
- };
- };
- PT_END;
- });
- };
- sub poll() {
- my ( $self ) = @_;
- if ( my $frm = $self->{hash}->{IODev} ) {
- main::FRM_poll($frm);
- }
- };
- 1;
|