2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

Initial commit FRM modules (for Arduino/Firmata)

git-svn-id: https://svn.fhem.de/fhem/trunk@2581 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
ntruchsess 2013-01-27 19:50:22 +00:00
parent cc0eecb116
commit 6449ed79ed
6 changed files with 1000 additions and 0 deletions

448
fhem/FHEM/10_FRM.pm Executable file
View File

@ -0,0 +1,448 @@
##############################################
package main;
use strict;
use warnings;
use Device::Firmata::Constants qw/ :all /;
use Device::Firmata::IO;
use Device::Firmata::Protocol;
use Device::Firmata::Platform;
sub FRM_Set($@);
sub FRM_Attr(@);
sub Log($$);
#####################################
sub FRM_Initialize($) {
my ($hash) = @_;
require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
# Provider
$hash->{Clients} =
":FRM_IN:FRM_OUT:FRM_AD:FRM_PWM:FRM_I2C:";
$hash->{ReadyFn} = "FRM_Ready";
$hash->{ReadFn} = "FRM_Read";
# Consumer
$hash->{DefFn} = "FRM_Define";
$hash->{UndefFn} = "FRM_Undef";
$hash->{GetFn} = "FRM_Get";
$hash->{SetFn} = "FRM_Set";
$hash->{AttrFn} = "FRM_Attr";
$hash->{AttrList} = "model:nano dummy:1,0 loglevel:0,1,2,3,4,5 sampling-interval i2c-config $main::readingFnAttributes";
}
#####################################
sub FRM_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
my $po;
DevIo_CloseDev($hash);
my $name = $a[0];
my $dev = $a[2];
if ( $dev eq "none" ) {
Log 1, "FRM device is none, commands will be echoed only";
$main::attr{$name}{dummy} = 1;
return undef;
}
$hash->{DeviceName} = $dev;
my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit");
main::readingsSingleUpdate($hash,"state","initialized", 1);
return $ret;
}
#####################################
sub FRM_Undef($) {
my $hash = @_;
FRM_forall_clients($hash,\&FRM_Client_Unassign,undef);
DevIo_CloseDev($hash);
my $device = $hash->{FirmataDevice};
if (defined $device) {
if (defined $device->{io}) {
delete $hash->{FirmataDevice}->{io}->{handle} if defined $hash->{FirmataDevice}->{io}->{handle};
delete $hash->{FirmataDevice}->{io};
}
delete $device->{protocol} if defined $device->{protocol};
delete $hash->{FirmataDevice};
}
return undef;
}
#####################################
sub FRM_Set($@) {
my ( $hash, @a ) = @_;
my $u1 = "Usage: set <name> reset/reinit\n";
return $u1 if ( int(@a) < 2 );
my $name = $hash->{DeviceName};
if ( $a[1] eq 'reset' ) {
DevIo_CloseDev($hash);
my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit");
return $ret;
} elsif ( $a[1] eq 'reinit' ) {
FRM_forall_clients($hash,\&FRM_Init_Client,undef);
} else {
return "Unknown argument $a[1], supported arguments are 'reset', 'reinit'";
}
return undef;
}
#####################################
sub FRM_Get($@) {
my ( $hash, @a ) = @_;
return "\"get FRM\" needs only one parameter" if ( @a != 2 );
shift @a;
my $spec = shift @a;
if ( $spec eq "firmware" ) {
if (defined $hash->{FirmataDevice}) {
return $hash->{FirmataDevice}->{metadata}->{firmware};
} else {
return "not connected to FirmataDevice";
}
} elsif ( $spec eq "version" ) {
if (defined $hash->{FirmataDevice}) {
return $hash->{FirmataDevice}->{metadata}->{firmware_version};
} else {
return "not connected to FirmataDevice";
}
}
}
#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub FRM_Read($) {
my ( $hash ) = @_;
my $device = $hash->{FirmataDevice} or return;
$device->poll();
}
sub FRM_Ready($) {
my ($hash) = @_;
return DevIo_OpenDev($hash, 1, "FRM_DoInit") if($hash->{READINGS}{state} eq "disconnected");
}
sub FRM_Attr(@) {
my ($command,$name,$attribute,$value) = @_;
if ($command eq "set") {
$main::attr{$name}{$attribute}=$value;
if ($attribute eq "sampling-interval"
or $attribute eq "i2c-config" ) {
FRM_apply_attribute($main::defs{$name},$attribute);
}
}
}
sub FRM_apply_attribute {
my ($hash,$attribute) = @_;
my $firmata = $hash->{FirmataDevice};
my $name = $hash->{NAME};
if (defined $firmata) {
if ($attribute eq "sampling-interval") {
$firmata->sampling_interval(main::AttrVal($name,$attribute,"1000"));
} elsif ($attribute eq "i2c-config") {
my $i2cattr = main::AttrVal($name,$attribute,undef);
if (defined $i2cattr) {
my @a = split(" ", $i2cattr);
my $i2cpins = $firmata->{metadata}{i2c_pins};
if (defined $i2cpins and scalar @$i2cpins) {
foreach my $i2cpin (@$i2cpins) {
$firmata->pin_mode($i2cpin,PIN_I2C);
}
$firmata->i2c_config(@a);
$firmata->observe_i2c(\&FRM_i2c_observer,$hash);
} else {
Log 1,"Error, arduino doesn't support I2C";
}
}
}
}
}
sub FRM_DoInit($) {
my ($hash) = @_;
my $name = $hash->{NAME};
$hash->{loglevel} = main::GetLogLevel($name);
my $firmata_io = Firmata_IO->new($hash);
my $device = Device::Firmata::Platform->attach($firmata_io) or return 1;
$hash->{FirmataDevice} = $device;
$device->observe_string(\&FRM_string_observer,$hash);
my $found; # we cannot call $device->probe() here, as it doesn't select bevore read, so it would likely cause IODev to close the connection on the first attempt to read from empty stream
do {
$device->system_reset();
$device->firmware_version_query();
for (my $i=0;$i<50;$i++) {
my ($rout, $rin) = ('', '');
vec($rin, $hash->{FD}, 1) = 1;
my $nfound = select($rout=$rin, undef, undef, 0.1);
my $mfound = vec($rout, $hash->{FD}, 1);
if($mfound) {
$device->poll();
if ($device->{metadata}{firmware} && $device->{metadata}{firmware_version}){
$device->{protocol}->{protocol_version} = $device->{metadata}{firmware_version};
$main::defs{$name}{firmware} = $device->{metadata}{firmware};
$main::defs{$name}{firmware_version} = $device->{metadata}{firmware_version};
$device->analog_mapping_query();
$device->capability_query();
for (my $j=0;$j<100;$j++) {
my ($rout, $rin) = ('', '');
vec($rin, $hash->{FD}, 1) = 1;
my $nfound = select($rout=$rin, undef, undef, 0.1);
my $mfound = vec($rout, $hash->{FD}, 1);
if ($mfound) {
$device->poll();
if (($device->{metadata}{analog_mappings}) and ($device->{metadata}{capabilities})) {
my $inputpins = $device->{metadata}{input_pins};
$main::defs{$name}{input_pins} = join(",", sort{$a<=>$b}(@$inputpins));
my $outputpins = $device->{metadata}{output_pins};
$main::defs{$name}{output_pins} = join(",", sort{$a<=>$b}(@$outputpins));
my $analogpins = $device->{metadata}{analog_pins};
$main::defs{$name}{analog_pins} = join(",", sort{$a<=>$b}(@$analogpins));
my $i2cpins = $device->{metadata}{i2c_pins};
$main::defs{$name}{i2c_pins} = join(",", sort{$a<=>$b}(@$i2cpins));
my $onewirepins = $device->{metadata}{onewire_pins};
$main::defs{$name}{onewire_pins} = join(",", sort{$a<=>$b}(@$onewirepins));
$found = 1;
last;
}
} else {
select (undef,undef,undef,0.1);
}
}
$found = 1;
last;
}
} else {
select (undef,undef,undef,0.1);
}
}
} while (!$found);
FRM_apply_attribute($hash,"sampling-interval");
FRM_apply_attribute($hash,"i2c-config");
FRM_forall_clients($hash,\&FRM_Init_Client,undef);
return undef;
}
sub
FRM_forall_clients($$$)
{
my ($hash,$fn,$args) = @_;
foreach my $d ( sort keys %main::defs ) {
if ( defined( $main::defs{$d} )
&& defined( $main::defs{$d}{IODev} )
&& $main::defs{$d}{IODev} == $hash ) {
&$fn($main::defs{$d},$args);
}
}
return undef;
}
sub
FRM_Init_Client($$) {
my ($hash,$args) = @_;
$hash->{loglevel} = main::GetLogLevel($hash->{NAME});
main::CallFn($hash->{NAME},"InitFn",$hash,$args);
}
sub
FRM_Init_Pin_Client($$) {
my ($hash,$args) = @_;
my $u = "wrong syntax: define <name> FRM_XXX pin";
return $u if(int(@$args) < 3);
$hash->{PIN} = @$args[2];
}
sub
FRM_Client_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
main::readingsSingleUpdate($hash,"state","defined",0);
main::AssignIoPort($hash);
FRM_Init_Client($hash,\@a);
return undef;
}
sub
FRM_Client_Undef($$)
{
my ($hash, $name) = @_;
}
sub
FRM_Client_Unassign($)
{
my ($dev) = @_;
delete $dev->{IODev} if defined $dev->{IODev};
main::readingsSingleUpdate($dev,"state","defined",0);
}
package Firmata_IO {
sub new {
my ($class,$hash) = @_;
return bless {
hash => $hash,
}, $class;
}
sub data_write {
my ( $self, $buf ) = @_;
main::Log 5, ">".join(",",map{sprintf"%02x",ord$_}split//,$buf);
main::DevIo_SimpleWrite($self->{hash},$buf);
}
sub data_read {
my ( $self, $bytes ) = @_;
my $string = main::DevIo_SimpleRead($self->{hash});
if (defined $string ) {
main::Log 5,"<".join(",",map{sprintf"%02x",ord$_}split//,$string);
}
return $string;
}
}
sub
FRM_i2c_observer
{
my ($data,$hash) = @_;
main::Log 5,"onI2CMessage address: '".$data->{address}."', register: '".$data->{register}."' data: '".$data->{data}."'";
FRM_forall_clients($hash,\&FRM_i2c_update_device,$data);
}
sub FRM_i2c_update_device
{
my ($hash,$data) = @_;
if (defined $hash->{"i2c-address"} && $hash->{"i2c-address"}==$data->{address}) {
my $replydata = $data->{data};
my @values = split(" ",main::ReadingsVal($hash->{NAME},"values",""));
splice(@values,$data->{register},@$replydata, @$replydata);
main::readingsBeginUpdate($hash);
main::readingsBulkUpdate($hash,"state","active",0);
main::readingsBulkUpdate($hash,"values",join (" ",@values),1);
main::readingsEndUpdate($hash,undef);
}
}
sub FRM_string_observer
{
my ($string,$hash) = @_;
main::Log 4, "received String_data: ".$string;
main::readingsSingleUpdate($hash,"error",$string,1);
}
1;
=pod
=begin html
<a name="FRM"></a>
<h3>FRM</h3>
<ul>
connects fhem to <a href="http://www.arduino.cc">Arduino</a> using
the <a href="http://www.firmata.org">Firmata</a> protocol.
<br><br>
A single FRM device can serve multiple FRM-clients.<br><br>
Clients of FRM are:<br><br>
<a href="#FRM_IN">FRM_IN</a> for digital input<br>
<a href="#FRM_OUT">FRM_OUT</a> for digital out<br>
<a href="#FRM_AD">FRM_AD</a> for analog input<br>
<a href="#FRM_PWM">FRM_PWM</a> for analog (pulse_width_modulated) output<br>
<a href="#FRM_I2C">FRM_I2C</a> to read data from integrated circutes attached
to Arduino supporting the <a href="http://en.wikipedia.org/wiki/I%C2%B2C">
i2c-protocol</a>.<br><br>
Each client stands for a Pin of the Arduino configured for a specific use
(digital/analog in/out) or an integrated circuit connected to Arduino by i2c.<br><br>
Note: this module requires the <a href="https://github.com/amimoto/perl-firmata">Device::Firmata</a> module (perl-firmata).
You can download it <a href="https://github.com/amimoto/perl-firmata/archive/master.zip">as a single zip</a> file from github.
Copy 'lib/Device' (with all subdirectories) to e.g. FHEM directory (or other location within perl include path)<br><br>
Note: this module may require the Device::SerialPort or Win32::SerialPort
module if you attach the device via USB and the OS sets strange default
parameters for serial devices.<br><br>
<a name="FRMdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM &lt;device&gt;</code> <br>
Specifies the FRM device. </ul>
<br>
<ul>
USB-connected devices:<br><ul>
&lt;device&gt; specifies the serial port to communicate with the Arduino.
The name of the serial-device depends on your distribution, under
linux the cdc_acm kernel module is responsible, and usually a
/dev/ttyACM0 device will be created. If your distribution does not have a
cdc_acm module, you can force usbserial to handle the Arduino by the
following command:<ul>modprobe usbserial vendor=0x03eb
product=0x204b</ul>In this case the device is most probably
/dev/ttyUSB0.<br><br>
You can also specify a baudrate if the device name contains the @
character, e.g.: /dev/ttyACM0@38400<br><br>
If the baudrate is "directio" (e.g.: /dev/ttyACM0@directio), then the
perl module Device::SerialPort is not needed, and fhem opens the device
with simple file io. This might work if the operating system uses sane
defaults for the serial parameters, e.g. some Linux distributions and
OSX. <br><br>
The Arduino has to run 'StandardFirmata'. You can find StandardFirmata
in the Arduino-IDE under 'Examples->Firmata->StandardFirmata<br><br>
</ul>
Network-connected devices:<br><ul>
&lt;device&gt; specifies the host:port of the device. E.g.
192.168.0.244:2323<br>
As of now EthernetFirmata is still eperimental.
</ul>
<br>
If the device is called none, then no device will be opened, so you
can experiment without hardware attached.<br>
</ul>
<br>
<a name="FRMset"></a>
<b>Set</b>
<ul>
N/A<br>
</ul><br>
<a name="FRMattr"></a>
<b>Attributes</b><br>
<ul>
<li>i2c-config<br>
Configure the arduino for ic2 communication. This will enable i2c on the
i2c_pins received by the capability-query issued during initialization of FRM.<br>
As of Firmata 2.3 you can set a delay-time (in microseconds) that will be inserted into i2c
protocol when switching from write to read.<br>
See: <a href="http://www.firmata.org/wiki/Protocol#I2C">Firmata Protocol details about I2C</a><br>
</li><br>
<li>sampling-interval<br>
Configure the interval Firmata reports data to FRM. Unit is milliseconds.<br>
See: <a href="http://www.firmata.org/wiki/Protocol#Sampling_Interval">Firmata Protocol details about Sampling Interval</a></br>
</li>
</ul>
</ul>
<br>
=end html
=cut

114
fhem/FHEM/20_FRM_AD.pm Executable file
View File

@ -0,0 +1,114 @@
#############################################
package main;
use strict;
use warnings;
use Device::Firmata;
use Device::Firmata::Constants qw/ :all /;
#####################################
sub
FRM_AD_Initialize($)
{
my ($hash) = @_;
$hash->{GetFn} = "FRM_AD_Get";
$hash->{DefFn} = "FRM_Client_Define";
$hash->{InitFn} = "FRM_AD_Init";
$hash->{UndefFn} = "FRM_AD_Undef";
$hash->{AttrFn} = "FRM_Attr";
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5 $main::readingFnAttributes";
}
sub
FRM_AD_Init($$)
{
my ($hash,$args) = @_;
FRM_Init_Pin_Client($hash,$args);
if (defined $hash->{IODev}) {
my $firmata = $hash->{IODev}->{FirmataDevice};
if (defined $firmata and defined $hash->{PIN}) {
$firmata->pin_mode($hash->{PIN},PIN_ANALOG);
$firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash);
main::readingsSingleUpdate($hash,"state","initialized",1);
return undef;
}
}
return 1;
}
sub
FRM_AD_observer
{
my ($pin,$old,$new,$hash) = @_;
main::Log(6,"onAnalogMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"));
main::readingsSingleUpdate($hash,"state",$new, 1);
}
sub
FRM_AD_Get($)
{
my ($hash) = @_;
my $iodev = $hash->{IODev};
if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
my $ret = $iodev->{FirmataDevice}->analog_read($hash->{PIN});
return $ret;
} else {
return $hash->{NAME}." no IODev assigned" if (!defined $iodev);
return $hash->{NAME}.", ".$iodev->{NAME}." is not connected";
}
}
sub
FRM_AD_Undef($$)
{
my ($hash, $name) = @_;
}
1;
=pod
=begin html
<a name="FRM_AD"></a>
<h3>FRM_AD</h3>
<ul>
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
configured for analog input.<br>
The value read is stored in reading 'state'. Range is from 0 to 1.<br>
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
<a name="FRM_ADdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM_AD &lt;pin&gt;</code> <br>
Specifies the FRM_AD device.
</ul>
<br>
<a name="FRM_ADset"></a>
<b>Set</b><br>
<ul>
N/A<br>
</ul><br>
<a name="FRM_ADget"></a>
<b>Get</b><br>
<ul>
N/A<br>
</ul><br>
<a name="FRM_ADattr"></a>
<b>Attributes</b><br>
<ul>
<li><a href="#IODev">IODev</a><br>
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
than one FRM-device defined.)
</li>
<li><a href="#eventMap">eventMap</a><br></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
</ul>
</ul>
<br>
=end html
=cut

