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:
parent
cc0eecb116
commit
6449ed79ed
448
fhem/FHEM/10_FRM.pm
Executable file
448
fhem/FHEM/10_FRM.pm
Executable 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 <name> FRM <device></code> <br>
|
||||
Specifies the FRM device. </ul>
|
||||
|
||||
<br>
|
||||
<ul>
|
||||
USB-connected devices:<br><ul>
|
||||
<device> 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>
|
||||
<device> 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
114
fhem/FHEM/20_FRM_AD.pm
Executable 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 <name> FRM_AD <pin></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
108
fhem/FHEM/20_FRM_I2C.pm
Executable 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 <name> FRM_I2C <i2c-address> <register> <bytes-to-read></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
114
fhem/FHEM/20_FRM_IN.pm
Executable 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 <name> FRM_IN <pin></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
111
fhem/FHEM/20_FRM_OUT.pm
Executable 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 <name> FRM_OUT <pin></code> <br>
|
||||
Specifies the FRM_OUT device.
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<a name="FRM_OUTset"></a>
|
||||
<b>Set</b><br>
|
||||
<ul>
|
||||
<code>set <name> 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
105
fhem/FHEM/20_FRM_PWM.pm
Executable 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 <name> FRM_PWM <pin></code> <br>
|
||||
Specifies the FRM_PWM device.
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<a name="FRM_PWMset"></a>
|
||||
<b>Set</b><br>
|
||||
<ul>
|
||||
<code>set <name> <value></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
|
Loading…
x
Reference in New Issue
Block a user