adding support for EthernetClientFirmata (FRM run in server-mode)
git-svn-id: https://fhem.svn.sourceforge.net/svnroot/fhem/trunk/fhem@2864 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
150
FHEM/10_FRM.pm
150
FHEM/10_FRM.pm
@@ -45,22 +45,37 @@ sub FRM_Initialize($) {
|
|||||||
#####################################
|
#####################################
|
||||||
sub FRM_Define($$) {
|
sub FRM_Define($$) {
|
||||||
my ( $hash, $def ) = @_;
|
my ( $hash, $def ) = @_;
|
||||||
my @a = split( "[ \t][ \t]*", $def );
|
|
||||||
my $po;
|
my ($name, $type, $dev, $global) = split("[ \t]+", $def);
|
||||||
|
|
||||||
|
my $isServer = 1 if($dev && $dev =~ m/^(IPV6:)?\d+$/);
|
||||||
|
# my $isClient = 1 if($dev && $dev =~ m/^(IPV6:)?.*:\d+$/);
|
||||||
|
|
||||||
|
# return "Usage: define <name> FRM {<device>[@<baudrate>] | [IPV6:]<tcp-portnr> [global]}"
|
||||||
|
# if(!($isServer || $isClient) ||
|
||||||
|
# ($isClient && $global) ||
|
||||||
|
# ($global && $global ne "global"));
|
||||||
|
|
||||||
|
# Make sure that fhem only runs once
|
||||||
|
if($isServer) {
|
||||||
|
my $ret = TcpServer_Open($hash, $dev, $global);
|
||||||
|
if (!$ret) {
|
||||||
|
$hash->{STATE}="listening";
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
DevIo_CloseDev($hash);
|
DevIo_CloseDev($hash);
|
||||||
|
|
||||||
my $name = $a[0];
|
|
||||||
my $dev = $a[2];
|
|
||||||
|
|
||||||
if ( $dev eq "none" ) {
|
if ( $dev eq "none" ) {
|
||||||
Log (GetLogLevel($hash->{NAME}), "FRM device is none, commands will be echoed only");
|
Log (GetLogLevel($hash->{NAME}), "FRM device is none, commands will be echoed only");
|
||||||
$main::attr{$name}{dummy} = 1;
|
$main::attr{$name}{dummy} = 1;
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
$hash->{DeviceName} = $dev;
|
$hash->{DeviceName} = $dev;
|
||||||
|
|
||||||
my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit");
|
my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit");
|
||||||
readingsSingleUpdate($hash,"state","Initialized", 1) unless ($ret);
|
$hash->{STATE}="Initialized";
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +111,7 @@ sub FRM_Set($@) {
|
|||||||
} elsif ( $a[1] eq 'reinit' ) {
|
} elsif ( $a[1] eq 'reinit' ) {
|
||||||
FRM_forall_clients($hash,\&FRM_Init_Client,undef);
|
FRM_forall_clients($hash,\&FRM_Init_Client,undef);
|
||||||
} else {
|
} else {
|
||||||
return "Unknown argument $a[1], supported arguments are 'reset', 'reinit'";
|
return "Unknown argument $a[1], supported arguments are reset, reinit";
|
||||||
}
|
}
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@@ -124,8 +139,18 @@ sub FRM_Get($@) {
|
|||||||
|
|
||||||
#####################################
|
#####################################
|
||||||
# called from the global loop, when the select for hash->{FD} reports data
|
# called from the global loop, when the select for hash->{FD} reports data
|
||||||
|
|
||||||
sub FRM_Read($) {
|
sub FRM_Read($) {
|
||||||
|
|
||||||
my ( $hash ) = @_;
|
my ( $hash ) = @_;
|
||||||
|
if($hash->{SERVERSOCKET}) { # Accept and create a child
|
||||||
|
my $chash = TcpServer_Accept($hash, "FRM");
|
||||||
|
return if(!$chash);
|
||||||
|
$chash->{DeviceName}=$hash->{PORT}; # required for DevIo_CloseDev and FRM_Ready
|
||||||
|
$chash->{TCPDev}=$chash->{CD};
|
||||||
|
FRM_DoInit($chash);
|
||||||
|
return;
|
||||||
|
}
|
||||||
my $device = $hash->{FirmataDevice} or return;
|
my $device = $hash->{FirmataDevice} or return;
|
||||||
$device->poll();
|
$device->poll();
|
||||||
}
|
}
|
||||||
@@ -133,7 +158,18 @@ sub FRM_Read($) {
|
|||||||
sub FRM_Ready($) {
|
sub FRM_Ready($) {
|
||||||
|
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
return DevIo_OpenDev($hash, 1, "FRM_DoInit") if($hash->{READINGS}{state} eq "disconnected");
|
my $name = $hash->{NAME};
|
||||||
|
print "ready: $name\n";
|
||||||
|
if ($name=~/^^FRM:.+:\d+$/) { # this is a closed tcp-connection, remove it
|
||||||
|
TcpServer_Close($hash);
|
||||||
|
delete $main::defs{$hash->{SNAME}}{FirmataDevice} if (defined $hash->{SNAME} && defined $main::defs{$hash->{SNAME}}{FirmataDevice});
|
||||||
|
my $dev = $hash->{DeviceName};
|
||||||
|
delete $main::readyfnlist{"$name.$dev"};
|
||||||
|
delete $main::attr{$name};
|
||||||
|
delete $main::defs{$name};
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
return DevIo_OpenDev($hash, 1, "FRM_DoInit") if($hash->{STATE} eq "disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
sub FRM_Attr(@) {
|
sub FRM_Attr(@) {
|
||||||
@@ -187,7 +223,9 @@ sub FRM_DoInit($) {
|
|||||||
|
|
||||||
my ($hash) = @_;
|
my ($hash) = @_;
|
||||||
|
|
||||||
my $name = $hash->{NAME};
|
my $name = $hash->{SNAME}; #is this a serversocket-connection?
|
||||||
|
my $shash = defined $name ? $main::defs{$name} : $hash;
|
||||||
|
$name = $hash->{NAME} if (!defined $name);
|
||||||
|
|
||||||
my $firmata_io = Firmata_IO->new($hash);
|
my $firmata_io = Firmata_IO->new($hash);
|
||||||
my $device = Device::Firmata::Platform->attach($firmata_io) or return 1;
|
my $device = Device::Firmata::Platform->attach($firmata_io) or return 1;
|
||||||
@@ -197,72 +235,77 @@ sub FRM_DoInit($) {
|
|||||||
|
|
||||||
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
|
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
|
||||||
my $endTicks = time+5;
|
my $endTicks = time+5;
|
||||||
|
my $queryTicks = time+2;
|
||||||
$device->system_reset();
|
$device->system_reset();
|
||||||
do {
|
do {
|
||||||
Log (3, "querying Firmata Firmware Version");
|
FRM_poll($hash);
|
||||||
$device->firmware_version_query();
|
if ($device->{metadata}{firmware} && $device->{metadata}{firmware_version}) {
|
||||||
for (my $i=0;$i<50;$i++) {
|
|
||||||
if (FRM_poll($hash)) {
|
|
||||||
if ($device->{metadata}{firmware} && $device->{metadata}{firmware_version}){
|
|
||||||
$device->{protocol}->{protocol_version} = $device->{metadata}{firmware_version};
|
$device->{protocol}->{protocol_version} = $device->{metadata}{firmware_version};
|
||||||
$main::defs{$name}{firmware} = $device->{metadata}{firmware};
|
$main::defs{$name}{firmware} = $device->{metadata}{firmware};
|
||||||
$main::defs{$name}{firmware_version} = $device->{metadata}{firmware_version};
|
$main::defs{$name}{firmware_version} = $device->{metadata}{firmware_version};
|
||||||
Log (3, "Firmata Firmware Version: ".$device->{metadata}{firmware}." ".$device->{metadata}{firmware_version});
|
Log (3, "Firmata Firmware Version: ".$device->{metadata}{firmware}." ".$device->{metadata}{firmware_version});
|
||||||
$device->analog_mapping_query();
|
$device->analog_mapping_query();
|
||||||
$device->capability_query();
|
$device->capability_query();
|
||||||
for (my $j=0;$j<100;$j++) {
|
do {
|
||||||
if (FRM_poll($hash)) {
|
FRM_poll($hash);
|
||||||
if (($device->{metadata}{analog_mappings}) and ($device->{metadata}{capabilities})) {
|
if ($device->{metadata}{analog_mappings} and $device->{metadata}{capabilities}) {
|
||||||
my $inputpins = $device->{metadata}{input_pins};
|
my $inputpins = $device->{metadata}{input_pins};
|
||||||
$main::defs{$name}{input_pins} = join(",", sort{$a<=>$b}(@$inputpins));
|
$main::defs{$name}{input_pins} = join(",", sort{$a<=>$b}(@$inputpins)) if (defined $inputpins and scalar @$inputpins);
|
||||||
my $outputpins = $device->{metadata}{output_pins};
|
my $outputpins = $device->{metadata}{output_pins};
|
||||||
$main::defs{$name}{output_pins} = join(",", sort{$a<=>$b}(@$outputpins));
|
$main::defs{$name}{output_pins} = join(",", sort{$a<=>$b}(@$outputpins)) if (defined $outputpins and scalar @$outputpins);
|
||||||
my $analogpins = $device->{metadata}{analog_pins};
|
my $analogpins = $device->{metadata}{analog_pins};
|
||||||
$main::defs{$name}{analog_pins} = join(",", sort{$a<=>$b}(@$analogpins));
|
$main::defs{$name}{analog_pins} = join(",", sort{$a<=>$b}(@$analogpins)) if (defined $analogpins and scalar @$analogpins);
|
||||||
my $pwmpins = $device->{metadata}{pwm_pins};
|
my $pwmpins = $device->{metadata}{pwm_pins};
|
||||||
$main::defs{$name}{pwm_pins} = join(",", sort{$a<=>$b}(@$pwmpins));
|
$main::defs{$name}{pwm_pins} = join(",", sort{$a<=>$b}(@$pwmpins)) if (defined $pwmpins and scalar @$pwmpins);
|
||||||
my $servopins = $device->{metadata}{servo_pins};
|
my $servopins = $device->{metadata}{servo_pins};
|
||||||
$main::defs{$name}{servo_pins} = join(",", sort{$a<=>$b}(@$servopins));
|
$main::defs{$name}{servo_pins} = join(",", sort{$a<=>$b}(@$servopins)) if (defined $servopins and scalar @$servopins);
|
||||||
my $i2cpins = $device->{metadata}{i2c_pins};
|
my $i2cpins = $device->{metadata}{i2c_pins};
|
||||||
$main::defs{$name}{i2c_pins} = join(",", sort{$a<=>$b}(@$i2cpins));
|
$main::defs{$name}{i2c_pins} = join(",", sort{$a<=>$b}(@$i2cpins)) if (defined $i2cpins and scalar @$i2cpins);
|
||||||
my $onewirepins = $device->{metadata}{onewire_pins};
|
my $onewirepins = $device->{metadata}{onewire_pins};
|
||||||
$main::defs{$name}{onewire_pins} = join(",", sort{$a<=>$b}(@$onewirepins));
|
$main::defs{$name}{onewire_pins} = join(",", sort{$a<=>$b}(@$onewirepins)) if (defined $onewirepins and scalar @$onewirepins);
|
||||||
|
if (defined $device->{metadata}{analog_resolutions}) {
|
||||||
my @analog_resolutions;
|
my @analog_resolutions;
|
||||||
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{analog_resolutions}})) {
|
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{analog_resolutions}})) {
|
||||||
push @analog_resolutions,$pin.":".$device->{metadata}{analog_resolutions}{$pin};
|
push @analog_resolutions,$pin.":".$device->{metadata}{analog_resolutions}{$pin};
|
||||||
}
|
}
|
||||||
$main::defs{$name}{analog_resolutions} = join(",",@analog_resolutions);
|
$main::defs{$name}{analog_resolutions} = join(",",@analog_resolutions) if (scalar @analog_resolutions);
|
||||||
|
}
|
||||||
|
if (defined $device->{metadata}{pwm_resolutions}) {
|
||||||
my @pwm_resolutions;
|
my @pwm_resolutions;
|
||||||
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{pwm_resolutions}})) {
|
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{pwm_resolutions}})) {
|
||||||
push @pwm_resolutions,$pin.":".$device->{metadata}{pwm_resolutions}{$pin};
|
push @pwm_resolutions,$pin.":".$device->{metadata}{pwm_resolutions}{$pin};
|
||||||
}
|
}
|
||||||
$main::defs{$name}{pwm_resolutions} = join(",",@pwm_resolutions);
|
$main::defs{$name}{pwm_resolutions} = join(",",@pwm_resolutions) if (scalar @pwm_resolutions);
|
||||||
|
}
|
||||||
|
if (defined $device->{metadata}{servo_resolutions}) {
|
||||||
my @servo_resolutions;
|
my @servo_resolutions;
|
||||||
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{servo_resolutions}})) {
|
foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{servo_resolutions}})) {
|
||||||
push @servo_resolutions,$pin.":".$device->{metadata}{servo_resolutions}{$pin};
|
push @servo_resolutions,$pin.":".$device->{metadata}{servo_resolutions}{$pin};
|
||||||
}
|
}
|
||||||
$main::defs{$name}{servo_resolutions} = join(",",@servo_resolutions);
|
$main::defs{$name}{servo_resolutions} = join(",",@servo_resolutions) if (scalar @servo_resolutions);
|
||||||
$found = 1;
|
|
||||||
last;
|
|
||||||
}
|
}
|
||||||
|
$found = 1;
|
||||||
} else {
|
} else {
|
||||||
select (undef,undef,undef,0.01);
|
select (undef,undef,undef,0.01);
|
||||||
}
|
}
|
||||||
}
|
} while (time < $endTicks and !$found);
|
||||||
$found = 1;
|
$found = 1;
|
||||||
last;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
select (undef,undef,undef,0.01);
|
select (undef,undef,undef,0.01);
|
||||||
|
if (time > $queryTicks) {
|
||||||
|
Log (3, "querying Firmata Firmware Version");
|
||||||
|
$device->firmware_version_query();
|
||||||
|
$queryTicks++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} while (time < $endTicks and !$found);
|
||||||
if ($found) {
|
if ($found) {
|
||||||
FRM_apply_attribute($hash,"sampling-interval");
|
$shash->{FirmataDevice} = $device;
|
||||||
FRM_apply_attribute($hash,"i2c-config");
|
FRM_apply_attribute($shash,"sampling-interval");
|
||||||
FRM_forall_clients($hash,\&FRM_Init_Client,undef);
|
FRM_apply_attribute($shash,"i2c-config");
|
||||||
|
FRM_forall_clients($shash,\&FRM_Init_Client,undef);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
} while (time < $endTicks);
|
|
||||||
Log (3, "no response from Firmata, closing DevIO");
|
Log (3, "no response from Firmata, closing DevIO");
|
||||||
DevIo_Disconnected($hash);
|
DevIo_Disconnected($hash);
|
||||||
delete $hash->{FirmataDevice};
|
delete $hash->{FirmataDevice};
|
||||||
@@ -286,6 +329,10 @@ FRM_forall_clients($$$)
|
|||||||
sub
|
sub
|
||||||
FRM_Init_Client($$) {
|
FRM_Init_Client($$) {
|
||||||
my ($hash,$args) = @_;
|
my ($hash,$args) = @_;
|
||||||
|
if (!defined $args and defined $hash->{DEF}) {
|
||||||
|
my @a = split("[ \t][ \t]*", $hash->{DEF});
|
||||||
|
$args = \@a;
|
||||||
|
}
|
||||||
my $ret = CallFn($hash->{NAME},"InitFn",$hash,$args);
|
my $ret = CallFn($hash->{NAME},"InitFn",$hash,$args);
|
||||||
if ($ret) {
|
if ($ret) {
|
||||||
Log (GetLogLevel($hash->{NAME},2),"error initializing ".$hash->{NAME}.": ".$ret);
|
Log (GetLogLevel($hash->{NAME},2),"error initializing ".$hash->{NAME}.": ".$ret);
|
||||||
@@ -296,8 +343,8 @@ sub
|
|||||||
FRM_Init_Pin_Client($$$) {
|
FRM_Init_Pin_Client($$$) {
|
||||||
my ($hash,$args,$mode) = @_;
|
my ($hash,$args,$mode) = @_;
|
||||||
my $u = "wrong syntax: define <name> FRM_XXX pin";
|
my $u = "wrong syntax: define <name> FRM_XXX pin";
|
||||||
return $u unless defined $args and int(@$args) > 2;
|
return $u unless defined $args and int(@$args) > 0;
|
||||||
my $pin = @$args[2];
|
my $pin = @$args[0];
|
||||||
$hash->{PIN} = $pin;
|
$hash->{PIN} = $pin;
|
||||||
if (defined $hash->{IODev} and defined $hash->{IODev}->{FirmataDevice}) {
|
if (defined $hash->{IODev} and defined $hash->{IODev}->{FirmataDevice}) {
|
||||||
eval {
|
eval {
|
||||||
@@ -316,10 +363,10 @@ FRM_Client_Define($$)
|
|||||||
my ($hash, $def) = @_;
|
my ($hash, $def) = @_;
|
||||||
my @a = split("[ \t][ \t]*", $def);
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
|
||||||
readingsSingleUpdate($hash,"state","defined",0);
|
$hash->{STATE}="defined";
|
||||||
|
|
||||||
AssignIoPort($hash);
|
AssignIoPort($hash);
|
||||||
FRM_Init_Client($hash,\@a);
|
FRM_Init_Client($hash,[@a[2..scalar(@a)-1]]);
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@@ -335,7 +382,7 @@ FRM_Client_Unassign($)
|
|||||||
{
|
{
|
||||||
my ($dev) = @_;
|
my ($dev) = @_;
|
||||||
delete $dev->{IODev} if defined $dev->{IODev};
|
delete $dev->{IODev} if defined $dev->{IODev};
|
||||||
readingsSingleUpdate($dev,"state","defined",0);
|
$dev->{STATE}="defined";
|
||||||
}
|
}
|
||||||
|
|
||||||
package Firmata_IO;
|
package Firmata_IO;
|
||||||
@@ -381,7 +428,7 @@ sub FRM_i2c_update_device
|
|||||||
my @values = split(" ",ReadingsVal($hash->{NAME},"values",""));
|
my @values = split(" ",ReadingsVal($hash->{NAME},"values",""));
|
||||||
splice(@values,$data->{register},@$replydata, @$replydata);
|
splice(@values,$data->{register},@$replydata, @$replydata);
|
||||||
readingsBeginUpdate($hash);
|
readingsBeginUpdate($hash);
|
||||||
readingsBulkUpdate($hash,"state","active",0);
|
$hash->{STATE}="active";
|
||||||
readingsBulkUpdate($hash,"values",join (" ",@values),1);
|
readingsBulkUpdate($hash,"values",join (" ",@values),1);
|
||||||
readingsEndUpdate($hash,undef);
|
readingsEndUpdate($hash,undef);
|
||||||
}
|
}
|
||||||
@@ -423,7 +470,7 @@ FRM_OWX_Init($$)
|
|||||||
if ( AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
|
if ( AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) {
|
||||||
$firmata->onewire_config($pin,1);
|
$firmata->onewire_config($pin,1);
|
||||||
}
|
}
|
||||||
readingsSingleUpdate($hash,"state","Initialized",1);
|
$hash->{STATE}="Initialized";
|
||||||
$firmata->onewire_search($pin);
|
$firmata->onewire_search($pin);
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@@ -654,7 +701,7 @@ sub FRM_OWX_Discover ($) {
|
|||||||
<a name="FRMdefine"></a>
|
<a name="FRMdefine"></a>
|
||||||
<b>Define</b>
|
<b>Define</b>
|
||||||
<ul>
|
<ul>
|
||||||
<code>define <name> FRM <device></code> <br>
|
<code>define <name> FRM {<device> | <port> [global]}</code> <br>
|
||||||
Specifies the FRM device. </ul>
|
Specifies the FRM device. </ul>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@@ -683,9 +730,18 @@ sub FRM_OWX_Discover ($) {
|
|||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
Network-connected devices:<br><ul>
|
Network-connected devices:<br><ul>
|
||||||
<device> specifies the host:port of the device. E.g.
|
<port> specifies the port the FRM device listens on. If 'global' is
|
||||||
192.168.0.244:2323<br>
|
specified the socket is bound to all local ip-addresses, otherwise to localhost
|
||||||
As of now EthernetFirmata is still eperimental.
|
only.<br>
|
||||||
|
The Arduino must run a Version of firmata that connects in client-mode (the
|
||||||
|
connection is initiated by the arduino). The ip-address and port of the fhem-server
|
||||||
|
has to be configured an the arduino, so it knows where to connect to.<br>
|
||||||
|
As of now only a single Arduino per FRM-device configured is supported. Multiple
|
||||||
|
Arduinos may connect to different FRM-devices configured for different ports.<br>
|
||||||
|
The support for Firmata over ethernet is still experimental. Firmata-ethenet-client
|
||||||
|
can be found here:
|
||||||
|
<a href="https://github.com/ntruchsess/arduino/blob/configurable_ethernet/examples/ConfigurableEthernetclient/ConfigurableEthernetclient.ino">
|
||||||
|
ConfigurableEthernetclient.ino</a><br>
|
||||||
</ul>
|
</ul>
|
||||||
<br>
|
<br>
|
||||||
If the device is called none, then no device will be opened, so you
|
If the device is called none, then no device will be opened, so you
|
||||||
|
|||||||
Reference in New Issue
Block a user