108
fhem/FHEM/20_FRM_I2C.pm Executable file
View File

@ -0,0 +1,108 @@
#############################################
package main;
use strict;
use warnings;
use Device::Firmata;
use Device::Firmata::Constants qw/ :all /;
#####################################
sub
FRM_I2C_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "FRM_Client_Define";
$hash->{InitFn} = "FRM_I2C_Init";
$hash->{UndefFn} = "FRM_I2C_Undef";
$hash->{AttrFn} = "FRM_I2C_Attr";
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5 $main::readingFnAttributes";
}
sub
FRM_I2C_Init($)
{
my ($hash,$args) = @_;
my $u = "wrong syntax: define <name> FRM_I2C address register numbytes";
return $u if(int(@$args) < 5);
$hash->{"i2c-address"} = @$args[2];
$hash->{"i2c-register"} = @$args[3];
$hash->{"i2c-bytestoread"} = @$args[4];
if (defined $hash->{IODev}) {
my $firmata = $hash->{IODev}->{FirmataDevice};
if (defined $firmata) {
$firmata->i2c_read(@$args[2],@$args[3],@$args[4]);
}
return undef;
}
return 1;
}
sub FRM_I2C_Attr(@) {
my ($command,$name,$attribute,$value) = @_;
my $hash = $main::defs{$name};
if ($command eq "set") {
$main::attr{$name}{$attribute}=$value;
}
}
sub
FRM_I2C_Undef($$)
{
my ($hash, $name) = @_;
}
1;
=pod
=begin html
<a name="FRM_I2C"></a>
<h3>FRM_I2C</h3>
<ul>
represents an integrated curcuit connected to the i2c-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>
this FRM-device has to be configures for i2c by setting attr 'i2c-config' on the FRM-device<br>
it reads out the ic-internal storage in intervals of 'sampling-interval' as set on the FRM-device<br><br>
<a name="FRM_I2Cdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM_I2C &lt;i2c-address&gt; &lt;register&gt; &lt;bytes-to-read&gt;</code> <br>
Specifies the FRM_I2C device.<br>
<li>i2c-address is the (device-specific) address of the ic on the i2c-bus</li>
<li>register is the (device-internal) address to start reading bytes from.</li>
<li>bytes-to-read is the number of bytes read from the ic</li>
</ul>
<br>
<a name="FRM_I2Cset"></a>
<b>Set</b><br>
<ul>
N/A<br>
</ul>
<a name="FRM_I2Cget"></a>
<b>Get</b><br>
<ul>
N/A<br>
</ul><br>
<a name="FRM_I2Cattr"></a>
<b>Attributes</b><br>
<ul>
<li><a href="#IODev">IODev</a><br>
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
than one FRM-device defined.)
</li>
<li><a href="#eventMap">eventMap</a><br></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
</ul>
</ul>
<br>
=end html
=cut

