mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-01 20:20:10 +00:00
- check for IODev install error in Init, Get, Set, Attr and Undef - missing get/set argument metadata added - get/set argument verifier improved - moved define argument verification and decoding from Init to Define - error behaviour of Init, Get, Set and Attr standardized - annotaded module help of attributes for FHEMWEB git-svn-id: https://svn.fhem.de/fhem/trunk@23054 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
b94eb42175
commit
79a664abe5
@ -1,5 +1,8 @@
|
|||||||
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
|
||||||
# Do not insert empty lines here, update check depends on it.
|
# Do not insert empty lines here, update check depends on it.
|
||||||
|
- feature: 10_FRM: Device::Firmata, receiveTimeout, ... (forum #114552)
|
||||||
|
- feature: 20_FRM_*: Device::Firmata, ... (forum #114552)
|
||||||
|
- change: 20_FRM_LCD: removed, use I2C_LCD (forum #114552)
|
||||||
- bugfix: 70_DENON_AVR: fixed multizone bug (thx timmib)
|
- bugfix: 70_DENON_AVR: fixed multizone bug (thx timmib)
|
||||||
- feature: 49_Arlo: Added 2-factor authentication
|
- feature: 49_Arlo: Added 2-factor authentication
|
||||||
- bugfix: 73_AutoShuttersControl: fix IsDay Fn for weekend condition
|
- bugfix: 73_AutoShuttersControl: fix IsDay Fn for weekend condition
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
|
||||||
# FHEM module for one Firmata analog input pin
|
|
||||||
#
|
|
||||||
########################################################################################
|
|
||||||
#
|
|
||||||
# LICENSE AND COPYRIGHT
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013 ntruchess
|
|
||||||
# Copyright (C) 2016 jensb
|
|
||||||
#
|
|
||||||
# All rights reserved
|
|
||||||
#
|
|
||||||
# This script is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# The GNU General Public License can be found at
|
|
||||||
# http://www.gnu.org/copyleft/gpl.html.
|
|
||||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
|
||||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
|
||||||
#
|
|
||||||
# This script is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# This copyright notice MUST APPEAR in all copies of the script!
|
|
||||||
#
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata analog input pin
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2016 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -39,26 +44,24 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
|
# get command default return values
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"reading" => "",
|
"reading" => "",
|
||||||
"state" => "",
|
"state" => "",
|
||||||
"alarm-upper-threshold" => "off",
|
"alarm-upper-threshold" => "off",
|
||||||
"alarm-lower-threshold" => "off",
|
"alarm-lower-threshold" => "off",
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_AD_Initialize
|
||||||
FRM_AD_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -66,83 +69,102 @@ FRM_AD_Initialize($)
|
|||||||
$hash->{GetFn} = "FRM_AD_Get";
|
$hash->{GetFn} = "FRM_AD_Get";
|
||||||
$hash->{DefFn} = "FRM_Client_Define";
|
$hash->{DefFn} = "FRM_Client_Define";
|
||||||
$hash->{InitFn} = "FRM_AD_Init";
|
$hash->{InitFn} = "FRM_AD_Init";
|
||||||
|
|
||||||
$hash->{AttrList} = "IODev upper-threshold lower-threshold $main::readingFnAttributes";
|
$hash->{AttrList} = "IODev upper-threshold lower-threshold $main::readingFnAttributes";
|
||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_AD_Init
|
||||||
FRM_AD_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_ANALOG);
|
my $name = $hash->{NAME};
|
||||||
return $ret if (defined $ret);
|
|
||||||
my $firmata = $hash->{IODev}->{FirmataDevice};
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
my $name = $hash->{NAME};
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
my $resolution = 10;
|
}
|
||||||
if (defined $firmata->{metadata}{analog_resolutions}) {
|
|
||||||
$resolution = $firmata->{metadata}{analog_resolutions}{$hash->{PIN}}
|
my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_ANALOG);
|
||||||
}
|
if (defined($ret)) {
|
||||||
$hash->{resolution} = $resolution;
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 1024;
|
return $ret;
|
||||||
eval {
|
}
|
||||||
$firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash);
|
|
||||||
};
|
eval {
|
||||||
return FRM_Catch($@) if $@;
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
my $resolution = 10;
|
||||||
$main::attr{$name}{"stateFormat"} = "reading";
|
if (defined $firmata->{metadata}{analog_resolutions}) {
|
||||||
}
|
$resolution = $firmata->{metadata}{analog_resolutions}{$hash->{PIN}}
|
||||||
if (! (defined AttrVal($name,"event-min-interval",undef))) {
|
}
|
||||||
$main::attr{$name}{"event-min-interval"} = 5;
|
$hash->{resolution} = $resolution;
|
||||||
}
|
$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 1024;
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
$firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash);
|
||||||
return undef;
|
};
|
||||||
|
if ($@) {
|
||||||
|
$ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(defined AttrVal($name,"stateFormat",undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "reading";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(defined AttrVal($name,"event-min-interval",undef))) {
|
||||||
|
$main::attr{$name}{"event-min-interval"} = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_AD_observer
|
||||||
FRM_AD_observer
|
|
||||||
{
|
{
|
||||||
my ($pin,$old,$new,$hash) = @_;
|
my ($pin,$old,$new,$hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
Log3 $name,5,"onAnalogMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
|
Log3 $name, 5, "$name: observer pin: ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
|
||||||
main::readingsBeginUpdate($hash);
|
main::readingsBeginUpdate($hash);
|
||||||
main::readingsBulkUpdate($hash,"reading",$new,1);
|
main::readingsBulkUpdate($hash,"reading",$new,1);
|
||||||
my $upperthresholdalarm = ReadingsVal($name,"alarm-upper-threshold","off");
|
my $upperthresholdalarm = ReadingsVal($name,"alarm-upper-threshold","off");
|
||||||
if ( $new < AttrVal($name,"upper-threshold",$hash->{".max"}) ) {
|
if ( $new < AttrVal($name,"upper-threshold",$hash->{".max"}) ) {
|
||||||
if ( $upperthresholdalarm eq "on" ) {
|
if ( $upperthresholdalarm eq "on" ) {
|
||||||
main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1);
|
main::readingsBulkUpdate($hash,"alarm-upper-threshold","off",1);
|
||||||
}
|
}
|
||||||
my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off");
|
my $lowerthresholdalarm = ReadingsVal($name,"alarm-lower-threshold","off");
|
||||||
if ( $new > AttrVal($name,"lower-threshold",-1) ) {
|
if ( $new > AttrVal($name,"lower-threshold",-1) ) {
|
||||||
if ( $lowerthresholdalarm eq "on" ) {
|
if ( $lowerthresholdalarm eq "on" ) {
|
||||||
main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1);
|
main::readingsBulkUpdate($hash,"alarm-lower-threshold","off",1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( $lowerthresholdalarm eq "off" ) {
|
if ( $lowerthresholdalarm eq "off" ) {
|
||||||
main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1);
|
main::readingsBulkUpdate($hash,"alarm-lower-threshold","on",1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ( $upperthresholdalarm eq "off" ) {
|
if ( $upperthresholdalarm eq "off" ) {
|
||||||
main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1);
|
main::readingsBulkUpdate($hash,"alarm-upper-threshold","on",1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
main::readingsEndUpdate($hash,1);
|
main::readingsEndUpdate($hash,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_AD_Get
|
||||||
FRM_AD_Get($)
|
|
||||||
{
|
{
|
||||||
my ($hash,@a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
my $name = shift @a;
|
|
||||||
my $cmd = shift @a;
|
return "get command missing" if(!defined($cmd));
|
||||||
my $ret;
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
|
|
||||||
ARGUMENT_HANDLER: {
|
ARGUMENT_HANDLER: {
|
||||||
$cmd eq "reading" and do {
|
$cmd eq "reading" and do {
|
||||||
eval {
|
my $result = eval {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
return FRM_Client_FirmataDevice($hash)->analog_read($hash->{PIN});
|
return FRM_Client_FirmataDevice($hash)->analog_read($hash->{PIN});
|
||||||
};
|
};
|
||||||
return $@;
|
return FRM_Catch($@) if ($@);
|
||||||
|
return $result;
|
||||||
};
|
};
|
||||||
( $cmd eq "alarm-upper-threshold" or $cmd eq "alarm-lower-threshold" or $cmd eq "state" ) and do {
|
( $cmd eq "alarm-upper-threshold" or $cmd eq "alarm-lower-threshold" or $cmd eq "state" ) and do {
|
||||||
return main::ReadingsVal($name,"count",$gets{$cmd});
|
return main::ReadingsVal($name,"count",$gets{$cmd});
|
||||||
@ -151,8 +173,8 @@ FRM_AD_Get($)
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_AD_Attr
|
||||||
FRM_AD_Attr($$$$) {
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
eval {
|
eval {
|
||||||
@ -169,9 +191,9 @@ FRM_AD_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,46 +201,65 @@ FRM_AD_Attr($$$$) {
|
|||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
CHANGES
|
=head1 CHANGES
|
||||||
|
|
||||||
2016 jensb
|
2016 jensb
|
||||||
o modified sub FRM_AD_Init to catch exceptions and return error message
|
o modified sub FRM_AD_Init to catch exceptions and return error message
|
||||||
|
|
||||||
19.01.2018 jensb
|
19.01.2018 jensb
|
||||||
o support analog resolution depending on device capability
|
o support analog resolution depending on device capability
|
||||||
|
|
||||||
|
24.08.2020 jensb
|
||||||
|
o check for IODev install error in Init and Get
|
||||||
|
o prototypes removed
|
||||||
|
o set argument verifier added
|
||||||
|
|
||||||
|
22.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
=pod
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
=item device
|
=item device
|
||||||
|
|
||||||
=item summary Firmata: analog input
|
=item summary Firmata: analog input
|
||||||
=item summary_DE Firmata: analog Eingang
|
|
||||||
|
=item summary_DE Firmata: analoger Eingang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_AD"></a>
|
<a name="FRM_AD"/>
|
||||||
<h3>FRM_AD</h3>
|
<h3>FRM_AD</h3>
|
||||||
<ul>
|
<ul>
|
||||||
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
||||||
that should be configured as an analog input.<br><br>
|
that should be configured as an analog input.<br><br>
|
||||||
|
|
||||||
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading "<a href="#FRMinternals">analog_pins</a>"<br>
|
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading "<a href="#FRMinternals">analog_pins</a>"<br>
|
||||||
of the FRM device (after connecting to the Firmata device) to be used as analog input.<br><br>
|
of the FRM device (after connecting to the Firmata device) to be used as analog input.<br><br>
|
||||||
|
|
||||||
<a name="FRM_ADdefine"></a>
|
<a name="FRM_ADdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_AD <pin></code><br><br>
|
<code>define <name> FRM_AD <pin></code><br><br>
|
||||||
|
|
||||||
Defines the FRM_AD device. <pin> is the arduino-pin to use.
|
Defines the FRM_AD device. <pin> is the arduino-pin to use.
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_ADset"></a>
|
<a name="FRM_ADset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
N/A<br>
|
N/A<br>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_ADget"></a>
|
<a name="FRM_ADget"/>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>reading<br>
|
<li>reading<br>
|
||||||
@ -236,35 +277,52 @@ FRM_AD_Attr($$$$) {
|
|||||||
<li>state<br>
|
<li>state<br>
|
||||||
returns the 'state' reading</li>
|
returns the 'state' reading</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_ADattr"></a>
|
<a name="FRM_ADattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>upper-threshold<br>
|
<a name="upper-threshold"/>
|
||||||
sets the 'upper-threshold'. Whenever the 'reading' exceeds this value 'alarm-upper-threshold' is set to 'on'<br>
|
<li>upper-threshold<br>
|
||||||
As soon 'reading' falls below the 'upper-threshold' 'alarm-upper-threshold' turns 'off' again<br>
|
sets the 'upper-threshold'. Whenever the 'reading' exceeds this value 'alarm-upper-threshold' is set to 'on'<br>
|
||||||
Defaults to the max pin resolution plus one.</li>
|
As soon 'reading' falls below the 'upper-threshold' 'alarm-upper-threshold' turns 'off' again<br>
|
||||||
<li>lower-threshold<br>
|
Defaults to the max pin resolution plus one.</li>
|
||||||
sets the 'lower-threshold'. Whenever the 'reading' falls below this value 'alarm-lower-threshold' is set to 'on'<br>
|
|
||||||
As soon 'reading' rises above the 'lower-threshold' 'alarm-lower-threshold' turns 'off' again<br>
|
<a name="lower-threshold"/>
|
||||||
Defaults to -1.</li>
|
<li>lower-threshold<br>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
sets the 'lower-threshold'. Whenever the 'reading' falls below this value 'alarm-lower-threshold' is set to 'on'<br>
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
As soon 'reading' rises above the 'lower-threshold' 'alarm-lower-threshold' turns 'off' again<br>
|
||||||
than one FRM-device defined.)
|
Defaults to -1.</li>
|
||||||
</li>
|
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
<a name="IODev"/>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li><a href="#IODev">IODev</a><br>
|
||||||
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_ADnotes"></a>
|
<a name="FRM_ADnotes"/>
|
||||||
<b>Notes</b><br>
|
<b>Notes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>attribute <i>stateFormat</i><br>
|
<li>attribute <i>stateFormat</i><br>
|
||||||
In most cases it is a good idea to assign "reading" to the attribute <i>stateFormat</i>. This will show the
|
In most cases it is a good idea to assign "reading" to the attribute <i>stateFormat</i>. This will show the
|
||||||
current value of the pin in the web interface.
|
current value of the pin in the web interface.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_AD"/>
|
||||||
|
<h3>FRM_AD</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_AD gibt es nur auf <a href="commandref.html#FRM_AD">Englisch</a>. <br>
|
||||||
|
</ul> <br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
|
||||||
# FHEM module for one Firmata digial input pin
|
|
||||||
#
|
|
||||||
########################################################################################
|
|
||||||
#
|
|
||||||
# LICENSE AND COPYRIGHT
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013 ntruchess
|
|
||||||
# Copyright (C) 2018 jensb
|
|
||||||
#
|
|
||||||
# All rights reserved
|
|
||||||
#
|
|
||||||
# This script is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# The GNU General Public License can be found at
|
|
||||||
# http://www.gnu.org/copyleft/gpl.html.
|
|
||||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
|
||||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
|
||||||
#
|
|
||||||
# This script is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# This copyright notice MUST APPEAR in all copies of the script!
|
|
||||||
#
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata digial input pin
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2018 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -39,22 +44,22 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
|
# default values for Attr
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"alarm" => "",
|
"alarm" => "",
|
||||||
"count" => 0,
|
"count" => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# default values for Get
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"reading" => "",
|
"reading" => "",
|
||||||
"state" => "",
|
"state" => "",
|
||||||
@ -62,8 +67,7 @@ my %gets = (
|
|||||||
"alarm" => "off"
|
"alarm" => "off"
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_IN_Initialize
|
||||||
FRM_IN_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -78,8 +82,7 @@ FRM_IN_Initialize($)
|
|||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_PinModePullupSupported
|
||||||
FRM_IN_PinModePullupSupported($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
my $iodev = $hash->{IODev};
|
my $iodev = $hash->{IODev};
|
||||||
@ -88,133 +91,160 @@ FRM_IN_PinModePullupSupported($)
|
|||||||
return defined($pullupPins);
|
return defined($pullupPins);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_Init
|
||||||
FRM_IN_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
if (FRM_IN_PinModePullupSupported($hash)) {
|
my $name = $hash->{NAME};
|
||||||
my $pullup = AttrVal($hash->{NAME},"internal-pullup","off");
|
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,defined($pullup) && ($pullup eq "on")? PIN_PULLUP : PIN_INPUT);
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
return $ret if (defined $ret);
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
eval {
|
}
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
|
||||||
my $pin = $hash->{PIN};
|
if (FRM_IN_PinModePullupSupported($hash)) {
|
||||||
$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
|
my $pullup = AttrVal($name, "internal-pullup", "off");
|
||||||
};
|
my $ret = FRM_Init_Pin_Client($hash,$args,defined($pullup) && ($pullup eq "on")? Device::Firmata::Constants->PIN_PULLUP : Device::Firmata::Constants->PIN_INPUT);
|
||||||
return FRM_Catch($@) if $@;
|
return $ret if (defined $ret);
|
||||||
} else {
|
eval {
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_INPUT);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
return $ret if (defined $ret);
|
my $pin = $hash->{PIN};
|
||||||
eval {
|
$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
};
|
||||||
my $pin = $hash->{PIN};
|
if ($@) {
|
||||||
if (defined (my $pullup = AttrVal($hash->{NAME},"internal-pullup",undef))) {
|
my $ret = FRM_Catch($@);
|
||||||
$firmata->digital_write($pin,$pullup eq "on" ? 1 : 0);
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
}
|
return $ret;
|
||||||
$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
|
}
|
||||||
};
|
} else {
|
||||||
return FRM_Catch($@) if $@;
|
my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_INPUT);
|
||||||
}
|
return $ret if (defined $ret);
|
||||||
if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) {
|
eval {
|
||||||
$main::attr{$hash->{NAME}}{"stateFormat"} = "reading";
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
}
|
my $pin = $hash->{PIN};
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
if (defined(my $pullup = AttrVal($name, "internal-pullup", undef))) {
|
||||||
return undef;
|
$firmata->digital_write($pin,$pullup eq "on" ? 1 : 0);
|
||||||
|
}
|
||||||
|
$firmata->observe_digital($pin,\&FRM_IN_observer,$hash);
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(defined AttrVal($name, "stateFormat", undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "reading";
|
||||||
|
}
|
||||||
|
|
||||||
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_observer
|
||||||
FRM_IN_observer($$$$)
|
|
||||||
{
|
{
|
||||||
my ($pin,$last,$new,$hash) = @_;
|
my ($pin,$last,$new,$hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
my $old = ReadingsVal($name, "reading", undef);
|
|
||||||
if (defined($old)) {
|
my $old = ReadingsVal($name, "reading", undef);
|
||||||
$old = $old eq "on" ? PIN_HIGH : PIN_LOW;
|
if (defined($old)) {
|
||||||
}
|
$old = $old eq "on" ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW;
|
||||||
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
}
|
||||||
$new = $new == PIN_LOW ? PIN_HIGH : PIN_LOW;
|
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
||||||
}
|
$new = $new == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW;
|
||||||
Log3 $name, 5, "$name observer pin: $pin, old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
|
}
|
||||||
my $changed = !defined($old) || $old != $new;
|
Log3 $name, 5, "$name: observer pin: $pin, old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--");
|
||||||
if ($changed) {
|
|
||||||
main::readingsBeginUpdate($hash);
|
my $changed = !defined($old) || $old != $new;
|
||||||
if (defined (my $mode = main::AttrVal($name,"count-mode",undef))) {
|
if ($changed) {
|
||||||
if (($mode eq "both")
|
main::readingsBeginUpdate($hash);
|
||||||
or (($mode eq "rising") and ($new == PIN_HIGH))
|
if (defined (my $mode = main::AttrVal($name,"count-mode",undef))) {
|
||||||
or (($mode eq "falling") and ($new == PIN_LOW))) {
|
if (($mode eq "both")
|
||||||
my $count = main::ReadingsVal($name,"count",0);
|
or (($mode eq "rising") and ($new == Device::Firmata::Constants->PIN_HIGH))
|
||||||
$count++;
|
or (($mode eq "falling") and ($new == Device::Firmata::Constants->PIN_LOW))) {
|
||||||
if (defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) {
|
my $count = main::ReadingsVal($name,"count",0);
|
||||||
if ( $count > $threshold ) {
|
$count++;
|
||||||
if (AttrVal($name,"reset-on-threshold-reached","no") eq "yes") {
|
if (defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) {
|
||||||
$count=0;
|
if ( $count > $threshold ) {
|
||||||
main::readingsBulkUpdate($hash,"alarm","on",1);
|
if (AttrVal($name,"reset-on-threshold-reached","no") eq "yes") {
|
||||||
} elsif ( main::ReadingsVal($name,"alarm","off") ne "on" ) {
|
$count=0;
|
||||||
main::readingsBulkUpdate($hash,"alarm","on",1);
|
main::readingsBulkUpdate($hash,"alarm","on",1);
|
||||||
}
|
} elsif ( main::ReadingsVal($name,"alarm","off") ne "on" ) {
|
||||||
}
|
main::readingsBulkUpdate($hash,"alarm","on",1);
|
||||||
}
|
}
|
||||||
main::readingsBulkUpdate($hash,"count",$count,1);
|
}
|
||||||
}
|
}
|
||||||
};
|
main::readingsBulkUpdate($hash,"count",$count,1);
|
||||||
main::readingsBulkUpdate($hash,"reading",$new == PIN_HIGH ? "on" : "off", 1);
|
}
|
||||||
main::readingsEndUpdate($hash,1);
|
};
|
||||||
}
|
main::readingsBulkUpdate($hash, "reading", $new == Device::Firmata::Constants->PIN_HIGH ? "on" : "off", 1);
|
||||||
|
main::readingsEndUpdate($hash,1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_Set
|
||||||
FRM_IN_Set($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "set command missing" if(@a < 2 || !defined($a[1]));
|
|
||||||
return "unknown set command '$a[1]', choose one of " . join(" ", sort keys %sets) if(!defined($sets{$a[1]}));
|
return "set command missing" if(!defined($cmd));
|
||||||
my $command = $a[1];
|
return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if(!defined($sets{$cmd}));
|
||||||
my $value = $a[2];
|
return "$cmd requires 1 argument" unless (@a == 1);
|
||||||
|
|
||||||
|
my $value = shift @a;
|
||||||
COMMAND_HANDLER: {
|
COMMAND_HANDLER: {
|
||||||
$command eq "alarm" and do {
|
$cmd eq "alarm" and do {
|
||||||
return undef if (!($value eq "off" or $value eq "on"));
|
return undef if (!($value eq "off" or $value eq "on"));
|
||||||
main::readingsSingleUpdate($hash,"alarm",$value,1);
|
main::readingsSingleUpdate($hash,"alarm",$value,1);
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$command eq "count" and do {
|
$cmd eq "count" and do {
|
||||||
main::readingsSingleUpdate($hash,"count",$value,1);
|
main::readingsSingleUpdate($hash,"count",$value,1);
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_Get
|
||||||
FRM_IN_Get($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "get command missing" if(@a < 2 || !defined($a[1]));
|
|
||||||
return "unknown get command '$a[1]', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$a[1]}));
|
return "get command missing" if(!defined($cmd));
|
||||||
my $name = shift @a;
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
my $cmd = shift @a;
|
|
||||||
ARGUMENT_HANDLER: {
|
ARGUMENT_HANDLER: {
|
||||||
( $cmd eq "reading" ) and do {
|
$cmd eq "reading" and do {
|
||||||
my $last;
|
my $last;
|
||||||
eval {
|
eval {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
$last = FRM_Client_FirmataDevice($hash)->digital_read($hash->{PIN});
|
$last = FRM_Client_FirmataDevice($hash)->digital_read($hash->{PIN});
|
||||||
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
||||||
$last = $last == PIN_LOW ? PIN_HIGH : PIN_LOW;
|
$last = $last == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return FRM_Catch($@) if $@;
|
if ($@) {
|
||||||
return $last == PIN_HIGH ? "on" : "off";
|
my $ret = FRM_Catch($@);
|
||||||
|
$hash->{STATE} = "get $cmd error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
|
}
|
||||||
|
return $last == Device::Firmata::Constants->PIN_HIGH ? "on" : "off";
|
||||||
};
|
};
|
||||||
( $cmd eq "count" or $cmd eq "alarm" or $cmd eq "state" ) and do {
|
|
||||||
|
($cmd eq "count" or $cmd eq "alarm" or $cmd eq "state") and do {
|
||||||
return main::ReadingsVal($name,$cmd,$gets{$cmd});
|
return main::ReadingsVal($name,$cmd,$gets{$cmd});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_IN_Attr
|
||||||
FRM_IN_Attr($$$$) {
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
my $pin = $hash->{PIN};
|
my $pin = $hash->{PIN};
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
if ($command eq "set") {
|
if ($command eq "set") {
|
||||||
ARGUMENT_HANDLER: {
|
ARGUMENT_HANDLER: {
|
||||||
@ -225,12 +255,14 @@ FRM_IN_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "count-mode" and do {
|
$attribute eq "count-mode" and do {
|
||||||
if ($value ne "none" and !defined main::ReadingsVal($name,"count",undef)) {
|
if ($value ne "none" and !defined main::ReadingsVal($name,"count",undef)) {
|
||||||
main::readingsSingleUpdate($main::defs{$name},"count",$sets{count},1);
|
main::readingsSingleUpdate($main::defs{$name},"count",$sets{count},1);
|
||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "reset-on-threshold-reached" and do {
|
$attribute eq "reset-on-threshold-reached" and do {
|
||||||
if ($value eq "yes"
|
if ($value eq "yes"
|
||||||
and defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) {
|
and defined (my $threshold = main::AttrVal($name,"count-threshold",undef))) {
|
||||||
@ -240,6 +272,7 @@ FRM_IN_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "count-threshold" and do {
|
$attribute eq "count-threshold" and do {
|
||||||
if (main::ReadingsVal($name,"count",0) > $value) {
|
if (main::ReadingsVal($name,"count",0) > $value) {
|
||||||
main::readingsBeginUpdate($hash);
|
main::readingsBeginUpdate($hash);
|
||||||
@ -253,11 +286,15 @@ FRM_IN_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "internal-pullup" and do {
|
$attribute eq "internal-pullup" and do {
|
||||||
if ($main::init_done) {
|
if ($main::init_done) {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
if (FRM_IN_PinModePullupSupported($hash)) {
|
if (FRM_IN_PinModePullupSupported($hash)) {
|
||||||
$firmata->pin_mode($pin,$value eq "on"? PIN_PULLUP : PIN_INPUT);
|
$firmata->pin_mode($pin, $value eq "on"? Device::Firmata::Constants->PIN_PULLUP : Device::Firmata::Constants->PIN_INPUT);
|
||||||
} else {
|
} else {
|
||||||
$firmata->digital_write($pin,$value eq "on" ? 1 : 0);
|
$firmata->digital_write($pin,$value eq "on" ? 1 : 0);
|
||||||
#ignore any errors here, the attribute-value will be applied next time FRM_IN_init() is called.
|
#ignore any errors here, the attribute-value will be applied next time FRM_IN_init() is called.
|
||||||
@ -265,11 +302,15 @@ FRM_IN_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "activeLow" and do {
|
$attribute eq "activeLow" and do {
|
||||||
my $oldval = AttrVal($hash->{NAME},"activeLow","no");
|
my $oldval = AttrVal($hash->{NAME},"activeLow","no");
|
||||||
if ($oldval ne $value) {
|
if ($oldval ne $value) {
|
||||||
$main::attr{$hash->{NAME}}{activeLow} = $value;
|
$main::attr{$hash->{NAME}}{activeLow} = $value;
|
||||||
if ($main::init_done) {
|
if ($main::init_done) {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash);
|
FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash);
|
||||||
}
|
}
|
||||||
@ -280,16 +321,23 @@ FRM_IN_Attr($$$$) {
|
|||||||
} elsif ($command eq "del") {
|
} elsif ($command eq "del") {
|
||||||
ARGUMENT_HANDLER: {
|
ARGUMENT_HANDLER: {
|
||||||
$attribute eq "internal-pullup" and do {
|
$attribute eq "internal-pullup" and do {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
if (FRM_IN_PinModePullupSupported($hash)) {
|
if (FRM_IN_PinModePullupSupported($hash)) {
|
||||||
$firmata->pin_mode($pin,PIN_INPUT);
|
$firmata->pin_mode($pin, Device::Firmata::Constants->PIN_INPUT);
|
||||||
} else {
|
} else {
|
||||||
$firmata->digital_write($pin,0);
|
$firmata->digital_write($pin,0);
|
||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "activeLow" and do {
|
$attribute eq "activeLow" and do {
|
||||||
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
if (AttrVal($hash->{NAME},"activeLow","no") eq "yes") {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
delete $main::attr{$hash->{NAME}}{activeLow};
|
delete $main::attr{$hash->{NAME}}{activeLow};
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash);
|
FRM_IN_observer($pin,undef,$firmata->digital_read($pin),$hash);
|
||||||
@ -299,9 +347,10 @@ FRM_IN_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (my $error = FRM_Catch($@)) {
|
if ($@) {
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$error;
|
my $ret = FRM_Catch($@);
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$error;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,11 +358,11 @@ FRM_IN_Attr($$$$) {
|
|||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
CHANGES
|
=head1 CHANGES
|
||||||
|
|
||||||
15.02.2019 jensb
|
15.02.2019 jensb
|
||||||
o bugfix: change detection no longer assumes that reading "reading" is defined
|
o bugfix: change detection no longer assumes that reading "reading" is defined
|
||||||
|
|
||||||
04.11.2018 jensb
|
04.11.2018 jensb
|
||||||
o bugfix: get alarm/reading/state
|
o bugfix: get alarm/reading/state
|
||||||
o feature: remove unused FHEMWEB input field from all get commands
|
o feature: remove unused FHEMWEB input field from all get commands
|
||||||
@ -324,8 +373,17 @@ FRM_IN_Attr($$$$) {
|
|||||||
03.01.2018 jensb
|
03.01.2018 jensb
|
||||||
o implemented Firmata 2.5 feature PIN_MODE_PULLUP (requires perl-firmata 0.64 or higher)
|
o implemented Firmata 2.5 feature PIN_MODE_PULLUP (requires perl-firmata 0.64 or higher)
|
||||||
|
|
||||||
|
24.08.2020 jensb
|
||||||
|
o check for IODev install error in Init, Get and Attr
|
||||||
|
o prototypes removed
|
||||||
|
o set argument verifier improved
|
||||||
|
|
||||||
|
19.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
=head1 FHEM COMMANDREF METADATA
|
=head1 FHEM COMMANDREF METADATA
|
||||||
@ -344,7 +402,7 @@ FRM_IN_Attr($$$$) {
|
|||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_IN"></a>
|
<a name="FRM_IN"/>
|
||||||
<h3>FRM_IN</h3>
|
<h3>FRM_IN</h3>
|
||||||
<ul>
|
<ul>
|
||||||
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
||||||
@ -354,7 +412,7 @@ FRM_IN_Attr($$$$) {
|
|||||||
the internal reading <a href="#FRMinternals">"input_pins" or "pullup_pins"</a>
|
the internal reading <a href="#FRMinternals">"input_pins" or "pullup_pins"</a>
|
||||||
of the FRM device (after connecting to the Firmata device) to be used as digital input with or without pullup.<br><br>
|
of the FRM device (after connecting to the Firmata device) to be used as digital input with or without pullup.<br><br>
|
||||||
|
|
||||||
<a name="FRM_INdefine"></a>
|
<a name="FRM_INdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_IN <pin></code> <br>
|
<code>define <name> FRM_IN <pin></code> <br>
|
||||||
@ -362,7 +420,7 @@ FRM_IN_Attr($$$$) {
|
|||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a name="FRM_INset"></a>
|
<a name="FRM_INset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>alarm on|off<br>
|
<li>alarm on|off<br>
|
||||||
@ -373,47 +431,64 @@ FRM_IN_Attr($$$$) {
|
|||||||
The counter is incremented depending on the attribute 'count-mode'.</li>
|
The counter is incremented depending on the attribute 'count-mode'.</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_INget"></a>
|
<a name="FRM_INget"/>
|
||||||
<b>Get</b>
|
<b>Get</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li>reading<br>
|
<li>reading<br>
|
||||||
returns the logical state of the input pin last received from the Firmata device depending on the attribute 'activeLow'.
|
returns the logical state of the input pin last received from the Firmata device depending on the attribute 'activeLow'.
|
||||||
Values are 'on' and 'off'.<br></li>
|
Values are 'on' and 'off'.<br></li>
|
||||||
<li>count<br>
|
<li>count<br>
|
||||||
returns the current counter value. Contains the number of toggles reported by the Fimata device on this input pin.
|
returns the current counter value. Contains the number of toggles reported by the Fimata device on this input pin.
|
||||||
Depending on the attribute 'count-mode' every rising or falling edge (or both) is counted.</li>
|
Depending on the attribute 'count-mode' every rising or falling edge (or both) is counted.</li>
|
||||||
<li>alarm<br>
|
<li>alarm<br>
|
||||||
returns the 'alarm' reading. Values are 'on' and 'off' (Defaults to 'off').
|
returns the 'alarm' reading. Values are 'on' and 'off' (Defaults to 'off').
|
||||||
The 'alarm' reading doesn't clear itself, it has to be set to 'off' explicitly.</li>
|
The 'alarm' reading doesn't clear itself, it has to be set to 'off' explicitly.</li>
|
||||||
<li>state<br>
|
<li>state<br>
|
||||||
returns the 'state' reading</li>
|
returns the 'state' reading</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_INattr"></a>
|
<a name="FRM_INattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>activeLow yes|no<br>
|
<a name="activeLow"/>
|
||||||
inverts the logical state of the pin reading if set to yes (defaults to 'no').</li>
|
<li>activeLow yes|no<br>
|
||||||
<li>count-mode none|rising|falling|both<br>
|
inverts the logical state of the pin reading if set to yes (defaults to 'no').
|
||||||
Determines whether 'rising' (transitions from 'off' to 'on') of falling (transitions from 'on' to 'off')
|
</li>
|
||||||
edges (or 'both') are counted (defaults to 'none').</li>
|
|
||||||
<li>count-threshold <number><br>
|
<a name="count-mode"/>
|
||||||
sets the threshold-value for the counter - if defined whenever 'count' exceeds the 'count-threshold' the 'alarm' reading is
|
<li>count-mode none|rising|falling|both<br>
|
||||||
set to 'on' (defaults to undefined). Use 'set alarm off' to clear the alarm.</li>
|
Determines whether 'rising' (transitions from 'off' to 'on') of falling (transitions from 'on' to 'off')
|
||||||
<li>reset-on-threshold-reached yes|no<br>
|
edges (or 'both') are counted (defaults to 'none').
|
||||||
if set to 'yes' reset the counter to 0 when the threshold is reached (defaults to 'no').
|
</li>
|
||||||
</li>
|
|
||||||
<li>internal-pullup on|off<br>
|
<a name="count-threshold"/>
|
||||||
enables/disables the internal pullup resistor of the Firmata pin (defaults to 'off'). Requires hardware and firmware support.
|
<li>count-threshold <number><br>
|
||||||
</li>
|
sets the threshold-value for the counter - if defined whenever 'count' exceeds the 'count-threshold'
|
||||||
<li><a href="#IODev">IODev</a><br>
|
the 'alarm' reading is set to 'on' (defaults to undefined). Use 'set alarm off' to clear the alarm.
|
||||||
specify which <a href="#FRM">FRM</a> to use.
|
</li>
|
||||||
</li>
|
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
<a name="reset-on-threshold-reached"/>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li>reset-on-threshold-reached yes|no<br>
|
||||||
|
if set to 'yes' reset the counter to 0 when the threshold is reached (defaults to 'no').
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<a name="internal-pullup"/>
|
||||||
|
<li>internal-pullup on|off<br>
|
||||||
|
enables/disables the internal pullup resistor of the Firmata pin (defaults to 'off'). Requires hardware
|
||||||
|
and firmware support.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<a name="IODev"/>
|
||||||
|
<li><a href="#IODev">IODev</a><br>
|
||||||
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_INnotes"></a>
|
<a name="FRM_INnotes"/>
|
||||||
<b>Notes</b><br>
|
<b>Notes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>attribute <i>stateFormat</i><br>
|
<li>attribute <i>stateFormat</i><br>
|
||||||
@ -430,7 +505,7 @@ FRM_IN_Attr($$$$) {
|
|||||||
|
|
||||||
=begin html_DE
|
=begin html_DE
|
||||||
|
|
||||||
<a name="FRM_IN"></a>
|
<a name="FRM_IN"/>
|
||||||
<h3>FRM_IN</h3>
|
<h3>FRM_IN</h3>
|
||||||
<ul>
|
<ul>
|
||||||
Die Modulbeschreibung von FRM_IN gibt es nur auf <a href="commandref.html#FRM_IN">Englisch</a>. <br>
|
Die Modulbeschreibung von FRM_IN gibt es nur auf <a href="commandref.html#FRM_IN">Englisch</a>. <br>
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
|
||||||
# FHEM module for one Firmata digial output pin
|
|
||||||
#
|
|
||||||
########################################################################################
|
|
||||||
#
|
|
||||||
# LICENSE AND COPYRIGHT
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013 ntruchess
|
|
||||||
# Copyright (C) 2016 jensb
|
|
||||||
#
|
|
||||||
# All rights reserved
|
|
||||||
#
|
|
||||||
# This script is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# The GNU General Public License can be found at
|
|
||||||
# http://www.gnu.org/copyleft/gpl.html.
|
|
||||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
|
||||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
|
||||||
#
|
|
||||||
# This script is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# This copyright notice MUST APPEAR in all copies of the script!
|
|
||||||
#
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata digial output pin
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2016 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -39,19 +44,24 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
use SetExtensions;
|
use SetExtensions;
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
sub
|
|
||||||
FRM_OUT_Initialize($)
|
# number of arguments
|
||||||
|
my %sets = (
|
||||||
|
"on:noArg" => 0,
|
||||||
|
"off:noArg" => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub FRM_OUT_Initialize
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -61,96 +71,127 @@ FRM_OUT_Initialize($)
|
|||||||
$hash->{UndefFn} = "FRM_Client_Undef";
|
$hash->{UndefFn} = "FRM_Client_Undef";
|
||||||
$hash->{AttrFn} = "FRM_OUT_Attr";
|
$hash->{AttrFn} = "FRM_OUT_Attr";
|
||||||
$hash->{StateFn} = "FRM_OUT_State";
|
$hash->{StateFn} = "FRM_OUT_State";
|
||||||
|
|
||||||
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off activeLow:yes,no IODev valueMode:send,receive,bidirectional $main::readingFnAttributes";
|
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off activeLow:yes,no IODev valueMode:send,receive,bidirectional $main::readingFnAttributes";
|
||||||
|
|
||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_OUT_Init
|
||||||
FRM_OUT_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_OUTPUT);
|
my $name = $hash->{NAME};
|
||||||
return $ret if (defined $ret);
|
|
||||||
eval {
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
my $pin = $hash->{PIN};
|
}
|
||||||
$firmata->observe_digital($pin,\&FRM_OUT_observer,$hash);
|
|
||||||
};
|
my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_OUTPUT);
|
||||||
my $name = $hash->{NAME};
|
if (defined($ret)) {
|
||||||
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
$main::attr{$name}{"stateFormat"} = "value";
|
return $ret;
|
||||||
}
|
}
|
||||||
my $value = ReadingsVal($name,"value",undef);
|
|
||||||
if (!defined($value)) {
|
eval {
|
||||||
readingsSingleUpdate($hash,"value","off",0);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
}
|
my $pin = $hash->{PIN};
|
||||||
if (AttrVal($hash->{NAME},"restoreOnReconnect", "on") eq "on") {
|
$firmata->observe_digital($pin,\&FRM_OUT_observer,$hash);
|
||||||
FRM_OUT_Set($hash,$name,$value);
|
};
|
||||||
}
|
if ($@) {
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
$ret = FRM_Catch($@);
|
||||||
return undef;
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(defined AttrVal($name,"stateFormat",undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "value";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $value = ReadingsVal($name,"value",undef);
|
||||||
|
if (!defined($value)) {
|
||||||
|
readingsSingleUpdate($hash,"value","off",0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AttrVal($name, "restoreOnReconnect", "on") eq "on") {
|
||||||
|
FRM_OUT_Set($hash,$name,$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_OUT_observer
|
||||||
FRM_OUT_observer($$$$)
|
|
||||||
{
|
{
|
||||||
my ($pin,$old,$new,$hash) = @_;
|
my ($pin,$old,$new,$hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
Log3 $name, 5, "onDigitalMessage for pin ".$pin.", old: ".(defined $old? $old : "--").", new: ".(defined $new? $new : "--");
|
Log3 $name, 5, "$name: observer pin: ".$pin.", old: ".(defined $old? $old : "--").", new: ".(defined $new? $new : "--");
|
||||||
if (AttrVal($hash->{NAME}, "activeLow", "no") eq "yes") {
|
if (AttrVal($hash->{NAME}, "activeLow", "no") eq "yes") {
|
||||||
$old = $old == PIN_LOW ? PIN_HIGH : PIN_LOW if (defined $old);
|
$old = $old == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW if (defined $old);
|
||||||
$new = $new == PIN_LOW ? PIN_HIGH : PIN_LOW;
|
$new = $new == Device::Firmata::Constants->PIN_LOW ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW;
|
||||||
}
|
}
|
||||||
my $changed = !defined($old) || ($old != $new);
|
my $changed = !defined($old) || ($old != $new);
|
||||||
if ($changed && (AttrVal($hash->{NAME}, "valueMode", "send") ne "send")) {
|
if ($changed && (AttrVal($hash->{NAME}, "valueMode", "send") ne "send")) {
|
||||||
main::readingsSingleUpdate($hash, "value", $new == PIN_HIGH? "on" : "off", 1);
|
main::readingsSingleUpdate($hash, "value", $new == Device::Firmata::Constants->PIN_HIGH? "on" : "off", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_OUT_Set
|
||||||
FRM_OUT_Set($$$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $name, $cmd, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
my $value;
|
|
||||||
my $invert = AttrVal($hash->{NAME},"activeLow", "no");
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
if (defined ($cmd)) {
|
return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
|
||||||
if ($cmd eq "on") {
|
return "$cmd requires $sets{$match[0]} arguments" unless (@a == $sets{$match[0]});
|
||||||
$value = $invert eq "yes" ? PIN_LOW : PIN_HIGH;
|
|
||||||
} elsif ($cmd eq "off") {
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
$value = $invert eq "yes" ? PIN_HIGH : PIN_LOW;
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
} else {
|
}
|
||||||
my $list = "on off";
|
|
||||||
return SetExtensions($hash, $list, $name, $cmd, @a);
|
my $value = Device::Firmata::Constants->PIN_LOW;
|
||||||
}
|
my $invert = AttrVal($hash->{NAME}, "activeLow", "no");
|
||||||
eval {
|
SETHANDLER: {
|
||||||
FRM_Client_FirmataDevice($hash)->digital_write($hash->{PIN},$value);
|
$cmd eq "on" and do {
|
||||||
if (AttrVal($hash->{NAME}, "valueMode", "send") ne "receive") {
|
$value = $invert eq "yes" ? Device::Firmata::Constants->PIN_LOW : Device::Firmata::Constants->PIN_HIGH;
|
||||||
main::readingsSingleUpdate($hash,"value",$cmd, 1);
|
last;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
return $@;
|
$cmd eq "off" and do {
|
||||||
} else {
|
$value = $invert eq "yes" ? Device::Firmata::Constants->PIN_HIGH : Device::Firmata::Constants->PIN_LOW;
|
||||||
return "no command specified";
|
last;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
eval {
|
||||||
|
FRM_Client_FirmataDevice($hash)->digital_write($hash->{PIN},$value);
|
||||||
|
if (AttrVal($hash->{NAME}, "valueMode", "send") ne "receive") {
|
||||||
|
main::readingsSingleUpdate($hash,"value",$cmd, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_OUT_State
|
||||||
|
{
|
||||||
|
my ($hash, $tim, $sname, $sval) = @_;
|
||||||
|
|
||||||
|
STATEHANDLER: {
|
||||||
|
$sname eq "value" and do {
|
||||||
|
if (AttrVal($hash->{NAME},"restoreOnStartup", "on") eq "on") {
|
||||||
|
FRM_OUT_Set($hash,$hash->{NAME},$sval);
|
||||||
|
}
|
||||||
|
last;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub FRM_OUT_State($$$$)
|
sub FRM_OUT_Attr
|
||||||
{
|
{
|
||||||
my ($hash, $tim, $sname, $sval) = @_;
|
|
||||||
|
|
||||||
STATEHANDLER: {
|
|
||||||
$sname eq "value" and do {
|
|
||||||
if (AttrVal($hash->{NAME},"restoreOnStartup", "on") eq "on") {
|
|
||||||
FRM_OUT_Set($hash,$hash->{NAME},$sval);
|
|
||||||
}
|
|
||||||
last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub
|
|
||||||
FRM_OUT_Attr($$$$) {
|
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
eval {
|
eval {
|
||||||
@ -163,6 +204,7 @@ FRM_OUT_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
|
|
||||||
$attribute eq "activeLow" and do {
|
$attribute eq "activeLow" and do {
|
||||||
my $oldval = AttrVal($hash->{NAME},"activeLow", "no");
|
my $oldval = AttrVal($hash->{NAME},"activeLow", "no");
|
||||||
if ($oldval ne $value) {
|
if ($oldval ne $value) {
|
||||||
@ -179,9 +221,9 @@ FRM_OUT_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,102 +231,148 @@ FRM_OUT_Attr($$$$) {
|
|||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
CHANGES
|
=head1 CHANGES
|
||||||
|
|
||||||
2016 jensb
|
2016 jensb
|
||||||
o new sub FRM_OUT_observer, modified sub FRM_OUT_Init
|
o new sub FRM_OUT_observer, modified sub FRM_OUT_Init
|
||||||
to receive output state from Firmata device
|
to receive output state from Firmata device
|
||||||
o support attribute "activeLow"
|
o support attribute "activeLow"
|
||||||
|
|
||||||
01.01.2018 jensb
|
01.01.2018 jensb
|
||||||
o create reading "value" in FRM_OUT_Init if missing
|
o create reading "value" in FRM_OUT_Init if missing
|
||||||
|
|
||||||
02.01.2018 jensb
|
02.01.2018 jensb
|
||||||
o new attribute "valueMode" to control how "value" reading is updated
|
o new attribute "valueMode" to control how "value" reading is updated
|
||||||
|
|
||||||
14.01.2018 jensb
|
14.01.2018 jensb
|
||||||
o fix "uninitialised" when calling FRM_OUT_Set without command
|
o fix "uninitialised" when calling FRM_OUT_Set without command
|
||||||
|
|
||||||
|
23.08.2020 jensb
|
||||||
|
o check for IODev install error in Init
|
||||||
|
o prototypes removed
|
||||||
|
o set argument metadata added
|
||||||
|
o set argument verifier improved
|
||||||
|
|
||||||
|
22.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
=item device
|
=item device
|
||||||
|
|
||||||
=item summary Firmata: digital output
|
=item summary Firmata: digital output
|
||||||
|
|
||||||
=item summary_DE Firmata: digitaler Ausang
|
=item summary_DE Firmata: digitaler Ausang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_OUT"></a>
|
<a name="FRM_OUT"/>
|
||||||
<h3>FRM_OUT</h3>
|
<h3>FRM_OUT</h3>
|
||||||
<ul>
|
<ul>
|
||||||
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
||||||
that should be configured as a digital output.<br><br>
|
that should be configured as a digital output.<br><br>
|
||||||
|
|
||||||
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in
|
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in
|
||||||
the internal reading "<a href="#FRMinternals">output_pins</a>"<br>
|
the internal reading "<a href="#FRMinternals">output_pins</a>"<br>
|
||||||
of the FRM device (after connecting to the Firmata device) to be used as digital output.<br><br>
|
of the FRM device (after connecting to the Firmata device) to be used as digital output.<br><br>
|
||||||
|
|
||||||
<a name="FRM_OUTdefine"></a>
|
<a name="FRM_OUTdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_OUT <pin></code> <br>
|
<code>define <name> FRM_OUT <pin></code> <br>
|
||||||
Defines the FRM_OUT device. <pin>> is the arduino-pin to use.
|
Defines the FRM_OUT device. <pin>> is the arduino-pin to use.
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_OUTset"></a>
|
<a name="FRM_OUTset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> on|off</code><br><br>
|
<code>set <name> on|off</code><br><br>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<a href="#setExtensions">set extensions</a> are supported<br>
|
<a href="#setExtensions">set extensions</a> are supported<br>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_OUTget"></a>
|
<a name="FRM_OUTget"/>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
N/A
|
N/A
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_OUTattr"></a>
|
<a name="FRM_OUTattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>restoreOnStartup <on|off>, default: on<br>
|
<a name="restoreOnStartup"/>
|
||||||
Set output value in Firmata device on FHEM startup (if device is already connected) and
|
<li>restoreOnStartup <on|off>, default: on<br>
|
||||||
whenever the <em>setstate</em> command is used.
|
Set output value in Firmata device on FHEM startup (if device is already connected) and
|
||||||
</li>
|
whenever the <em>setstate</em> command is used.
|
||||||
<li>restoreOnReconnect <on|off>, default: on<br>
|
</li>
|
||||||
Set output value in Firmata device after IODev is initialized.
|
|
||||||
</li>
|
<a name="restoreOnReconnect"/>
|
||||||
<li>activeLow <yes|no>, default: no</li>
|
<li>restoreOnReconnect <on|off>, default: on<br>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
Set output value in Firmata device after IODev is initialized.
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
</li>
|
||||||
than one FRM-device defined.)
|
|
||||||
</li>
|
<a name="activeLow"/>
|
||||||
<li>valueMode <send|receive|bidirectional>, default: send<br>
|
<li>activeLow <yes|no>, default: no</li>
|
||||||
Define how the reading <em>value</em> is updated:<br>
|
|
||||||
|
<a name="IODev"/>
|
||||||
|
<li><a href="#IODev">IODev</a><br>
|
||||||
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<a name="valueMode"/>
|
||||||
|
<li>valueMode <send|receive|bidirectional>, default: send<br>
|
||||||
|
Define how the reading <em>value</em> is updated:<br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>send - after sending</li>
|
<li>send - after sending</li>
|
||||||
<li>receive - after receiving</li>
|
<li>receive - after receiving</li>
|
||||||
<li>bidirectional - after sending and receiving</li>
|
<li>bidirectional - after sending and receiving</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_OUTnotes"></a>
|
<a name="FRM_OUTnotes"/>
|
||||||
<b>Notes</b><br>
|
<b>Notes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>attribute <i>stateFormat</i><br>
|
<li>attribute <i>stateFormat</i><br>
|
||||||
In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the state
|
In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the state
|
||||||
of the pin in the web interface.
|
of the pin in the web interface.
|
||||||
</li>
|
</li>
|
||||||
<li>attribute <i>valueMode</i><br>
|
<li>attribute <i>valueMode</i><br>
|
||||||
For modes "receive<" and "bidirectional" to work the default Firmata application code must
|
For modes "receive" and "bidirectional" to work the default Firmata application code must
|
||||||
be modified in function "<code>setPinModeCallback</code>":<br>
|
be modified in function "<code>setPinModeCallback</code>":<br>
|
||||||
add "<ins> || mode == OUTPUT</ins>" to the if condition for "<code>portConfigInputs[pin / 8] |= (1 << (pin & 7));</code>" to enable<br>
|
add "<code> || mode == OUTPUT</code>" to the if condition for "<code>portConfigInputs[pin / 8] |= (1 << (pin & 7));</code>" to enable<br>
|
||||||
reporting the output state (as if the pin were an input). This is of interest if you have custom code in your Firmata device that can change<br>
|
reporting the output state (as if the pin were an input). This is of interest if you have custom code in your Firmata device that my change to pin state.<br>
|
||||||
the state of an output or you want a feedback from the Firmata device after the output state was changed.
|
the state of an output or you want a feedback from the Firmata device after the output state was changed.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_OUT"/>
|
||||||
|
<h3>FRM_OUT</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_OUT gibt es nur auf <a href="commandref.html#FRM_OUT">Englisch</a>. <br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,37 +1,42 @@
|
|||||||
########################################################################################
|
########################################################################################
|
||||||
#
|
|
||||||
# $Id$
|
# $Id$
|
||||||
#
|
|
||||||
# FHEM module for one Firmata PWM output pin
|
|
||||||
#
|
|
||||||
########################################################################################
|
|
||||||
#
|
|
||||||
# LICENSE AND COPYRIGHT
|
|
||||||
#
|
|
||||||
# Copyright (C) 2013 ntruchess
|
|
||||||
# Copyright (C) 2016 jensb
|
|
||||||
#
|
|
||||||
# All rights reserved
|
|
||||||
#
|
|
||||||
# This script is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# The GNU General Public License can be found at
|
|
||||||
# http://www.gnu.org/copyleft/gpl.html.
|
|
||||||
# A copy is found in the textfile GPL.txt and important notices to the license
|
|
||||||
# from the author is found in LICENSE.txt distributed with these scripts.
|
|
||||||
#
|
|
||||||
# This script is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# This copyright notice MUST APPEAR in all copies of the script!
|
|
||||||
#
|
|
||||||
########################################################################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata PWM output pin
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2016 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -39,37 +44,34 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
use SetExtensions qw/ :all /;
|
use SetExtensions qw/ :all /;
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"dim" => 0,
|
"dim" => "",
|
||||||
"value" => 0,
|
"value" => "",
|
||||||
"devStateIcon" => 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# number of arguments
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"on" => 0,
|
"on:noArg" => 0,
|
||||||
"off" => 0,
|
"off:noArg" => 0,
|
||||||
"toggle" => 0,
|
"toggle:noArg" => 0,
|
||||||
"value" => 1,
|
"value" => 1,
|
||||||
"dim:slider,0,1,100" => 1,
|
"dim:slider,0,1,100" => 1,
|
||||||
"fadeTo" => 2,
|
"dimUp:noArg" => 0,
|
||||||
"dimUp" => 0,
|
"dimDown:noArg" => 0,
|
||||||
"dimDown" => 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_Initialize
|
||||||
FRM_PWM_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -80,51 +82,73 @@ FRM_PWM_Initialize($)
|
|||||||
$hash->{UndefFn} = "FRM_Client_Undef";
|
$hash->{UndefFn} = "FRM_Client_Undef";
|
||||||
$hash->{AttrFn} = "FRM_PWM_Attr";
|
$hash->{AttrFn} = "FRM_PWM_Attr";
|
||||||
$hash->{StateFn} = "FRM_PWM_State";
|
$hash->{StateFn} = "FRM_PWM_State";
|
||||||
|
|
||||||
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev $main::readingFnAttributes";
|
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev $main::readingFnAttributes";
|
||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_Init
|
||||||
FRM_PWM_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_PWM);
|
my $name = $hash->{NAME};
|
||||||
return $ret if (defined $ret);
|
|
||||||
my $firmata = $hash->{IODev}->{FirmataDevice};
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $resolution = 8;
|
|
||||||
if (defined $firmata->{metadata}{pwm_resolutions}) {
|
|
||||||
$resolution = $firmata->{metadata}{pwm_resolutions}{$hash->{PIN}}
|
|
||||||
}
|
|
||||||
$hash->{resolution} = $resolution;
|
|
||||||
$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 255;
|
|
||||||
$hash->{".dim"} = 0;
|
|
||||||
$hash->{".toggle"} = "off";
|
|
||||||
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
|
||||||
$main::attr{$name}{"stateFormat"} = "value";
|
|
||||||
}
|
|
||||||
my $value = ReadingsVal($name,"value",undef);
|
|
||||||
if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") {
|
|
||||||
FRM_PWM_Set($hash,$name,"value",$value);
|
|
||||||
}
|
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
FRM_PWM_Set($@)
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
{
|
}
|
||||||
my ($hash, $name, $cmd, @a) = @_;
|
|
||||||
|
my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_PWM);
|
||||||
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
if (defined($ret)) {
|
||||||
#-- check argument
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
|
return $ret;
|
||||||
return "$cmd expects $sets{$match[0]} parameters" unless (@a eq $sets{$match[0]});
|
}
|
||||||
|
|
||||||
|
eval {
|
||||||
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
|
my $resolution = 8;
|
||||||
|
if (defined $firmata->{metadata}{pwm_resolutions}) {
|
||||||
|
$resolution = $firmata->{metadata}{pwm_resolutions}{$hash->{PIN}}
|
||||||
|
}
|
||||||
|
$hash->{resolution} = $resolution;
|
||||||
|
$hash->{".max"} = defined $resolution ? (1<<$resolution)-1 : 255;
|
||||||
|
$hash->{".dim"} = 0;
|
||||||
|
$hash->{".toggle"} = "off";
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(defined AttrVal($name,"stateFormat",undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "value";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $value = ReadingsVal($name,"value",undef);
|
||||||
|
if (defined $value and AttrVal($hash->{NAME},"restoreOnReconnect","on") eq "on") {
|
||||||
|
FRM_PWM_Set($hash,$name,"value",$value);
|
||||||
|
}
|
||||||
|
|
||||||
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_PWM_Set
|
||||||
|
{
|
||||||
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
|
|
||||||
|
return "set command missing" if(!defined($cmd));
|
||||||
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
|
return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
|
||||||
|
return "$cmd requires $sets{$match[0]} argument(s)" unless (@a == $sets{$match[0]});
|
||||||
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $value = shift @a;
|
||||||
eval {
|
eval {
|
||||||
SETHANDLER: {
|
SETHANDLER: {
|
||||||
my $value = $a[0] if @a;
|
|
||||||
$cmd eq "on" and do {
|
$cmd eq "on" and do {
|
||||||
FRM_PWM_writeOut($hash,$hash->{".max"});
|
FRM_PWM_writeOut($hash,$hash->{".max"});
|
||||||
$hash->{".toggle"} = "on";
|
$hash->{".toggle"} = "on";
|
||||||
@ -141,7 +165,7 @@ FRM_PWM_Set($@)
|
|||||||
$toggle eq "off" and do {
|
$toggle eq "off" and do {
|
||||||
FRM_PWM_writeOut($hash,$hash->{".dim"});
|
FRM_PWM_writeOut($hash,$hash->{".dim"});
|
||||||
$hash->{".toggle"} = "up";
|
$hash->{".toggle"} = "up";
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$toggle eq "up" and do {
|
$toggle eq "up" and do {
|
||||||
FRM_PWM_writeOut($hash,$hash->{".max"});
|
FRM_PWM_writeOut($hash,$hash->{".max"});
|
||||||
@ -151,7 +175,7 @@ FRM_PWM_Set($@)
|
|||||||
$toggle eq "on" and do {
|
$toggle eq "on" and do {
|
||||||
FRM_PWM_writeOut($hash,$hash->{".dim"});
|
FRM_PWM_writeOut($hash,$hash->{".dim"});
|
||||||
$hash->{".toggle"} = "down";
|
$hash->{".toggle"} = "down";
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$toggle eq "down" and do {
|
$toggle eq "down" and do {
|
||||||
FRM_PWM_writeOut($hash,0);
|
FRM_PWM_writeOut($hash,0);
|
||||||
@ -197,9 +221,6 @@ FRM_PWM_Set($@)
|
|||||||
};
|
};
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$cmd eq "fadeTo" and do {
|
|
||||||
die "fadeTo not implemented yet";
|
|
||||||
};
|
|
||||||
$cmd eq "dimUp" and do {
|
$cmd eq "dimUp" and do {
|
||||||
my $dim = $hash->{".dim"};
|
my $dim = $hash->{".dim"};
|
||||||
my $max = $hash->{".max"};
|
my $max = $hash->{".max"};
|
||||||
@ -230,18 +251,19 @@ FRM_PWM_Set($@)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting '$cmd': ".(defined $1 ? $1 : $@);
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
return "error setting '$hash->{NAME} $cmd': ".(defined $1 ? $1 : $@);
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
return undef;
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_writeOut
|
||||||
FRM_PWM_writeOut($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$value) = @_;
|
my ($hash,$value) = @_;
|
||||||
|
|
||||||
FRM_Client_FirmataDevice($hash)->analog_write($hash->{PIN},$value);
|
FRM_Client_FirmataDevice($hash)->analog_write($hash->{PIN},$value);
|
||||||
readingsBeginUpdate($hash);
|
readingsBeginUpdate($hash);
|
||||||
readingsBulkUpdate($hash,"value",$value, 1);
|
readingsBulkUpdate($hash,"value",$value, 1);
|
||||||
@ -249,14 +271,13 @@ FRM_PWM_writeOut($$)
|
|||||||
readingsEndUpdate($hash, 1);
|
readingsEndUpdate($hash, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_Get
|
||||||
FRM_PWM_Get($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, $name, $cmd, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
|
|
||||||
return "FRM_PWM: Get with unknown argument $cmd, choose one of ".join(" ", sort keys %gets)
|
return "get command missing" if(!defined($cmd));
|
||||||
unless defined($gets{$cmd});
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
|
|
||||||
GETHANDLER: {
|
GETHANDLER: {
|
||||||
$cmd eq 'dim' and do {
|
$cmd eq 'dim' and do {
|
||||||
return ReadingsVal($name,"dim",undef);
|
return ReadingsVal($name,"dim",undef);
|
||||||
@ -264,14 +285,10 @@ FRM_PWM_Get($@)
|
|||||||
$cmd eq 'value' and do {
|
$cmd eq 'value' and do {
|
||||||
return ReadingsVal($name,"value",undef);
|
return ReadingsVal($name,"value",undef);
|
||||||
};
|
};
|
||||||
$cmd eq 'devStateIcon' and do {
|
|
||||||
return return "not implemented yet";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_State
|
||||||
FRM_PWM_State($$$$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $tim, $sname, $sval) = @_;
|
my ($hash, $tim, $sname, $sval) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -291,8 +308,7 @@ FRM_PWM_State($$$$)
|
|||||||
return 0; # default processing by fhem.pl
|
return 0; # default processing by fhem.pl
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_PWM_Attr
|
||||||
FRM_PWM_Attr($$$$)
|
|
||||||
{
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
@ -310,9 +326,9 @@ FRM_PWM_Attr($$$$)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,103 +336,151 @@ FRM_PWM_Attr($$$$)
|
|||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
CHANGES
|
=head1 CHANGES
|
||||||
|
|
||||||
2016 jensb
|
2016 jensb
|
||||||
o modified subs FRM_PWM_Init and FRM_PWM_State to support attribute "restoreOnStartup"
|
o modified subs FRM_PWM_Init and FRM_PWM_State to support attribute "restoreOnStartup"
|
||||||
|
|
||||||
|
24.08.2020 jensb
|
||||||
|
o check for IODev install error in Init and Set
|
||||||
|
o prototypes removed
|
||||||
|
o set argument metadata added
|
||||||
|
o get/set argument verifier improved
|
||||||
|
o module help updated
|
||||||
|
|
||||||
|
22.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
=item device
|
=item device
|
||||||
|
|
||||||
=item summary Firmata: PWM output
|
=item summary Firmata: PWM output
|
||||||
|
|
||||||
=item summary_DE Firmata: PWM Ausgang
|
=item summary_DE Firmata: PWM Ausgang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_PWM"></a>
|
<a name="FRM_PWM"/>
|
||||||
<h3>FRM_PWM</h3>
|
<h3>FRM_PWM</h3>
|
||||||
<ul>
|
<ul>
|
||||||
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
This module represents a pin of a <a href="http://www.firmata.org">Firmata device</a>
|
||||||
that should be configured as a pulse width modulated output (PWM).<br><br>
|
that should be configured as a pulse width modulated output (PWM).<br><br>
|
||||||
|
|
||||||
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading "<a href="#FRMinternals">pwm_pins</a>"<br>
|
Requires a defined <a href="#FRM">FRM</a> device to work. The pin must be listed in the internal reading
|
||||||
of the FRM device (after connecting to the Firmata device) to be used as PWM output.<br><br>
|
"<a href="#FRMinternals">pwm_pins</a>" of the FRM device (after connecting to the Firmata device) to be
|
||||||
|
used as PWM output.<br><br>
|
||||||
<a name="FRM_PWMdefine"></a>
|
|
||||||
|
<a name="FRM_PWMdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_PWM <pin></code><br><br>
|
<code>define <name> FRM_PWM <pin></code><br><br>
|
||||||
|
|
||||||
Defines the FRM_PWM device. <pin>> is the arduino-pin to use.
|
Defines the FRM_PWM device. <pin>> is the arduino-pin to use.
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_PWMset"></a>
|
<a name="FRM_PWMset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li><code>set <name> on</code><br>
|
<li><code>set <name> on</code><br>
|
||||||
sets the pulse-width to 100%<br>
|
sets the pulse-width to 100%<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> off</code><br>
|
<code>set <name> off</code><br>
|
||||||
sets the pulse-width to 0%<br>
|
sets the pulse-width to 0%<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#setExtensions">set extensions</a> are supported<br>
|
<a href="#setExtensions">set extensions</a> are supported<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> toggle</code><br>
|
<code>set <name> toggle</code><br>
|
||||||
toggles the pulse-width in between to the last value set by 'value' or 'dim' and 0 respectivly 100%<br>
|
toggles the pulse-width in between to the last value set by 'value' or 'dim' and 0 respectivly 100%<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> value <value></code><br>
|
<code>set <name> value <value></code><br>
|
||||||
sets the pulse-width to the value specified<br>
|
sets the pulse-width to the value specified<br>
|
||||||
The min value is zero and the max value depends on the Firmata device (see internal reading<br>
|
The min value is zero and the max value depends on the Firmata device (see internal reading<br>
|
||||||
"<a href="#FRMinternals">pwm_resolutions</a>" of the FRM device). For 8 bits resolution the range
|
"<a href="#FRMinternals">pwm_resolutions</a>" of the FRM device). For 8 bits resolution the range
|
||||||
is 0 to 255 (also see <a href="http://arduino.cc/en/Reference/AnalogWrite">analogWrite()</a> for details)<br>
|
is 0 to 255 (also see <a href="http://arduino.cc/en/Reference/AnalogWrite">analogWrite()</a> for details)<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> dim <value></code><br>
|
<code>set <name> dim <value></code><br>
|
||||||
sets the pulse-width to the value specified in percent<br>
|
sets the pulse-width to the value specified in percent<br>
|
||||||
Range is from 0 to 100<br>
|
Range is from 0 to 100<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> dimUp</code><br>
|
<code>set <name> dimUp</code><br>
|
||||||
increases the pulse-width by 10%<br>
|
increases the pulse-width by 10%<br>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<code>set <name> dimDown</code><br>
|
<code>set <name> dimDown</code><br>
|
||||||
decreases the pulse-width by 10%<br>
|
decreases the pulse-width by 10%<br>
|
||||||
</li>
|
</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_PWMget"></a>
|
<a name="FRM_PWMget"/>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
N/A
|
<li>
|
||||||
|
<code>get <dim></code><br>
|
||||||
|
returns current dim setting in percent, see description for set command for more details<br>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<code>get <value></code><br>
|
||||||
|
returns current dim setting, see description for set command for more details<br>
|
||||||
|
</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_PWMattr"></a>
|
<a name="FRM_PWMattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>restoreOnStartup <on|off></li>
|
<a name="restoreOnStartup"/>
|
||||||
<li>restoreOnReconnect <on|off></li>
|
<li>restoreOnStartup <on|off></li>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
<a name="restoreOnReconnect"/>
|
||||||
than one FRM-device defined.)
|
<li>restoreOnReconnect <on|off></li>
|
||||||
</li>
|
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
<a name="IODev"/>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li><a href="#IODev">IODev</a><br>
|
||||||
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_PWMnotes"></a>
|
<a name="FRM_PWMnotes"/>
|
||||||
<b>Notes</b><br>
|
<b>Notes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>attribute <i>stateFormat</i><br>
|
<li>attribute <i>stateFormat</i><br>
|
||||||
In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the
|
In most cases it is a good idea to assign "value" to the attribute <i>stateFormat</i>. This will show the
|
||||||
current value of the pin in the web interface.
|
current value of the pin in the web interface.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_PWM"/>
|
||||||
|
<h3>FRM_PWM</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_PWM gibt es nur auf <a href="commandref.html#FRM_PWM">Englisch</a>. <br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,6 +1,42 @@
|
|||||||
##############################################
|
########################################################################################
|
||||||
# $Id$
|
# $Id$
|
||||||
##############################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata PWM output pin for controlling RGB LEDs
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2020 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use vars qw{%attr %defs $readingFnAttributes};
|
use vars qw{%attr %defs $readingFnAttributes};
|
||||||
@ -16,32 +52,29 @@ BEGIN {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
use Color qw/ :all /;
|
use Color qw/ :all /;
|
||||||
use SetExtensions qw/ :all /;
|
use SetExtensions qw/ :all /;
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"rgb" => 0,
|
"rgb" => "",
|
||||||
"RGB" => 0,
|
"RGB" => "",
|
||||||
"pct" => 0,
|
"pct" => "",
|
||||||
"devStateIcon" => 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# number of arguments
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"on" => 0,
|
"on:noArg" => 0,
|
||||||
"off" => 0,
|
"off:noArg" => 0,
|
||||||
"toggle" => 0,
|
"toggle:noArg" => 0,
|
||||||
"rgb:colorpicker,RGB" => 1,
|
"rgb:colorpicker,RGB" => 1,
|
||||||
"pct:slider,0,1,100" => 1,
|
"pct:slider,0,1,100" => 1,
|
||||||
"fadeTo" => 2,
|
"dimUp:noArg" => 0,
|
||||||
"dimUp" => 0,
|
"dimDown:noArg" => 0,
|
||||||
"dimDown" => 0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Initialize
|
||||||
FRM_RGB_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -52,34 +85,40 @@ FRM_RGB_Initialize($)
|
|||||||
$hash->{UndefFn} = "FRM_Client_Undef";
|
$hash->{UndefFn} = "FRM_Client_Undef";
|
||||||
$hash->{AttrFn} = "FRM_RGB_Attr";
|
$hash->{AttrFn} = "FRM_RGB_Attr";
|
||||||
$hash->{StateFn} = "FRM_RGB_State";
|
$hash->{StateFn} = "FRM_RGB_State";
|
||||||
|
|
||||||
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev loglevel:0,1,2,3,4,5 $readingFnAttributes";
|
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev loglevel:0,1,2,3,4,5 $readingFnAttributes";
|
||||||
|
|
||||||
LoadModule("FRM");
|
LoadModule("FRM");
|
||||||
FHEM_colorpickerInit();
|
FHEM_colorpickerInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Define
|
||||||
FRM_RGB_Define($$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $def) = @_;
|
my ($hash, $def) = @_;
|
||||||
$attr{$hash->{NAME}}{webCmd} = "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off";
|
$attr{$hash->{NAME}}{webCmd} = "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:toggle:on:off";
|
||||||
return FRM_Client_Define($hash,$def);
|
return FRM_Client_Define($hash,$def);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Init
|
||||||
FRM_RGB_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_PWM);
|
|
||||||
return $ret if (defined $ret);
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $ret = FRM_Init_Pin_Client($hash, $args, Device::Firmata::Constants->PIN_PWM);
|
||||||
|
if (defined($ret)) {
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
my @pins = ();
|
my @pins = ();
|
||||||
eval {
|
eval {
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
$hash->{PIN} = "";
|
$hash->{PIN} = "";
|
||||||
foreach my $pin (@{$args}) {
|
foreach my $pin (@{$args}) {
|
||||||
$firmata->pin_mode($pin,PIN_PWM);
|
$firmata->pin_mode($pin, Device::Firmata::Constants->PIN_PWM);
|
||||||
push @pins,{
|
push @pins,{
|
||||||
pin => $pin,
|
pin => $pin,
|
||||||
"shift" => defined $firmata->{metadata}{pwm_resolutions} ? $firmata->{metadata}{pwm_resolutions}{$pin}-8 : 0,
|
"shift" => defined $firmata->{metadata}{pwm_resolutions} ? $firmata->{metadata}{pwm_resolutions}{$pin}-8 : 0,
|
||||||
@ -89,11 +128,11 @@ FRM_RGB_Init($$)
|
|||||||
$hash->{PINS} = \@pins;
|
$hash->{PINS} = \@pins;
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
$ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error initializing: ".$1;
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
return "error initializing '".$hash->{NAME}."': ".$1;
|
return $ret;
|
||||||
}
|
}
|
||||||
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
if (!(defined AttrVal($name,"stateFormat",undef))) {
|
||||||
$attr{$name}{"stateFormat"} = "rgb";
|
$attr{$name}{"stateFormat"} = "rgb";
|
||||||
}
|
}
|
||||||
my $value = ReadingsVal($name,"rgb",undef);
|
my $value = ReadingsVal($name,"rgb",undef);
|
||||||
@ -103,22 +142,26 @@ FRM_RGB_Init($$)
|
|||||||
$hash->{toggle} = "off";
|
$hash->{toggle} = "off";
|
||||||
$hash->{".dim"} = {
|
$hash->{".dim"} = {
|
||||||
bri => 50,
|
bri => 50,
|
||||||
channels => [(255) x @{$hash->{PINS}}],
|
channels => [(255) x @{$hash->{PINS}}],
|
||||||
};
|
};
|
||||||
readingsSingleUpdate($hash,"state","Initialized",1);
|
readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Set
|
||||||
FRM_RGB_Set($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, $name, $cmd, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
|
|
||||||
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
|
||||||
#-- check argument
|
|
||||||
return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
|
|
||||||
return "$cmd expects $sets{$match[0]} parameters" unless (@a eq $sets{$match[0]});
|
|
||||||
|
|
||||||
|
return "set command missing" if(!defined($cmd));
|
||||||
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
|
return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @a) unless @match == 1;
|
||||||
|
return "$cmd requires $sets{$match[0]} argument(s)" unless (@a == $sets{$match[0]});
|
||||||
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $value = shift @a;
|
||||||
eval {
|
eval {
|
||||||
SETHANDLER: {
|
SETHANDLER: {
|
||||||
$cmd eq "on" and do {
|
$cmd eq "on" and do {
|
||||||
@ -137,7 +180,7 @@ FRM_RGB_Set($@)
|
|||||||
$toggle eq "off" and do {
|
$toggle eq "off" and do {
|
||||||
$hash->{toggle} = "up";
|
$hash->{toggle} = "up";
|
||||||
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$toggle eq "up" and do {
|
$toggle eq "up" and do {
|
||||||
FRM_RGB_SetChannels($hash,(0xFF) x @{$hash->{PINS}});
|
FRM_RGB_SetChannels($hash,(0xFF) x @{$hash->{PINS}});
|
||||||
@ -147,7 +190,7 @@ FRM_RGB_Set($@)
|
|||||||
$toggle eq "on" and do {
|
$toggle eq "on" and do {
|
||||||
$hash->{toggle} = "down";
|
$hash->{toggle} = "down";
|
||||||
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$toggle eq "down" and do {
|
$toggle eq "down" and do {
|
||||||
FRM_RGB_SetChannels($hash,(0x0) x @{$hash->{PINS}});
|
FRM_RGB_SetChannels($hash,(0x0) x @{$hash->{PINS}});
|
||||||
@ -158,18 +201,17 @@ FRM_RGB_Set($@)
|
|||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$cmd eq "rgb" and do {
|
$cmd eq "rgb" and do {
|
||||||
my $arg = $a[0];
|
|
||||||
my $numPins = scalar(@{$hash->{PINS}});
|
my $numPins = scalar(@{$hash->{PINS}});
|
||||||
my $nybles = $numPins << 1;
|
my $nybles = $numPins << 1;
|
||||||
die "$arg is not the right format" unless( $arg =~ /^[\da-f]{$nybles}$/i );
|
die "$value is not the right format" unless( $value =~ /^[\da-f]{$nybles}$/i );
|
||||||
my @channels = RgbToChannels($arg,$numPins);
|
my @channels = RgbToChannels($value,$numPins);
|
||||||
FRM_RGB_SetChannels($hash,@channels);
|
FRM_RGB_SetChannels($hash,@channels);
|
||||||
RGBHANDLER: {
|
RGBHANDLER: {
|
||||||
$arg =~ /^0{$nybles}$/ and do {
|
$value =~ /^0{$nybles}$/ and do {
|
||||||
$hash->{toggle} = "off";
|
$hash->{toggle} = "off";
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$arg =~ /^f{$nybles}$/i and do {
|
$value =~ /^f{$nybles}$/i and do {
|
||||||
$hash->{toggle} = "on";
|
$hash->{toggle} = "on";
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
@ -179,14 +221,14 @@ FRM_RGB_Set($@)
|
|||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$cmd eq "pct" and do {
|
$cmd eq "pct" and do {
|
||||||
$hash->{".dim"}->{bri} = $a[0];
|
$hash->{".dim"}->{bri} = $value;
|
||||||
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$cmd eq "dimUp" and do {
|
$cmd eq "dimUp" and do {
|
||||||
$hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} > 90 ? 100 : $hash->{".dim"}->{bri}+10;
|
$hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} > 90 ? 100 : $hash->{".dim"}->{bri}+10;
|
||||||
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
FRM_RGB_SetChannels($hash,BrightnessToChannels($hash->{".dim"}));
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$cmd eq "dimDown" and do {
|
$cmd eq "dimDown" and do {
|
||||||
$hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} < 10 ? 0 : $hash->{".dim"}->{bri}-10;
|
$hash->{".dim"}->{bri} = $hash->{".dim"}->{bri} < 10 ? 0 : $hash->{".dim"}->{bri}-10;
|
||||||
@ -195,22 +237,22 @@ FRM_RGB_Set($@)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting '$cmd': ".(defined $1 ? $1 : $@);
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
return "error setting '$hash->{NAME} $cmd': ".(defined $1 ? $1 : $@);
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Get
|
||||||
FRM_RGB_Get($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, $name, $cmd, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
|
|
||||||
return "FRM_RGB: Get with unknown argument $cmd, choose one of ".join(" ", sort keys %gets)
|
return "get command missing" if(!defined($cmd));
|
||||||
unless defined($gets{$cmd});
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
|
|
||||||
GETHANDLER: {
|
GETHANDLER: {
|
||||||
$cmd eq 'rgb' and do {
|
$cmd eq 'rgb' and do {
|
||||||
return ReadingsVal($name,"rgb",undef);
|
return ReadingsVal($name,"rgb",undef);
|
||||||
@ -220,20 +262,18 @@ FRM_RGB_Get($@)
|
|||||||
};
|
};
|
||||||
$cmd eq 'pct' and do {
|
$cmd eq 'pct' and do {
|
||||||
return $hash->{".dim"}->{bri};
|
return $hash->{".dim"}->{bri};
|
||||||
return undef;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_SetChannels
|
||||||
FRM_RGB_SetChannels($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,@channels) = @_;
|
my ($hash,@channels) = @_;
|
||||||
|
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
my @pins = @{$hash->{PINS}};
|
my @pins = @{$hash->{PINS}};
|
||||||
my @values = @channels;
|
my @values = @channels;
|
||||||
|
|
||||||
while(@values) {
|
while(@values) {
|
||||||
my $pin = shift @pins;
|
my $pin = shift @pins;
|
||||||
my $value = shift @values;
|
my $value = shift @values;
|
||||||
@ -250,8 +290,7 @@ FRM_RGB_SetChannels($$)
|
|||||||
readingsEndUpdate($hash, 1);
|
readingsEndUpdate($hash, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_State
|
||||||
FRM_RGB_State($$$$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $tim, $sname, $sval) = @_;
|
my ($hash, $tim, $sname, $sval) = @_;
|
||||||
if ($sname eq "rgb") {
|
if ($sname eq "rgb") {
|
||||||
@ -259,8 +298,7 @@ FRM_RGB_State($$$$)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_RGB_Attr
|
||||||
FRM_RGB_Attr($$$$)
|
|
||||||
{
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
@ -278,91 +316,141 @@ FRM_RGB_Attr($$$$)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 CHANGES
|
||||||
|
|
||||||
|
30.08.2020 jensb
|
||||||
|
o check for IODev install error in Init and Set
|
||||||
|
o prototypes removed
|
||||||
|
o set argument metadata added
|
||||||
|
o get/set argument verifier improved
|
||||||
|
|
||||||
|
19.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item device
|
||||||
|
|
||||||
|
=item summary Firmata: PWM output for RGB-LED
|
||||||
|
|
||||||
|
=item summary_DE Firmata: PWM Ausgang für RGB-LED
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_RGB"></a>
|
<a name="FRM_RGB"/>
|
||||||
<h3>FRM_RGB</h3>
|
<h3>FRM_RGB</h3>
|
||||||
<ul>
|
<ul>
|
||||||
allows to drive LED-controllers and other multichannel-devices that use PWM as input by an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
|
allows to drive LED-controllers and other multichannel-devices that use PWM as input by an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
|
||||||
<br>
|
<br>
|
||||||
The value set will be output by the specified pins as pulse-width-modulated signals.<br>
|
The value set will be output by the specified pins as pulse-width-modulated signals.<br>
|
||||||
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
||||||
|
|
||||||
<a name="FRM_RGBdefine"></a>
|
<a name="FRM_RGBdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_RGB <pin> <pin> <pin> [pin...]</code> <br>
|
<code>define <name> FRM_RGB <pin> <pin> <pin> [pin...]</code> <br>
|
||||||
Defines the FRM_RGB device. <pin>> are the arduino-pin to use.<br>
|
Defines the FRM_RGB device. <pin>> are the arduino-pin to use.<br>
|
||||||
For rgb-controlled devices first pin drives red, second pin green and third pin blue.
|
For rgb-controlled devices first pin drives red, second pin green and third pin blue.
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a name="FRM_RGBset"></a>
|
<a name="FRM_RGBset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> on</code><br>
|
<code>set <name> on</code><br>
|
||||||
sets the pulse-width of all configured pins to 100%</ul><br>
|
sets the pulse-width of all configured pins to 100%</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> off</code><br>
|
<code>set <name> off</code><br>
|
||||||
sets the pulse-width of all configured pins to 0%</ul><br>
|
sets the pulse-width of all configured pins to 0%</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<a href="#setExtensions">set extensions</a> are supported</ul><br>
|
<a href="#setExtensions">set extensions</a> are supported</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> toggle</code><br>
|
<code>set <name> toggle</code><br>
|
||||||
toggles in between the last dimmed value, 0% and 100%. If no dimmed value was set before defaults to pulsewidth 50% on all channels</ul><br>
|
toggles in between the last dimmed value, 0% and 100%. If no dimmed value was set before defaults to pulsewidth 50% on all channels</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> rgb <value></code><br>
|
<code>set <name> rgb <value></code><br>
|
||||||
sets the pulse-width of all channels at once. Also sets the value toggle can switch to<br>
|
sets the pulse-width of all channels at once. Also sets the value toggle can switch to<br>
|
||||||
Value is encoded as hex-string, 2-digigs per channel (e.g. FFFFFF for reguler rgb)</ul><br>
|
Value is encoded as hex-string, 2-digigs per channel (e.g. FFFFFF for reguler rgb)</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> pct <value></code><br>
|
<code>set <name> pct <value></code><br>
|
||||||
dims all channels at once while leving the ratio in between the channels unaltered.<br>
|
dims all channels at once while leving the ratio in between the channels unaltered.<br>
|
||||||
Range is 0-100 ('pct' stands for 'percent')</ul><br>
|
Range is 0-100 ('pct' stands for 'percent')</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> dimUp</code><br>
|
<code>set <name> dimUp</code><br>
|
||||||
dims up by 10%</ul><br>
|
dims up by 10%</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> dimDown</code><br>
|
<code>set <name> dimDown</code><br>
|
||||||
dims down by 10%</ul><br>
|
dims down by 10%
|
||||||
<a name="FRM_RGBget"></a>
|
</ul><br>
|
||||||
|
|
||||||
|
<a name="FRM_RGBget"/>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>get <name> rgb</code><br>
|
<code>get <name> rgb</code><br>
|
||||||
returns the values set for all channels. Format is hex, 2 nybbles per channel.
|
returns the values set for all channels. Format is hex, 2 nybbles per channel.
|
||||||
</ul><br>
|
</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>get <name> RGB</code><br>
|
<code>get <name> RGB</code><br>
|
||||||
returns the values set for all channels in normalized format. Format is hex, 2 nybbles per channel.
|
returns the values set for all channels in normalized format. Format is hex, 2 nybbles per channel.
|
||||||
Values are scaled such that the channel with the highest value is set to FF. The real values are calculated
|
Values are scaled such that the channel with the highest value is set to FF. The real values are calculated
|
||||||
by multipying each byte with the value of 'pct'.
|
by multipying each byte with the value of 'pct'.
|
||||||
</ul><br>
|
</ul><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>get <name> pct</code><br>
|
<code>get <name> pct</code><br>
|
||||||
returns the value of the channel with the highest value scaled to the range of 0-100 (percent).
|
returns the value of the channel with the highest value scaled to the range of 0-100 (percent).
|
||||||
</ul><br>
|
</ul><br>
|
||||||
<a name="FRM_RGBattr"></a>
|
|
||||||
|
<a name="FRM_RGBattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>restoreOnStartup <on|off></li>
|
<a name="restoreOnStartup"/>
|
||||||
<li>restoreOnReconnect <on|off></li>
|
<li>restoreOnStartup <on|off></li>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
<a name="restoreOnReconnect"/>
|
||||||
than one FRM-device defined.)
|
<li>restoreOnReconnect <on|off></li>
|
||||||
</li>
|
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
<a name="IODev"/>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li><a href="#IODev">IODev</a><br>
|
||||||
</ul>
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_RGB"/>
|
||||||
|
<h3>FRM_RGB</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_RGB gibt es nur auf <a href="commandref.html#FRM_RGB">Englisch</a>. <br>
|
||||||
|
</ul> <br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,6 +1,42 @@
|
|||||||
##############################################
|
########################################################################################
|
||||||
# $Id$
|
# $Id$
|
||||||
##############################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for two Firmata rotary encoder input pins
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2020 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -8,37 +44,35 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
|
# number of arguments
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"reset" => "noArg",
|
"reset:noArg" => 0,
|
||||||
"offset"=> "",
|
"offset" => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"position" => "noArg",
|
"position" => "",
|
||||||
"offset" => "noArg",
|
"offset" => "",
|
||||||
"value" => "noArg",
|
"value" => "",
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Initialize
|
||||||
FRM_ROTENC_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{SetFn} = "FRM_ROTENC_Set";
|
$hash->{SetFn} = "FRM_ROTENC_Set";
|
||||||
$hash->{GetFn} = "FRM_ROTENC_Get";
|
$hash->{GetFn} = "FRM_ROTENC_Get";
|
||||||
$hash->{AttrFn} = "FRM_ROTENC_Attr";
|
$hash->{AttrFn} = "FRM_ROTENC_Attr";
|
||||||
$hash->{DefFn} = "FRM_Client_Define";
|
$hash->{DefFn} = "FRM_ROTENC_Define";
|
||||||
$hash->{InitFn} = "FRM_ROTENC_Init";
|
$hash->{InitFn} = "FRM_ROTENC_Init";
|
||||||
$hash->{UndefFn} = "FRM_ROTENC_Undef";
|
$hash->{UndefFn} = "FRM_ROTENC_Undef";
|
||||||
$hash->{StateFn} = "FRM_ROTENC_State";
|
$hash->{StateFn} = "FRM_ROTENC_State";
|
||||||
@ -47,73 +81,87 @@ FRM_ROTENC_Initialize($)
|
|||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Define
|
||||||
FRM_ROTENC_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash, $def) = @_;
|
||||||
|
|
||||||
my $u = "wrong syntax: define <name> FRM_ROTENC pinA pinB [id]";
|
# verify define arguments
|
||||||
return $u unless defined $args and int(@$args) > 1;
|
my $usage = "usage: define <name> FRM_ROTENC pinA pinB [id]";
|
||||||
my $pinA = @$args[0];
|
|
||||||
my $pinB = @$args[1];
|
|
||||||
my $encoder = defined @$args[2] ? @$args[2] : 0;
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
|
|
||||||
$hash->{PINA} = $pinA;
|
|
||||||
$hash->{PINB} = $pinB;
|
|
||||||
|
|
||||||
$hash->{ENCODERNUM} = $encoder;
|
|
||||||
|
|
||||||
eval {
|
|
||||||
FRM_Client_AssignIOPort($hash);
|
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
|
||||||
$firmata->encoder_attach($encoder,$pinA,$pinB);
|
|
||||||
$firmata->observe_encoder($encoder, \&FRM_ROTENC_observer, $hash );
|
|
||||||
};
|
|
||||||
if ($@) {
|
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
|
||||||
$hash->{STATE} = "error initializing: ".$1;
|
|
||||||
return "error initializing '$name': $1";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
my @a = split("[ \t]+", $def);
|
||||||
$main::attr{$name}{"stateFormat"} = "position";
|
return $usage if (scalar(@a) < 4);
|
||||||
}
|
my $args = [@a[2..scalar(@a)-1]];
|
||||||
|
|
||||||
|
$hash->{PINA} = @$args[0];
|
||||||
|
$hash->{PINB} = @$args[1];
|
||||||
|
|
||||||
|
$hash->{ENCODERNUM} = defined @$args[2] ? @$args[2] : 0;
|
||||||
|
|
||||||
|
my $ret = FRM_Client_Define($hash, $def);
|
||||||
|
if ($ret) {
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_ROTENC_Init
|
||||||
|
{
|
||||||
|
my ($hash,$args) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
eval {
|
||||||
|
FRM_Client_AssignIOPort($hash);
|
||||||
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
|
$firmata->encoder_attach($hash->{ENCODERNUM}, $hash->{PINA}, $hash->{PINB});
|
||||||
|
$firmata->observe_encoder($hash->{ENCODERNUM}, \&FRM_ROTENC_observer, $hash );
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "position";
|
||||||
|
}
|
||||||
|
|
||||||
$hash->{offset} = ReadingsVal($name,"position",0);
|
$hash->{offset} = ReadingsVal($name,"position",0);
|
||||||
|
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
return undef;
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_observer
|
||||||
FRM_ROTENC_observer
|
|
||||||
{
|
{
|
||||||
my ( $encoder, $value, $hash ) = @_;
|
my ($encoder, $value, $hash) = @_;
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
Log3 ($name,5,"onEncoderMessage for pins ".$hash->{PINA}.",".$hash->{PINB}." encoder: ".$encoder." position: ".$value."\n");
|
Log3 ($name, 5, "$name: observer pins: ".$hash->{PINA}.", ".$hash->{PINB}." encoder: ".$encoder." position: ".$value."\n");
|
||||||
main::readingsBeginUpdate($hash);
|
main::readingsBeginUpdate($hash);
|
||||||
main::readingsBulkUpdate($hash,"position",$value+$hash->{offset}, 1);
|
main::readingsBulkUpdate($hash,"position",$value+$hash->{offset}, 1);
|
||||||
main::readingsBulkUpdate($hash,"value",$value, 1);
|
main::readingsBulkUpdate($hash,"value",$value, 1);
|
||||||
main::readingsEndUpdate($hash,1);
|
main::readingsEndUpdate($hash,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Set
|
||||||
FRM_ROTENC_Set
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "Need at least one parameters" if(@a < 2);
|
|
||||||
my $command = $a[1];
|
return "set command missing" if(!defined($cmd));
|
||||||
my $value = $a[2];
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
if(!defined($sets{$command})) {
|
return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0);
|
||||||
my @commands = ();
|
return "$cmd requires $sets{$match[0]} argument(s)" unless (@a == $sets{$match[0]});
|
||||||
foreach my $key (sort keys %sets) {
|
|
||||||
push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key;
|
my $value = shift @a;
|
||||||
}
|
SETHANDLER: {
|
||||||
return "Unknown argument $a[1], choose one of " . join(" ", @commands);
|
$cmd eq "reset" and do {
|
||||||
}
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
COMMAND_HANDLER: {
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
$command eq "reset" and do {
|
}
|
||||||
eval {
|
eval {
|
||||||
FRM_Client_FirmataDevice($hash)->encoder_reset_position($hash->{ENCODERNUM});
|
FRM_Client_FirmataDevice($hash)->encoder_reset_position($hash->{ENCODERNUM});
|
||||||
};
|
};
|
||||||
@ -123,46 +171,37 @@ FRM_ROTENC_Set
|
|||||||
main::readingsEndUpdate($hash,1);
|
main::readingsEndUpdate($hash,1);
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$command eq "offset" and do {
|
$cmd eq "offset" and do {
|
||||||
$hash->{offset} = $value;
|
$hash->{offset} = $value;
|
||||||
readingsSingleUpdate($hash,"position",ReadingsVal($hash->{NAME},"value",0)+$value,1);
|
readingsSingleUpdate($hash,"position",ReadingsVal($name,"value",0)+$value,1);
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Get
|
||||||
FRM_ROTENC_Get($)
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "Need at least one parameters" if(@a < 2);
|
|
||||||
my $command = $a[1];
|
return "get command missing" if(!defined($cmd));
|
||||||
my $value = $a[2];
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
if(!defined($gets{$command})) {
|
|
||||||
my @commands = ();
|
GETHANDLER: {
|
||||||
foreach my $key (sort keys %gets) {
|
|
||||||
push @commands, $gets{$key} ? $key.":".join(",",$gets{$key}) : $key;
|
|
||||||
}
|
|
||||||
return "Unknown argument $a[1], choose one of " . join(" ", @commands);
|
|
||||||
}
|
|
||||||
my $name = shift @a;
|
|
||||||
my $cmd = shift @a;
|
|
||||||
ARGUMENT_HANDLER: {
|
|
||||||
$cmd eq "position" and do {
|
$cmd eq "position" and do {
|
||||||
return ReadingsVal($hash->{NAME},"position","0");
|
return ReadingsVal($name,"position","0");
|
||||||
};
|
};
|
||||||
$cmd eq "offset" and do {
|
$cmd eq "offset" and do {
|
||||||
return $hash->{offset};
|
return $hash->{offset};
|
||||||
};
|
};
|
||||||
$cmd eq "value" and do {
|
$cmd eq "value" and do {
|
||||||
return ReadingsVal($hash->{NAME},"value","0");
|
return ReadingsVal($name,"value","0");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Attr
|
||||||
FRM_ROTENC_Attr($$$$) {
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
eval {
|
eval {
|
||||||
@ -179,38 +218,32 @@ FRM_ROTENC_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_Undef
|
||||||
FRM_ROTENC_Undef($$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $name) = @_;
|
my ($hash, $name) = @_;
|
||||||
|
|
||||||
my $pinA = $hash->{PINA};
|
my $pinA = $hash->{PINA};
|
||||||
my $pinB = $hash->{PINB};
|
my $pinB = $hash->{PINB};
|
||||||
eval {
|
eval {
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
$firmata->encoder_detach($hash->{ENCODERNUM});
|
$firmata->encoder_detach($hash->{ENCODERNUM});
|
||||||
$firmata->pin_mode($pinA,PIN_ANALOG);
|
|
||||||
$firmata->pin_mode($pinB,PIN_ANALOG);
|
|
||||||
};
|
};
|
||||||
if ($@) {
|
|
||||||
eval {
|
$hash->{PIN} = $hash->{PINA};
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
FRM_Client_Undef($hash, $name);
|
||||||
$firmata->pin_mode($pinA,PIN_INPUT);
|
$hash->{PIN} = $hash->{PINB};
|
||||||
$firmata->digital_write($pinA,0);
|
FRM_Client_Undef($hash, $name);
|
||||||
$firmata->pin_mode($pinB,PIN_INPUT);
|
|
||||||
$firmata->digital_write($pinB,0);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_ROTENC_State
|
||||||
FRM_ROTENC_State($$$$)
|
|
||||||
{
|
{
|
||||||
my ($hash, $tim, $sname, $sval) = @_;
|
my ($hash, $tim, $sname, $sval) = @_;
|
||||||
if ($sname eq "position") {
|
if ($sname eq "position") {
|
||||||
@ -222,30 +255,64 @@ FRM_ROTENC_State($$$$)
|
|||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 CHANGES
|
||||||
|
|
||||||
|
05.09.2020 jensb
|
||||||
|
o check for IODev install error in Init, Set and Undef
|
||||||
|
o prototypes removed
|
||||||
|
o set argument verifier improved
|
||||||
|
o moved define argument verification and decoding from Init to Define
|
||||||
|
|
||||||
|
19.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item device
|
||||||
|
|
||||||
|
=item summary Firmata: rotary encoder input
|
||||||
|
|
||||||
|
=item summary_DE Firmata: Drehgeber Eingang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_ROTENC"></a>
|
<a name="FRM_ROTENC"/>
|
||||||
<h3>FRM_ROTENC</h3>
|
<h3>FRM_ROTENC</h3>
|
||||||
<ul>
|
<ul>
|
||||||
represents a rotary-encoder attached to two pins of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a><br>
|
represents a rotary-encoder attached to two pins of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a><br>
|
||||||
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
||||||
|
|
||||||
<a name="FRM_ROTENCdefine"></a>
|
<a name="FRM_ROTENCdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_ROTENC <pinA> <pinB> [id]</code> <br>
|
<code>define <name> FRM_ROTENC <pinA> <pinB> [id]</code> <br>
|
||||||
Defines the FRM_ROTENC device. <pinA>> and <pinA>> are the arduino-pins to use.<br>
|
Defines the FRM_ROTENC device. <pinA>> and <pinA>> are the arduino-pins to use.<br>
|
||||||
[id] is the instance-id of the encoder. Must be a unique number per FRM-device (rages from 0-4 depending on Firmata being used, optional if a single encoder is attached to the arduino).<br>
|
[id] is the instance-id of the encoder. Must be a unique number per FRM-device (rages from 0-4 depending on Firmata being used, optional if a single encoder is attached to the arduino).<br>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a name="FRM_ROTENCset"></a>
|
<a name="FRM_ROTENCset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
|
<ul>
|
||||||
<li>reset<br>
|
<li>reset<br>
|
||||||
resets to value of 'position' to 0<br></li>
|
resets to value of 'position' to 0<br></li>
|
||||||
<li>offset <value><br>
|
<li>offset <value><br>
|
||||||
set offset value of 'position'<br></li>
|
set offset value of 'position'<br></li>
|
||||||
<a name="FRM_ROTENCget"></a>
|
</ul><br>
|
||||||
|
|
||||||
|
<a name="FRM_ROTENCget"/>
|
||||||
<b>Get</b>
|
<b>Get</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li>position<br>
|
<li>position<br>
|
||||||
@ -258,18 +325,31 @@ FRM_ROTENC_State($$$$)
|
|||||||
returns the raw position value as it's reported by the rotary-encoder attached to pinA and pinB of the arduino<br>
|
returns the raw position value as it's reported by the rotary-encoder attached to pinA and pinB of the arduino<br>
|
||||||
this value is reset to 0 whenever Arduino restarts or Firmata is reinitialized<br></li>
|
this value is reset to 0 whenever Arduino restarts or Firmata is reinitialized<br></li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
<a name="FRM_ROTENCattr"></a>
|
|
||||||
|
<a name="FRM_ROTENCattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
<a name="IODev"/>
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
<li><a href="#IODev">IODev</a><br>
|
||||||
than one FRM-device defined.)
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
</ul>
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_ROTENC"/>
|
||||||
|
<h3>FRM_ROTENC</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_ROTENC gibt es nur auf <a href="commandref.html#FRM_ROTENC">Englisch</a>. <br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,6 +1,42 @@
|
|||||||
##############################################
|
########################################################################################
|
||||||
# $Id$
|
# $Id$
|
||||||
##############################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for one Firmata PMW controlled servo output
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2020 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -8,23 +44,21 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
|
# number of arguments
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"angle" => "",
|
"angle" => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_SERVO_Initialize
|
||||||
FRM_SERVO_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
@ -33,29 +67,44 @@ FRM_SERVO_Initialize($)
|
|||||||
$hash->{InitFn} = "FRM_SERVO_Init";
|
$hash->{InitFn} = "FRM_SERVO_Init";
|
||||||
$hash->{UndefFn} = "FRM_Client_Undef";
|
$hash->{UndefFn} = "FRM_Client_Undef";
|
||||||
$hash->{AttrFn} = "FRM_SERVO_Attr";
|
$hash->{AttrFn} = "FRM_SERVO_Attr";
|
||||||
|
|
||||||
$hash->{AttrList} = "min-pulse max-pulse IODev $main::readingFnAttributes";
|
$hash->{AttrList} = "min-pulse max-pulse IODev $main::readingFnAttributes";
|
||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_SERVO_Init
|
||||||
FRM_SERVO_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
my $ret = FRM_Init_Pin_Client($hash,$args,PIN_SERVO);
|
my $name = $hash->{NAME};
|
||||||
return $ret if (defined $ret);
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $ret = FRM_Init_Pin_Client($hash,$args,Device::Firmata::Constants->PIN_SERVO);
|
||||||
|
if (defined($ret)) {
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
eval {
|
eval {
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
$hash->{resolution}=$firmata->{metadata}{servo_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{servo_resolutions});
|
$hash->{resolution}=$firmata->{metadata}{servo_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{servo_resolutions});
|
||||||
FRM_SERVO_apply_attribute($hash,"max-pulse"); #sets min-pulse as well
|
FRM_SERVO_apply_attribute($hash,"max-pulse"); #sets min-pulse as well
|
||||||
};
|
};
|
||||||
return FRM_Catch($@) if $@;
|
if ($@) {
|
||||||
|
$ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_SERVO_Attr
|
||||||
FRM_SERVO_Attr($$$$) {
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
eval {
|
eval {
|
||||||
@ -70,7 +119,10 @@ FRM_SERVO_Attr($$$$) {
|
|||||||
};
|
};
|
||||||
($attribute eq "min-pulse" || $attribute eq "max-pulse") and do {
|
($attribute eq "min-pulse" || $attribute eq "max-pulse") and do {
|
||||||
if ($main::init_done) {
|
if ($main::init_done) {
|
||||||
$main::attr{$name}{$attribute}=$value;
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
die 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
$main::attr{$name}{$attribute}=$value;
|
||||||
FRM_SERVO_apply_attribute($hash,$attribute);
|
FRM_SERVO_apply_attribute($hash,$attribute);
|
||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
@ -78,15 +130,15 @@ FRM_SERVO_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
my $ret = FRM_Catch($@) if $@;
|
if ($@) {
|
||||||
if ($ret) {
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$ret;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$ret;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
return undef;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub FRM_SERVO_apply_attribute {
|
sub FRM_SERVO_apply_attribute
|
||||||
|
{
|
||||||
my ($hash,$attribute) = @_;
|
my ($hash,$attribute) = @_;
|
||||||
if ( $attribute eq "min-pulse" || $attribute eq "max-pulse" ) {
|
if ( $attribute eq "min-pulse" || $attribute eq "max-pulse" ) {
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{NAME};
|
||||||
@ -95,69 +147,130 @@ sub FRM_SERVO_apply_attribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_SERVO_Set
|
||||||
FRM_SERVO_Set($@)
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "Need at least one parameters" if(@a < 2);
|
|
||||||
return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
|
return "set command missing" if(!defined($cmd));
|
||||||
if(!defined($sets{$a[1]}));
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
my $command = $a[1];
|
return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0);
|
||||||
my $value = $a[2];
|
return "$cmd requires $sets{$match[0]} argument" unless (@a == $sets{$match[0]});
|
||||||
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
my $value = shift @a;
|
||||||
eval {
|
eval {
|
||||||
FRM_Client_FirmataDevice($hash)->servo_write($hash->{PIN},$value);
|
FRM_Client_FirmataDevice($hash)->servo_write($hash->{PIN},$value);
|
||||||
main::readingsSingleUpdate($hash,"state",$value, 1);
|
main::readingsSingleUpdate($hash,"state",$value, 1);
|
||||||
};
|
};
|
||||||
return $@;
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 CHANGES
|
||||||
|
|
||||||
|
05.09.2020 jensb
|
||||||
|
o check for IODev install error in Init and Set
|
||||||
|
o prototypes removed
|
||||||
|
o set argument verifier improved
|
||||||
|
|
||||||
|
19.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item device
|
||||||
|
|
||||||
|
=item summary Firmata: PWM controlled servo output
|
||||||
|
|
||||||
|
=item summary_DE Firmata: PWM gesteuerter Servo Ausgang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_SERVO"></a>
|
<a name="FRM_SERVO"/>
|
||||||
<h3>FRM_SERVO</h3>
|
<h3>FRM_SERVO</h3>
|
||||||
<ul>
|
<ul>
|
||||||
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
|
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
|
||||||
configured to drive a pwm-controlled servo-motor.<br>
|
configured to drive a pwm-controlled servo-motor.<br>
|
||||||
The value set will be drive the shaft of the servo to the specified angle. see <a href="http://arduino.cc/en/Reference/ServoWrite">Servo.write</a> for values and range<br>
|
The value set will be drive the shaft of the servo to the specified angle. see <a href="http://arduino.cc/en/Reference/ServoWrite">Servo.write</a> for values and range<br>
|
||||||
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
||||||
|
|
||||||
<a name="FRM_SERVOdefine"></a>
|
<a name="FRM_SERVOdefine"/>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_SERVO <pin></code> <br>
|
<code>define <name> FRM_SERVO <pin></code> <br>
|
||||||
Defines the FRM_SERVO device. <pin>> is the arduino-pin to use.
|
Defines the FRM_SERVO device. <pin>> is the arduino-pin to use.
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a name="FRM_SERVOset"></a>
|
<a name="FRM_SERVOset"/>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> angle <value></code><br>sets the angle of the servo-motors shaft to the value specified (in degrees).<br>
|
<code>set <name> angle <value></code><br>sets the angle of the servo-motors shaft to the value specified (in degrees).<br>
|
||||||
</ul>
|
</ul>
|
||||||
<a name="FRM_SERVOget"></a>
|
|
||||||
|
<a name="FRM_SERVOget"/>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
N/A
|
N/A
|
||||||
</ul><br>
|
</ul><br>
|
||||||
<a name="FRM_SERVOattr"></a>
|
|
||||||
|
<a name="FRM_SERVOattr"/>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
<a name="IODev"/>
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
<li><a href="#IODev">IODev</a><br>
|
||||||
than one FRM-device defined.)
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
</li>
|
</li>
|
||||||
<li>min-pulse<br>
|
|
||||||
sets the minimum puls-width to use. Defaults to 544. For most servos this translates into a rotation of 180° counterclockwise.</li>
|
<a name="min-pulse"/>
|
||||||
<li>max-pulse<br>
|
<li>min-pulse<br>
|
||||||
sets the maximum puls-width to use. Defaults to 2400. For most servos this translates into a rotation of 180° clockwise</li>
|
sets the minimum puls-width to use. Defaults to 544. For most servos this translates into a rotation of 180° counterclockwise.
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
</li>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
|
||||||
</ul>
|
<a name="max-pulse"/>
|
||||||
|
<li>max-pulse<br>
|
||||||
|
sets the maximum puls-width to use. Defaults to 2400. For most servos this translates into a rotation of 180° clockwise.
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_SERVO"/>
|
||||||
|
<h3>FRM_SERVO</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_SERVO gibt es nur auf <a href="commandref.html#FRM_SERVO">Englisch</a>. <br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -1,6 +1,42 @@
|
|||||||
##############################################
|
########################################################################################
|
||||||
# $Id$
|
# $Id$
|
||||||
##############################################
|
########################################################################################
|
||||||
|
|
||||||
|
=encoding UTF-8
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
FHEM module for two/four Firmata stepper motor output pins
|
||||||
|
|
||||||
|
=head1 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (C) 2013 ntruchess
|
||||||
|
Copyright (C) 2020 jensb
|
||||||
|
|
||||||
|
All rights reserved
|
||||||
|
|
||||||
|
This script is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This script is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this script; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
A copy of the GNU General Public License, Version 2 can also be found at
|
||||||
|
|
||||||
|
http://www.gnu.org/licenses/old-licenses/gpl-2.0.
|
||||||
|
|
||||||
|
This copyright notice MUST APPEAR in all copies of the script!
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
@ -8,136 +44,151 @@ use warnings;
|
|||||||
|
|
||||||
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though...
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (!grep(/FHEM\/lib$/,@INC)) {
|
if (!grep(/FHEM\/lib$/,@INC)) {
|
||||||
foreach my $inc (grep(/FHEM$/,@INC)) {
|
foreach my $inc (grep(/FHEM$/,@INC)) {
|
||||||
push @INC,$inc."/lib";
|
push @INC,$inc."/lib";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
use Device::Firmata::Constants qw/ :all /;
|
|
||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
|
|
||||||
|
# (min) number of arguments
|
||||||
my %sets = (
|
my %sets = (
|
||||||
"reset" => "noArg",
|
"reset:noArg" => 0,
|
||||||
"position" => "",
|
"position" => 1,
|
||||||
"step" => "",
|
"step" => 1,
|
||||||
);
|
);
|
||||||
|
|
||||||
my %gets = (
|
my %gets = (
|
||||||
"position" => "noArg",
|
"position" => "",
|
||||||
);
|
);
|
||||||
|
|
||||||
sub
|
sub FRM_STEPPER_Initialize
|
||||||
FRM_STEPPER_Initialize($)
|
|
||||||
{
|
{
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
$hash->{SetFn} = "FRM_STEPPER_Set";
|
$hash->{SetFn} = "FRM_STEPPER_Set";
|
||||||
$hash->{GetFn} = "FRM_STEPPER_Get";
|
$hash->{GetFn} = "FRM_STEPPER_Get";
|
||||||
$hash->{DefFn} = "FRM_Client_Define";
|
$hash->{DefFn} = "FRM_STEPPER_Define";
|
||||||
$hash->{InitFn} = "FRM_STEPPER_Init";
|
$hash->{InitFn} = "FRM_STEPPER_Init";
|
||||||
$hash->{UndefFn} = "FRM_Client_Undef";
|
$hash->{UndefFn} = "FRM_Client_Undef";
|
||||||
$hash->{AttrFn} = "FRM_STEPPER_Attr";
|
$hash->{AttrFn} = "FRM_STEPPER_Attr";
|
||||||
$hash->{StateFn} = "FRM_STEPPER_State";
|
$hash->{StateFn} = "FRM_STEPPER_State";
|
||||||
|
|
||||||
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off speed acceleration deceleration IODev $main::readingFnAttributes";
|
$hash->{AttrList} = "restoreOnReconnect:on,off restoreOnStartup:on,off speed acceleration deceleration IODev $main::readingFnAttributes";
|
||||||
main::LoadModule("FRM");
|
main::LoadModule("FRM");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_STEPPER_Define
|
||||||
FRM_STEPPER_Init($$)
|
|
||||||
{
|
{
|
||||||
my ($hash,$args) = @_;
|
my ($hash, $def) = @_;
|
||||||
|
|
||||||
my $u = "wrong syntax: define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] directionPin stepPin [motorPin3 motorPin4] stepsPerRev [id]";
|
# verify define arguments
|
||||||
return $u unless defined $args;
|
my $usage = "usage: define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] directionPin stepPin [motorPin3 motorPin4] stepsPerRev [id]";
|
||||||
|
|
||||||
my $driver = shift @$args;
|
|
||||||
|
|
||||||
return $u unless ( $driver eq 'DRIVER' or $driver eq 'TWO_WIRE' or $driver eq 'FOUR_WIRE' );
|
|
||||||
return $u if (($driver eq 'DRIVER' or $driver eq 'TWO_WIRE') and (scalar(@$args) < 3 or scalar(@$args) > 4));
|
|
||||||
return $u if (($driver eq 'FOUR_WIRE') and (scalar(@$args) < 5 or scalar(@$args) > 6));
|
|
||||||
|
|
||||||
$hash->{DRIVER} = $driver;
|
|
||||||
|
|
||||||
$hash->{PIN1} = shift @$args;
|
|
||||||
$hash->{PIN2} = shift @$args;
|
|
||||||
|
|
||||||
if ($driver eq 'FOUR_WIRE') {
|
|
||||||
$hash->{PIN3} = shift @$args;
|
|
||||||
$hash->{PIN4} = shift @$args;
|
|
||||||
}
|
|
||||||
|
|
||||||
$hash->{STEPSPERREV} = shift @$args;
|
|
||||||
$hash->{STEPPERNUM} = shift @$args;
|
|
||||||
|
|
||||||
eval {
|
|
||||||
FRM_Client_AssignIOPort($hash);
|
|
||||||
my $firmata = FRM_Client_FirmataDevice($hash);
|
|
||||||
$firmata->stepper_config(
|
|
||||||
$hash->{STEPPERNUM},
|
|
||||||
$driver,
|
|
||||||
$hash->{STEPSPERREV},
|
|
||||||
$hash->{PIN1},
|
|
||||||
$hash->{PIN2},
|
|
||||||
$hash->{PIN3},
|
|
||||||
$hash->{PIN4});
|
|
||||||
$firmata->observe_stepper(0, \&FRM_STEPPER_observer, $hash );
|
|
||||||
};
|
|
||||||
if ($@) {
|
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
|
||||||
$hash->{STATE} = "error initializing: ".$1;
|
|
||||||
return "error initializing '".$hash->{NAME}."': ".$1;
|
|
||||||
}
|
|
||||||
$hash->{POSITION} = 0;
|
|
||||||
$hash->{DIRECTION} = 0;
|
|
||||||
$hash->{STEPS} = 0;
|
|
||||||
if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) {
|
|
||||||
$main::attr{$hash->{NAME}}{"stateFormat"} = "position";
|
|
||||||
}
|
|
||||||
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
FRM_STEPPER_observer
|
my $args = [@a[2..scalar(@a)-1]];
|
||||||
{
|
return $usage unless defined $args;
|
||||||
my ( $stepper, $hash ) = @_;
|
|
||||||
my $name = $hash->{NAME};
|
|
||||||
Log3 $name,5,"onStepperMessage for pins ".$hash->{PIN1}.",".$hash->{PIN2}.(defined ($hash->{PIN3}) ? ",".$hash->{PIN3} : ",-").(defined ($hash->{PIN4}) ? ",".$hash->{PIN4} : ",-")." stepper: ".$stepper;
|
|
||||||
my $position = $hash->{DIRECTION} ? $hash->{POSITION} - $hash->{STEPS} : $hash->{POSITION} + $hash->{STEPS};
|
|
||||||
$hash->{POSITION} = $position;
|
|
||||||
$hash->{DIRECTION} = 0;
|
|
||||||
$hash->{STEPS} = 0;
|
|
||||||
main::readingsSingleUpdate($hash,"position",$position,1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub
|
my $driver = shift @$args;
|
||||||
FRM_STEPPER_Set
|
return $usage unless ( $driver eq 'DRIVER' or $driver eq 'TWO_WIRE' or $driver eq 'FOUR_WIRE' );
|
||||||
{
|
return $usage if (($driver eq 'DRIVER' or $driver eq 'TWO_WIRE') and (scalar(@$args) < 3 or scalar(@$args) > 4));
|
||||||
my ($hash, @a) = @_;
|
return $usage if (($driver eq 'FOUR_WIRE') and (scalar(@$args) < 5 or scalar(@$args) > 6));
|
||||||
return "Need at least one parameters" if(@a < 2);
|
|
||||||
shift @a;
|
$hash->{DRIVER} = $driver;
|
||||||
my $name = $hash->{NAME};
|
|
||||||
my $command = shift @a;
|
$hash->{PIN1} = shift @$args;
|
||||||
if(!defined($sets{$command})) {
|
$hash->{PIN2} = shift @$args;
|
||||||
my @commands = ();
|
|
||||||
foreach my $key (sort keys %sets) {
|
if ($driver eq 'FOUR_WIRE') {
|
||||||
push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key;
|
$hash->{PIN3} = shift @$args;
|
||||||
}
|
$hash->{PIN4} = shift @$args;
|
||||||
return "Unknown argument $command, choose one of " . join(" ", @commands);
|
|
||||||
}
|
}
|
||||||
COMMAND_HANDLER: {
|
|
||||||
$command eq "reset" and do {
|
$hash->{STEPSPERREV} = shift @$args;
|
||||||
|
$hash->{STEPPERNUM} = shift @$args;
|
||||||
|
|
||||||
|
my $ret = FRM_Client_Define($hash, $def);
|
||||||
|
if ($ret) {
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_STEPPER_Init
|
||||||
|
{
|
||||||
|
my ($hash,$args) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
|
|
||||||
|
eval {
|
||||||
|
FRM_Client_AssignIOPort($hash);
|
||||||
|
my $firmata = FRM_Client_FirmataDevice($hash);
|
||||||
|
$firmata->stepper_config(
|
||||||
|
$hash->{STEPPERNUM},
|
||||||
|
$hash->{DRIVER},
|
||||||
|
$hash->{STEPSPERREV},
|
||||||
|
$hash->{PIN1},
|
||||||
|
$hash->{PIN2},
|
||||||
|
$hash->{PIN3},
|
||||||
|
$hash->{PIN4});
|
||||||
|
$firmata->observe_stepper(0, \&FRM_STEPPER_observer, $hash );
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
readingsSingleUpdate($hash, 'state', "error initializing: $ret", 1);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash->{POSITION} = 0;
|
||||||
|
$hash->{DIRECTION} = 0;
|
||||||
|
$hash->{STEPS} = 0;
|
||||||
|
if (! (defined AttrVal($name,"stateFormat",undef))) {
|
||||||
|
$main::attr{$name}{"stateFormat"} = "position";
|
||||||
|
}
|
||||||
|
|
||||||
|
main::readingsSingleUpdate($hash,"state","Initialized",1);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_STEPPER_observer
|
||||||
|
{
|
||||||
|
my ( $stepper, $hash ) = @_;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
Log3 $name, 5, "$name: observer pins: ".$hash->{PIN1}.",".$hash->{PIN2}.(defined ($hash->{PIN3}) ? ",".$hash->{PIN3} : ",-").(defined ($hash->{PIN4}) ? ",".$hash->{PIN4} : ",-")." stepper: ".$stepper;
|
||||||
|
my $position = $hash->{DIRECTION} ? $hash->{POSITION} - $hash->{STEPS} : $hash->{POSITION} + $hash->{STEPS};
|
||||||
|
$hash->{POSITION} = $position;
|
||||||
|
$hash->{DIRECTION} = 0;
|
||||||
|
$hash->{STEPS} = 0;
|
||||||
|
main::readingsSingleUpdate($hash,"position",$position,1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub FRM_STEPPER_Set
|
||||||
|
{
|
||||||
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
|
|
||||||
|
return "set command missing" if(!defined($cmd));
|
||||||
|
my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets );
|
||||||
|
return "unknown set command '$cmd', choose one of " . join(" ", sort keys %sets) if ($cmd eq '?' || @match == 0);
|
||||||
|
return "$cmd requires (at least) $sets{$match[0]} argument(s)" unless (@a >= $sets{$match[0]});
|
||||||
|
|
||||||
|
my $value = shift @a;
|
||||||
|
SETHANDLER: {
|
||||||
|
$cmd eq "reset" and do {
|
||||||
$hash->{POSITION} = 0;
|
$hash->{POSITION} = 0;
|
||||||
main::readingsSingleUpdate($hash,"position",0,1);
|
main::readingsSingleUpdate($hash,"position",0,1);
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$command eq "position" and do {
|
$cmd eq "position" and do {
|
||||||
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
my $position = $hash->{POSITION};
|
my $position = $hash->{POSITION};
|
||||||
my $value = shift @a;
|
|
||||||
my $direction = $value < $position ? 1 : 0;
|
my $direction = $value < $position ? 1 : 0;
|
||||||
my $steps = $direction ? $position - $value : $value - $position;
|
my $steps = $direction ? $position - $value : $value - $position;
|
||||||
my $speed = shift @a;
|
my $speed = shift @a;
|
||||||
@ -149,13 +200,19 @@ FRM_STEPPER_Set
|
|||||||
$hash->{DIRECTION} = $direction;
|
$hash->{DIRECTION} = $direction;
|
||||||
$hash->{STEPS} = $steps;
|
$hash->{STEPS} = $steps;
|
||||||
eval {
|
eval {
|
||||||
# $stepperNum, $direction, $numSteps, $stepSpeed, $accel, $decel
|
|
||||||
FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel);
|
FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel);
|
||||||
};
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
$command eq "step" and do {
|
$cmd eq "step" and do {
|
||||||
my $value = shift @a;
|
if (defined($main::defs{$name}{IODev_ERROR})) {
|
||||||
|
return 'Perl module Device::Firmata not properly installed';
|
||||||
|
}
|
||||||
my $direction = $value < 0 ? 1 : 0;
|
my $direction = $value < 0 ? 1 : 0;
|
||||||
my $steps = abs $value;
|
my $steps = abs $value;
|
||||||
my $speed = shift @a;
|
my $speed = shift @a;
|
||||||
@ -167,42 +224,53 @@ FRM_STEPPER_Set
|
|||||||
$hash->{DIRECTION} = $direction;
|
$hash->{DIRECTION} = $direction;
|
||||||
$hash->{STEPS} = $steps;
|
$hash->{STEPS} = $steps;
|
||||||
eval {
|
eval {
|
||||||
# $stepperNum, $direction, $numSteps, $stepSpeed, $accel, $decel
|
|
||||||
FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel);
|
FRM_Client_FirmataDevice($hash)->stepper_step($hash->{STEPPERNUM},$direction,$steps,$speed,$accel,$decel);
|
||||||
};
|
};
|
||||||
|
if ($@) {
|
||||||
|
my $ret = FRM_Catch($@);
|
||||||
|
$hash->{STATE} = "set $cmd error: " . $ret;
|
||||||
|
return $hash->{STATE};
|
||||||
|
}
|
||||||
last;
|
last;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_STEPPER_Get
|
||||||
FRM_STEPPER_Get
|
|
||||||
{
|
{
|
||||||
my ($hash, @a) = @_;
|
my ($hash, $name, $cmd, @a) = @_;
|
||||||
return "Need at least one parameters" if(@a < 2);
|
|
||||||
shift @a;
|
return "get command missing" if(!defined($cmd));
|
||||||
my $name = $hash->{NAME};
|
return "unknown get command '$cmd', choose one of " . join(":noArg ", sort keys %gets) . ":noArg" if(!defined($gets{$cmd}));
|
||||||
my $command = shift @a;
|
|
||||||
return "Unknown argument $command, choose one of " . join(" ", sort keys %gets) unless defined($gets{$command});
|
GETHANDLER: {
|
||||||
|
$cmd eq 'position' and do {
|
||||||
|
return $hash->{POSITION};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub FRM_STEPPER_State($$$$)
|
sub FRM_STEPPER_State
|
||||||
{
|
{
|
||||||
my ($hash, $tim, $sname, $sval) = @_;
|
my ($hash, $tim, $sname, $sval) = @_;
|
||||||
|
|
||||||
STATEHANDLER: {
|
STATEHANDLER: {
|
||||||
$sname eq "value" and do {
|
$sname eq "value" and do {
|
||||||
if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") {
|
if (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") {
|
||||||
FRM_STEPPER_Set($hash,$hash->{NAME},$sval);
|
FRM_STEPPER_Set($hash,$hash->{NAME},$sval);
|
||||||
}
|
}
|
||||||
last;
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub
|
sub FRM_STEPPER_Attr
|
||||||
FRM_STEPPER_Attr($$$$) {
|
{
|
||||||
my ($command,$name,$attribute,$value) = @_;
|
my ($command,$name,$attribute,$value) = @_;
|
||||||
my $hash = $main::defs{$name};
|
my $hash = $main::defs{$name};
|
||||||
eval {
|
eval {
|
||||||
@ -219,115 +287,183 @@ FRM_STEPPER_Attr($$$$) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
$@ =~ /^(.*)( at.*FHEM.*)$/;
|
my $ret = FRM_Catch($@);
|
||||||
$hash->{STATE} = "error setting $attribute to $value: ".$1;
|
$hash->{STATE} = "$command $attribute error: " . $ret;
|
||||||
return "cannot $command attribute $attribute to $value for $name: ".$1;
|
return $hash->{STATE};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
|
=head1 CHANGES
|
||||||
|
|
||||||
|
05.09.2020 jensb
|
||||||
|
o check for IODev install error in Init and Set
|
||||||
|
o prototypes removed
|
||||||
|
o get position implemented
|
||||||
|
o set argument verifier improved
|
||||||
|
o module help updated
|
||||||
|
o moved define argument verification and decoding from Init to Define
|
||||||
|
|
||||||
|
22.10.2020 jensb
|
||||||
|
o annotaded module help of attributes for FHEMWEB
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 FHEM COMMANDREF METADATA
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item device
|
||||||
|
|
||||||
|
=item summary Firmata: rotary encoder input
|
||||||
|
|
||||||
|
=item summary_DE Firmata: Drehgeber Eingang
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 INSTALLATION AND CONFIGURATION
|
||||||
|
|
||||||
=begin html
|
=begin html
|
||||||
|
|
||||||
<a name="FRM_STEPPER"></a>
|
<a name="FRM_STEPPER"></a>
|
||||||
<h3>FRM_STEPPER</h3>
|
<h3>FRM_STEPPER</h3>
|
||||||
<ul>
|
<ul>
|
||||||
represents a stepper-motor attached to digital-i/o pins of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a><br>
|
represents a stepper-motor attached to digital-i/o pins of an <a href="http://www.arduino.cc">Arduino</a>
|
||||||
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
running <a href="http://www.firmata.org">Firmata</a><br>
|
||||||
|
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
|
||||||
|
|
||||||
<a name="FRM_STEPPERdefine"></a>
|
<a name="FRM_STEPPERdefine"></a>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] <directionPin> <stepPin> [motorPin3 motorPin4] stepsPerRev [stepper-id]</code><br>
|
<code>define <name> FRM_STEPPER [DRIVER|TWO_WIRE|FOUR_WIRE] <directionPin> <stepPin> [motorPin3 motorPin4] stepsPerRev [stepper-id]</code><br>
|
||||||
Defines the FRM_STEPPER device.
|
Defines the FRM_STEPPER device.
|
||||||
<li>[DRIVER|TWO_WIRE|FOUR_WIRE] defines the control-sequence being used to drive the motor.
|
<li>[DRIVER|TWO_WIRE|FOUR_WIRE] defines the control-sequence being used to drive the motor.
|
||||||
<ul>
|
<ul>
|
||||||
<li>DRIVER: motor is attached via a smart circuit that is controlled via two lines: 1 line defines the direction to turn, the other triggers one step per impluse.</li>
|
<li>DRIVER: motor is attached via a smart circuit that is controlled via two lines: 1 line defines the
|
||||||
<li>FOUR_WIRE: motor is attached via four wires each driving one coil individually.</li>
|
direction to turn, the other triggers one step per impluse.
|
||||||
<li>TWO_WIRE: motor is attached via two wires. This mode makes use of the fact that at any time two of the four motor
|
</li>
|
||||||
coils are the inverse of the other two so by using an inverting circuit to drive the motor the number of control connections can be reduced from 4 to 2.</li>
|
<li>FOUR_WIRE: motor is attached via four wires each driving one coil individually.</li>
|
||||||
</ul>
|
<li>TWO_WIRE: motor is attached via two wires. This mode makes use of the fact that at any time two of
|
||||||
</li>
|
the four motor coils are the inverse of the other two so by using an inverting circuit to drive the motor
|
||||||
<li>
|
the number of control connections can be reduced from 4 to 2.
|
||||||
<ul>
|
</li>
|
||||||
<li>The sequence of control signals for 4 control wires is as follows:<br>
|
</ul>
|
||||||
<br>
|
</li>
|
||||||
<code>
|
<li>
|
||||||
Step C0 C1 C2 C3<br>
|
<ul>
|
||||||
1 1 0 1 0<br>
|
<li>The sequence of control signals for 4 control wires is as follows:<br><br>
|
||||||
2 0 1 1 0<br>
|
|
||||||
3 0 1 0 1<br>
|
<code>
|
||||||
4 1 0 0 1<br>
|
Step C0 C1 C2 C3<br>
|
||||||
</code>
|
1 1 0 1 0<br>
|
||||||
</li>
|
2 0 1 1 0<br>
|
||||||
<li>The sequence of controls signals for 2 control wires is as follows:<br>
|
3 0 1 0 1<br>
|
||||||
(columns C1 and C2 from above):<br>
|
4 1 0 0 1<br>
|
||||||
<br>
|
</code>
|
||||||
<code>
|
</li>
|
||||||
Step C0 C1<br>
|
<li>The sequence of controls signals for 2 control wires is as follows:<br>
|
||||||
1 0 1<br>
|
(columns C1 and C2 from above):<br><br>
|
||||||
2 1 1<br>
|
|
||||||
3 1 0<br>
|
<code>
|
||||||
4 0 0<br>
|
Step C0 C1<br>
|
||||||
</code>
|
1 0 1<br>
|
||||||
</li>
|
2 1 1<br>
|
||||||
</ul>
|
3 1 0<br>
|
||||||
</li>
|
4 0 0<br>
|
||||||
<li>
|
</code>
|
||||||
If your stepper-motor does not move or does move but only in a single direction you will have to rearrage the pin-numbers to match the control sequence.<br>
|
</li>
|
||||||
that can be archived either by rearranging the physical connections, or by mapping the connection to the pin-definitions in FRM_STEPPERS define:<br>
|
</ul>
|
||||||
e.g. the widely used cheap 28byj-48 you can get for few EUR on eBay including a simple ULN2003 driver interface may be defined by<br>
|
</li>
|
||||||
<code>define stepper FRM_STEPPER FOUR_WIRE 7 5 6 8 64 0</code><br>
|
<li>
|
||||||
when being connected to the arduio with:<br>
|
If your stepper-motor does not move or does move but only in a single direction you will have to rearrage
|
||||||
<code>motor pin1 <-> arduino pin5<br>
|
the pin-numbers to match the control sequence. That can be archived either by rearranging the physical
|
||||||
motor pin2 <-> arduino pin6<br>
|
connections, or by mapping the connection to the pin-definitions in FRM_STEPPERS define:<br>
|
||||||
motor pin3 <-> arduino pin7<br>
|
e.g. the widely used cheap 28byj-48 you can get for few EUR on eBay including a simple ULN2003 driver
|
||||||
motor pin4 <-> arduino pin8<br>
|
interface may be defined by<br>
|
||||||
motor pin5 <-> ground</code><br>
|
<code>define stepper FRM_STEPPER FOUR_WIRE 7 5 6 8 64 0</code><br>
|
||||||
</li>
|
when being connected to the arduio with:<br><br>
|
||||||
</ul>
|
|
||||||
|
<code>
|
||||||
<br>
|
motor pin1 <-> arduino pin5<br>
|
||||||
|
motor pin2 <-> arduino pin6<br>
|
||||||
|
motor pin3 <-> arduino pin7<br>
|
||||||
|
motor pin4 <-> arduino pin8<br>
|
||||||
|
motor pin5 <-> ground
|
||||||
|
</code>
|
||||||
|
</li><br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_STEPPERset"></a>
|
<a name="FRM_STEPPERset"></a>
|
||||||
<b>Set</b><br>
|
<b>Set</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<code>set <name> reset</code>
|
<code>set <name> reset</code>
|
||||||
<li>resets the reading 'position' to 0 without moving the motor</li>
|
<li>resets the reading 'position' to 0 without moving the motor</li><br>
|
||||||
<br>
|
|
||||||
<code>set <name> position <position> [speed] [acceleration] [deceleration]</code>
|
<code>set <name> position <position> [speed] [acceleration] [deceleration]</code>
|
||||||
<li>moves the motor to the absolute position specified. positive or negative integer<br>
|
<li>moves the motor to the absolute position specified. positive or negative integer<br>
|
||||||
speed (10 * revolutions per minute, optional), defaults to 30, higher numbers are faster) At 2048 steps per revolution (28byj-48) a speed of 30 results in 3 rev/min<br>
|
speed (10 * revolutions per minute, optional), defaults to 30, higher numbers are faster.
|
||||||
acceleration and deceleration are optional.<br>
|
At 2048 steps per revolution (28byj-48) a speed of 30 results in 3 rev/min<br>
|
||||||
</li>
|
acceleration and deceleration are optional.<br>
|
||||||
<br>
|
</li><br>
|
||||||
<code>set <name> step <stepstomove> [speed] [accel] [decel]</code>
|
|
||||||
<li>moves the motor the number of steps specified. positive or negative integer<br>
|
<code>set <name> step <stepstomove> [speed] [accel] [decel]</code>
|
||||||
speed, accelleration and deceleration are optional.<br>
|
<li>moves the motor the number of steps specified. positive or negative integer<br>
|
||||||
</li>
|
speed, accelleration and deceleration are optional.<br>
|
||||||
</ul>
|
</li>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_STEPPERget"></a>
|
<a name="FRM_STEPPERget"></a>
|
||||||
<b>Get</b><br>
|
<b>Get</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
N/A
|
<code>get <position></code>
|
||||||
|
<li>returns the current position value</li>
|
||||||
</ul><br>
|
</ul><br>
|
||||||
|
|
||||||
<a name="FRM_STEPPERattr"></a>
|
<a name="FRM_STEPPERattr"></a>
|
||||||
<b>Attributes</b><br>
|
<b>Attributes</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
<li>restoreOnStartup <on|off></li>
|
<a name="restoreOnStartup"/>
|
||||||
<li>restoreOnReconnect <on|off></li>
|
<li>restoreOnStartup <on|off></li>
|
||||||
<li><a href="#IODev">IODev</a><br>
|
|
||||||
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
|
<a name="restoreOnReconnect"/>
|
||||||
than one FRM-device defined.)
|
<li>restoreOnReconnect <on|off></li>
|
||||||
</li>
|
|
||||||
<li>>speed (same meaning as in 'set position')</li>
|
<a name="IODev"/>
|
||||||
<li>acceleration (same meaning as in 'set position')</li>
|
<li><a href="#IODev">IODev</a><br>
|
||||||
<li>deceleration (same meaning as in 'set position')</li>
|
Specify which <a href="#FRM">FRM</a> to use. Only required if there is more than one FRM-device defined.
|
||||||
<li><a href="#eventMap">eventMap</a><br></li>
|
</li>
|
||||||
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
|
|
||||||
</ul>
|
<a name="speed"/>
|
||||||
|
<li>>speed (same meaning as in 'set position')</li>
|
||||||
|
|
||||||
|
<a name="acceleration"/>
|
||||||
|
<li>acceleration (same meaning as in 'set position')</li>
|
||||||
|
|
||||||
|
<a name="deceleration"/>
|
||||||
|
<li>deceleration (same meaning as in 'set position')</li>
|
||||||
|
|
||||||
|
<li><a href="#attributes">global attributes</a></li>
|
||||||
|
|
||||||
|
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
</ul><br>
|
||||||
|
|
||||||
=end html
|
=end html
|
||||||
|
|
||||||
|
=begin html_DE
|
||||||
|
|
||||||
|
<a name="FRM_STEPPER"></a>
|
||||||
|
<h3>FRM_STEPPER</h3>
|
||||||
|
<ul>
|
||||||
|
Die Modulbeschreibung von FRM_STEPPER gibt es nur auf <a href="commandref.html#FRM_STEPPER">Englisch</a>. <br>
|
||||||
|
</ul><br>
|
||||||
|
|
||||||
|
=end html_DE
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
@ -118,15 +118,14 @@ FHEM/18_CUL_HOERMANN.pm rudolfkoenig SlowRF
|
|||||||
FHEM/19_Revolt.pm yoda_gh SlowRF
|
FHEM/19_Revolt.pm yoda_gh SlowRF
|
||||||
FHEM/19_VBUSIF.pm Tobias/pejonp Sonstige Systeme
|
FHEM/19_VBUSIF.pm Tobias/pejonp Sonstige Systeme
|
||||||
FHEM/20_FRM_AD.pm jensb Sonstige Systeme
|
FHEM/20_FRM_AD.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_I2C.pm ntruchsess Sonstige Systeme
|
FHEM/20_FRM_I2C.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_IN.pm jensb Sonstige Systeme
|
FHEM/20_FRM_IN.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_LCD.pm ntruchsess Sonstige Systeme (deprecated)
|
|
||||||
FHEM/20_FRM_OUT.pm jensb Sonstige Systeme
|
FHEM/20_FRM_OUT.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_PWM.pm jensb Sonstige Systeme
|
FHEM/20_FRM_PWM.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_RGB.pm ntruchsess Sonstige Systeme
|
FHEM/20_FRM_RGB.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_ROTENC.pm ntruchsess Sonstige Systeme
|
FHEM/20_FRM_ROTENC.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_SERVO.pm ntruchsess Sonstige Systeme
|
FHEM/20_FRM_SERVO.pm jensb Sonstige Systeme
|
||||||
FHEM/20_FRM_STEPPER.pm ntruchsess Sonstige Systeme
|
FHEM/20_FRM_STEPPER.pm jensb Sonstige Systeme
|
||||||
FHEM/20_GUEST.pm loredo Automatisierung
|
FHEM/20_GUEST.pm loredo Automatisierung
|
||||||
FHEM/20_N4HBUS.pm okoerber Sonstige Systeme
|
FHEM/20_N4HBUS.pm okoerber Sonstige Systeme
|
||||||
FHEM/20_OWFS.pm mfr69bs 1Wire (deprecated)
|
FHEM/20_OWFS.pm mfr69bs 1Wire (deprecated)
|
||||||
@ -618,6 +617,7 @@ contrib/AttrTemplate/* Beta-User (depends on attrTemplate)
|
|||||||
contrib/commandref* rudolfkoenig Sonstiges
|
contrib/commandref* rudolfkoenig Sonstiges
|
||||||
contrib/pre-commit rudolfkoenig Sonstiges
|
contrib/pre-commit rudolfkoenig Sonstiges
|
||||||
contrib/DEBIAN/* betateilchen Sonstiges
|
contrib/DEBIAN/* betateilchen Sonstiges
|
||||||
|
contrib/deprecated/20_FRM_LCD.pm ntruchsess (deprecated)
|
||||||
contrib/deprecated/70_Pushalot.pm Talkabout (deprecated)
|
contrib/deprecated/70_Pushalot.pm Talkabout (deprecated)
|
||||||
contrib/deprecated/95_PachLog.pm rudolfkoenig/orphan (deprecated)
|
contrib/deprecated/95_PachLog.pm rudolfkoenig/orphan (deprecated)
|
||||||
contrib/DoorPi/70_DoorPi.pm pahenning Automatisierung
|
contrib/DoorPi/70_DoorPi.pm pahenning Automatisierung
|
||||||
|
Loading…
x
Reference in New Issue
Block a user