114
fhem/FHEM/20_FRM_IN.pm Executable file
View File

@ -0,0 +1,114 @@
#############################################
package main;
use strict;
use warnings;
use Device::Firmata;
use Device::Firmata::Constants qw/ :all /;
#####################################
sub
FRM_IN_Initialize($)
{
my ($hash) = @_;
$hash->{GetFn} = "FRM_IN_Get";
$hash->{DefFn} = "FRM_Client_Define";
$hash->{InitFn} = "FRM_IN_Init";
$hash->{UndefFn} = "FRM_IN_Undef";
$hash->{AttrFn} = "FRM_Attr";
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5 $main::readingFnAttributes";
}
sub
FRM_IN_Init($$)
{
my ($hash,$args) = @_;
FRM_Init_Pin_Client($hash,$args);
if (defined $hash->{IODev}) {
my $firmata = $hash->{IODev}->{FirmataDevice};
if (defined $firmata and defined $hash->{PIN}) {
$firmata->pin_mode($hash->{PIN},PIN_INPUT);
$firmata->observe_digital($hash->{PIN},\&FRM_IN_observer,$hash);
main::readingsSingleUpdate($hash,"state","initialized",1);
return undef;
}
}
return 1;
}
sub
FRM_IN_observer
{
my ($pin,$old,$new,$hash) = @_;
main::Log(6,"onDigitalMessage for pin ".$pin.", old: ".(defined $old ? $old : "--").", new: ".(defined $new ? $new : "--"));
main::readingsSingleUpdate($hash,"state",$new == PIN_HIGH ? "on" : "off", 1);
}
sub
FRM_IN_Get($)
{
my ($hash) = @_;
my $iodev = $hash->{IODev};
if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
my $ret = $iodev->{FirmataDevice}->digital_read($hash->{PIN});
return $ret == PIN_HIGH ? "on" : "off";
} else {
return $hash->{NAME}." no IODev assigned" if (!defined $iodev);
return $hash->{NAME}.", ".$iodev->{NAME}." is not connected";
}
}
sub
FRM_IN_Undef($$)
{
my ($hash, $name) = @_;
}
1;
=pod
=begin html
<a name="FRM_IN"></a>
<h3>FRM_IN</h3>
<ul>
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
configured for digital input.<br>
The current state of the arduino-pin is stored in reading 'state'. Values are 'on' and 'off'.<br>
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
<a name="FRM_INdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM_IN &lt;pin&gt;</code> <br>
Specifies the FRM_IN device.
</ul>
<br>
<a name="FRM_INset"></a>
<b>Set</b><br>
<ul>
N/A<br>
</ul>
<a name="FRM_INget"></a>
<b>Get</b><br>
<ul>
N/A<br>
</ul><br>
<a name="FRM_INattr"></a>
<b>Attributes</b><br>
<ul>
<li><a href="#IODev">IODev</a><br>
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
than one FRM-device defined.)
</li>
<li><a href="#eventMap">eventMap</a><br></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
</ul>
</ul>
<br>
=end html
=cut

111
fhem/FHEM/20_FRM_OUT.pm Executable file
View File

@ -0,0 +1,111 @@
#############################################
package main;
use strict;
use warnings;
use Device::Firmata;
use Device::Firmata::Constants qw/ :all /;
#####################################
sub
FRM_OUT_Initialize($)
{
my ($hash) = @_;
$hash->{SetFn} = "FRM_OUT_Set";
$hash->{DefFn} = "FRM_Client_Define";
$hash->{InitFn} = "FRM_OUT_Init";
$hash->{UndefFn} = "FRM_OUT_Undef";
$hash->{AttrFn} = "FRM_Attr";
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5 $main::readingFnAttributes";
}
sub
FRM_OUT_Init($$)
{
my ($hash,$args) = @_;
FRM_Init_Pin_Client($hash,$args);
if (defined $hash->{IODev}) {
my $firmata = $hash->{IODev}->{FirmataDevice};
if (defined $firmata and defined $hash->{PIN}) {
$firmata->pin_mode($hash->{PIN},PIN_OUTPUT);
main::readingsSingleUpdate($hash,"state","initialized",1);
}
}
}
sub
FRM_OUT_Set($@)
{
my ($hash, @a) = @_;
my $value;
if ($a[1] eq "on") {
$value=PIN_HIGH;
} elsif ($a[1] eq "off") {
$value=PIN_LOW;
} else {
return "illegal value '".$a[1]."', allowed are 'on' and 'off'";
}
my $iodev = $hash->{IODev};
if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
$iodev->{FirmataDevice}->digital_write($hash->{PIN},$value);
main::readingsSingleUpdate($hash,"state",$a[1], 1);
} else {
return $hash->{NAME}." no IODev assigned" if (!defined $iodev);
return $hash->{NAME}.", ".$iodev->{NAME}." is not connected";
}
return undef;
}
sub
FRM_OUT_Undef($$)
{
my ($hash, $name) = @_;
}
1;
=pod
=begin html
<a name="FRM_OUT"></a>
<h3>FRM_OUT</h3>
<ul>
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
configured for digital input.<br>
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
<a name="FRM_OUTdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM_OUT &lt;pin&gt;</code> <br>
Specifies the FRM_OUT device.
</ul>
<br>
<a name="FRM_OUTset"></a>
<b>Set</b><br>
<ul>
<code>set &lt;name&gt; on|off</code><br><br>
</ul>
<a name="FRM_OUTget"></a>
<b>Get</b><br>
<ul>
N/A
</ul><br>
<a name="FRM_OUTattr"></a>
<b>Attributes</b><br>
<ul>
<li><a href="#IODev">IODev</a><br>
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
than one FRM-device defined.)
</li>
<li><a href="#eventMap">eventMap</a><br></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
</ul>
</ul>
<br>
=end html
=cut

105
fhem/FHEM/20_FRM_PWM.pm Executable file
View File

@ -0,0 +1,105 @@
#############################################
package main;
use strict;
use warnings;
use Device::Firmata;
use Device::Firmata::Constants qw/ :all /;
#####################################
sub
FRM_PWM_Initialize($)
{
my ($hash) = @_;
$hash->{SetFn} = "FRM_PWM_Set";
$hash->{DefFn} = "FRM_Client_Define";
$hash->{InitFn} = "FRM_PWM_Init";
$hash->{UndefFn} = "FRM_PWM_Undef";
$hash->{AttrFn} = "FIR_Attr";
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5 $main::readingFnAttributes";
}
sub
FRM_PWM_Init($$)
{
my ($hash,$args) = @_;
FRM_Init_Pin_Client($hash,$args);
if (defined $hash->{IODev}) {
my $firmata = $hash->{IODev}->{FirmataDevice};
if (defined $firmata and defined $hash->{PIN}) {
$firmata->pin_mode($hash->{PIN},PIN_PWM);
main::readingsSingleUpdate($hash,"state","initialized",1);
}
}
}
sub
FRM_PWM_Set($@)
{
my ($hash, @a) = @_;
my $value = $a[1];
my $iodev = $hash->{IODev};
if (defined $iodev and defined $iodev->{FirmataDevice} and defined $iodev->{FD}) {
$iodev->{FirmataDevice}->analog_write($hash->{PIN},$value);
main::readingsSingleUpdate($hash,"state",$a[1], 1);
} else {
return $hash->{NAME}." no IODev assigned" if (!defined $iodev);
return $hash->{NAME}.", ".$iodev->{NAME}." is not connected";
}
return undef;
}
sub
FRM_PWM_Undef($$)
{
my ($hash, $name) = @_;
}
1;
=pod
=begin html
<a name="FRM_PWM"></a>
<h3>FRM_PWM</h3>
<ul>
represents a pin of an <a href="http://www.arduino.cc">Arduino</a> running <a href="http://www.firmata.org">Firmata</a>
configured for analog output.<br>
The value set will be output by the specified pin as a pulse-width-modulated signal.<br>
Requires a defined <a href="#FRM">FRM</a>-device to work.<br><br>
<a name="FRM_PWMdefine"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; FRM_PWM &lt;pin&gt;</code> <br>
Specifies the FRM_PWM device.
</ul>
<br>
<a name="FRM_PWMset"></a>
<b>Set</b><br>
<ul>
<code>set &lt;name&gt; &lt;value&gt;</code><br><br>
</ul>
<a name="FRM_PWMget"></a>
<b>Get</b><br>
<ul>
N/A
</ul><br>
<a name="FRM_PWMattr"></a>
<b>Attributes</b><br>
<ul>
<li><a href="#IODev">IODev</a><br>
Specify which <a href="#FRM">FRM</a> to use. (Optional, only required if there is more
than one FRM-device defined.)
</li>
<li><a href="#eventMap">eventMap</a><br></li>
<li><a href="#readingFnAttributes">readingFnAttributes</a><br></li>
</ul>
</ul>
<br>
=end html
=cut