diff --git a/fhem/FHEM/00_OWX.pm b/fhem/FHEM/00_OWX.pm index 9d3eb216a..b42db94f5 100644 --- a/fhem/FHEM/00_OWX.pm +++ b/fhem/FHEM/00_OWX.pm @@ -193,8 +193,8 @@ sub OWX_Define ($$) { $hash->{HWDEVICE} = $owx_hwdevice; #-- check if we are connecting to Arduino (via FRM): } elsif ($dev =~ /^\d{1,2}$/) { + $hash->{INTERFACE} = "firmata"; if (defined $main::modules{FRM}) { - $hash->{INTERFACE} = "firmata"; FRM_Client_Define($hash,$def); } else { Log 1,"module FRM not yet loaded, please define an FRM device first."; diff --git a/fhem/FHEM/10_FRM.pm b/fhem/FHEM/10_FRM.pm index 4a51bd8d8..6396be554 100755 --- a/fhem/FHEM/10_FRM.pm +++ b/fhem/FHEM/10_FRM.pm @@ -20,17 +20,15 @@ use Device::Firmata::Platform; sub FRM_Set($@); sub FRM_Attr(@); -sub Log($$); ##################################### sub FRM_Initialize($) { - my ($hash) = @_; + my $hash = shift @_; require "$main::attr{global}{modpath}/FHEM/DevIo.pm"; # Provider - $hash->{Clients} = - ":FRM_IN:FRM_OUT:FRM_AD:FRM_PWM:FRM_I2C:FRM_SERVO:OWX:"; + $hash->{Clients} = ":FRM_IN:FRM_OUT:FRM_AD:FRM_PWM:FRM_I2C:FRM_SERVO:OWX:"; $hash->{ReadyFn} = "FRM_Ready"; $hash->{ReadFn} = "FRM_Read"; @@ -41,7 +39,7 @@ sub FRM_Initialize($) { $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"; + $hash->{AttrList} = "model:nano dummy:1,0 loglevel:0,1,2,3,4,5,6 sampling-interval i2c-config $main::readingFnAttributes"; } ##################################### @@ -56,13 +54,13 @@ sub FRM_Define($$) { my $dev = $a[2]; if ( $dev eq "none" ) { - Log 1, "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; return undef; } $hash->{DeviceName} = $dev; my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit"); - main::readingsSingleUpdate($hash,"state","Initialized", 1); + readingsSingleUpdate($hash,"state","Initialized", 1) unless ($ret); return $ret; } @@ -70,7 +68,7 @@ sub FRM_Define($$) { sub FRM_Undef($) { my $hash = @_; FRM_forall_clients($hash,\&FRM_Client_Unassign,undef); - DevIo_CloseDev($hash); + DevIo_Disconnected($hash); my $device = $hash->{FirmataDevice}; if (defined $device) { if (defined $device->{io}) { @@ -143,7 +141,8 @@ sub FRM_Attr(@) { if ($command eq "set") { $main::attr{$name}{$attribute}=$value; if ($attribute eq "sampling-interval" - or $attribute eq "i2c-config" ) { + or $attribute eq "i2c-config" + or $attribute eq "loglevel" ) { FRM_apply_attribute($main::defs{$name},$attribute); } } @@ -155,21 +154,30 @@ sub FRM_apply_attribute { my $name = $hash->{NAME}; if (defined $firmata) { if ($attribute eq "sampling-interval") { - $firmata->sampling_interval(main::AttrVal($name,$attribute,"1000")); + $firmata->sampling_interval(AttrVal($name,$attribute,"1000")); } elsif ($attribute eq "i2c-config") { - my $i2cattr = main::AttrVal($name,$attribute,undef); + my $i2cattr = AttrVal($name,$attribute,undef); if (defined $i2cattr) { my @a = split(" ", $i2cattr); - my $i2cpins = $firmata->{metadata}{i2c_pins}; + my $i2cpins = $firmata->{metadata}{i2c_pins}; + my $err; 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); + eval { + foreach my $i2cpin (@$i2cpins) { + $firmata->pin_mode($i2cpin,PIN_I2C); + } + $firmata->i2c_config(@a); + $firmata->observe_i2c(\&FRM_i2c_observer,$hash); + }; + $err = $@ if ($@); } else { - Log 1,"Error, arduino doesn't support I2C"; + $err = "Error, arduino doesn't support I2C"; } + Log (GetLogLevel($hash->{NAME},2),$err) if ($err); + } + } elsif ($attribute eq "loglevel") { + if (defined $firmata->{io}) { + $firmata->{io}->{loglevel} = AttrVal($name,$attribute,5); } } } @@ -180,7 +188,6 @@ 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; @@ -189,8 +196,10 @@ sub FRM_DoInit($) { $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 + my $endTicks = time+5; + $device->system_reset(); do { - $device->system_reset(); + Log (3, "querying Firmata Firmware Version"); $device->firmware_version_query(); for (my $i=0;$i<50;$i++) { if (FRM_poll($hash)) { @@ -198,6 +207,7 @@ sub FRM_DoInit($) { $device->{protocol}->{protocol_version} = $device->{metadata}{firmware_version}; $main::defs{$name}{firmware} = $device->{metadata}{firmware}; $main::defs{$name}{firmware_version} = $device->{metadata}{firmware_version}; + Log (3, "Firmata Firmware Version: ".$device->{metadata}{firmware}." ".$device->{metadata}{firmware_version}); $device->analog_mapping_query(); $device->capability_query(); for (my $j=0;$j<100;$j++) { @@ -236,23 +246,27 @@ sub FRM_DoInit($) { last; } } else { - select (undef,undef,undef,0.1); + select (undef,undef,undef,0.01); } } $found = 1; last; } } else { - select (undef,undef,undef,0.1); + select (undef,undef,undef,0.01); } } - } while (!$found); - - FRM_apply_attribute($hash,"sampling-interval"); - FRM_apply_attribute($hash,"i2c-config"); - FRM_forall_clients($hash,\&FRM_Init_Client,undef); - - return undef; + if ($found) { + FRM_apply_attribute($hash,"sampling-interval"); + FRM_apply_attribute($hash,"i2c-config"); + FRM_forall_clients($hash,\&FRM_Init_Client,undef); + return undef; + } + } while (time < $endTicks); + Log (3, "no response from Firmata, closing DevIO"); + DevIo_Disconnected($hash); + delete $hash->{FirmataDevice}; + return "FirmataDevice not responding"; } sub @@ -272,22 +286,28 @@ FRM_forall_clients($$$) sub FRM_Init_Client($$) { my ($hash,$args) = @_; - $hash->{loglevel} = main::GetLogLevel($hash->{NAME}); - main::CallFn($hash->{NAME},"InitFn",$hash,$args); + my $ret = CallFn($hash->{NAME},"InitFn",$hash,$args); + if ($ret) { + Log (GetLogLevel($hash->{NAME},2),"error initializing ".$hash->{NAME}.": ".$ret); + } } sub FRM_Init_Pin_Client($$$) { my ($hash,$args,$mode) = @_; my $u = "wrong syntax: define FRM_XXX pin"; - return $u if(int(@$args) < 3); + return $u unless defined $args and int(@$args) > 2; my $pin = @$args[2]; $hash->{PIN} = $pin; if (defined $hash->{IODev} and defined $hash->{IODev}->{FirmataDevice}) { - $hash->{IODev}->{FirmataDevice}->pin_mode($pin,$mode); - return 1; + eval { + $hash->{IODev}->{FirmataDevice}->pin_mode($pin,$mode); + }; + return "error setting Firmata pin_mode for ".$hash->{NAME}.": ".$@ if ($@); + return undef; } - return undef; + return "no IODev set" unless defined $hash->{IODev}; + return "no FirmataDevice assigned to ".$hash->{IODev}->{NAME}; } sub @@ -296,9 +316,9 @@ FRM_Client_Define($$) my ($hash, $def) = @_; my @a = split("[ \t][ \t]*", $def); - main::readingsSingleUpdate($hash,"state","defined",0); + readingsSingleUpdate($hash,"state","defined",0); - main::AssignIoPort($hash); + AssignIoPort($hash); FRM_Init_Client($hash,\@a); return undef; @@ -315,7 +335,7 @@ FRM_Client_Unassign($) { my ($dev) = @_; delete $dev->{IODev} if defined $dev->{IODev}; - main::readingsSingleUpdate($dev,"state","defined",0); + readingsSingleUpdate($dev,"state","defined",0); } package Firmata_IO { @@ -324,12 +344,13 @@ package Firmata_IO { my ($class,$hash) = @_; return bless { hash => $hash, + loglevel => main::GetLogLevel($hash->{NAME},5), }, $class; } sub data_write { my ( $self, $buf ) = @_; - main::Log 5, ">".join(",",map{sprintf"%02x",ord$_}split//,$buf); + main::Log ($self->{loglevel}, ">".join(",",map{sprintf"%02x",ord$_}split//,$buf)); main::DevIo_SimpleWrite($self->{hash},$buf,undef); } @@ -337,7 +358,7 @@ package Firmata_IO { my ( $self, $bytes ) = @_; my $string = main::DevIo_SimpleRead($self->{hash}); if (defined $string ) { - main::Log 5,"<".join(",",map{sprintf"%02x",ord$_}split//,$string); + main::Log ($self->{loglevel},"<".join(",",map{sprintf"%02x",ord$_}split//,$string)); } return $string; } @@ -347,7 +368,7 @@ sub FRM_i2c_observer { my ($data,$hash) = @_; - main::Log 5,"onI2CMessage address: '".$data->{address}."', register: '".$data->{register}."' data: '".$data->{data}."'"; + Log GetLogLevel($hash->{NAME},5),"onI2CMessage address: '".$data->{address}."', register: '".$data->{register}."' data: '".$data->{data}."'"; FRM_forall_clients($hash,\&FRM_i2c_update_device,$data); } @@ -356,20 +377,20 @@ 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","")); + my @values = split(" ",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); + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"state","active",0); + readingsBulkUpdate($hash,"values",join (" ",@values),1); + readingsEndUpdate($hash,undef); } } sub FRM_string_observer { my ($string,$hash) = @_; - main::Log 4, "received String_data: ".$string; - main::readingsSingleUpdate($hash,"error",$string,1); + Log (GetLogLevel($hash->{NAME},3), "received String_data: ".$string); + readingsSingleUpdate($hash,"error",$string,1); } sub FRM_poll @@ -391,21 +412,19 @@ sub FRM_OWX_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_ONEWIRE)) { - $hash->{INTERFACE} = "firmata"; - my $firmata = $hash->{IODev}->{FirmataDevice}; - my $pin = $hash->{PIN}; - $firmata->observe_onewire($pin,\&FRM_OWX_observer,$hash); - $hash->{FRM_OWX_REPLIES} = {}; - $hash->{DEVS} = []; - if ( main::AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) { - $firmata->onewire_config($pin,1); - } - main::readingsSingleUpdate($hash,"state","Initialized",1); - $firmata->onewire_search($pin); - return undef; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_ONEWIRE); + return $ret if (defined $ret); + my $firmata = $hash->{IODev}->{FirmataDevice}; + my $pin = $hash->{PIN}; + $firmata->observe_onewire($pin,\&FRM_OWX_observer,$hash); + $hash->{FRM_OWX_REPLIES} = {}; + $hash->{DEVS} = []; + if ( AttrVal($hash->{NAME},"buspower","") eq "parasitic" ) { + $firmata->onewire_config($pin,1); } - return 1; + readingsSingleUpdate($hash,"state","Initialized",1); + $firmata->onewire_search($pin); + return undef; } sub FRM_OWX_observer @@ -426,7 +445,7 @@ sub FRM_OWX_observer } if ($command eq "SEARCH_REPLY") { $hash->{DEVS} = \@owx_devices; - $main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices; + #$main::attr{$hash->{NAME}}{"ow-devices"} = join " ",@owx_devices; } else { $hash->{ALARMDEVS} = \@owx_devices; } @@ -512,10 +531,10 @@ sub FRM_OWX_Complex ($$$$) { #-- get the interface my $frm = $hash->{IODev}; - return undef unless defined $frm; + return 0 unless defined $frm; my $firmata = $frm->{FirmataDevice}; my $pin = $hash->{PIN}; - return undef unless ( defined $firmata and defined $pin ); + return 0 unless ( defined $firmata and defined $pin ); my $ow_command = {}; @@ -546,7 +565,7 @@ sub FRM_OWX_Complex ($$$$) { $firmata->onewire_command_series( $pin, $ow_command ); if ($numread) { - my $times = main::AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec + my $times = AttrVal($hash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec for (my $i=0;$i<$times;$i++) { if (FRM_poll($hash->{IODev})) { if (defined $hash->{FRM_OWX_REPLIES}->{$owx_dev}) { diff --git a/fhem/FHEM/20_FRM_AD.pm b/fhem/FHEM/20_FRM_AD.pm index 4bdc51f6a..e2d93e88a 100755 --- a/fhem/FHEM/20_FRM_AD.pm +++ b/fhem/FHEM/20_FRM_AD.pm @@ -25,24 +25,23 @@ FRM_AD_Initialize($) $hash->{InitFn} = "FRM_AD_Init"; $hash->{UndefFn} = "FRM_AD_Undef"; - $hash->{AttrList} = "IODev upper-threshold lower-threshold loglevel:0,1,2,3,4,5 $main::readingFnAttributes"; + $hash->{AttrList} = "IODev upper-threshold lower-threshold loglevel:0,1,2,3,4,5,6 $main::readingFnAttributes"; } sub FRM_AD_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_ANALOG)) { - my $firmata = $hash->{IODev}->{FirmataDevice}; - $firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash); - $main::defs{$hash->{NAME}}{resolution}=$firmata->{metadata}{analog_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{analog_resolutions}); - if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { - $main::attr{$hash->{NAME}}{"stateFormat"} = "reading"; - } - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_ANALOG); + return $ret if (defined $ret); + my $firmata = $hash->{IODev}->{FirmataDevice}; + $firmata->observe_analog($hash->{PIN},\&FRM_AD_observer,$hash); + $main::defs{$hash->{NAME}}{resolution}=$firmata->{metadata}{analog_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{analog_resolutions}); + if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { + $main::attr{$hash->{NAME}}{"stateFormat"} = "reading"; } - return 1; + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } sub diff --git a/fhem/FHEM/20_FRM_I2C.pm b/fhem/FHEM/20_FRM_I2C.pm index c15581543..d784d4805 100755 --- a/fhem/FHEM/20_FRM_I2C.pm +++ b/fhem/FHEM/20_FRM_I2C.pm @@ -31,15 +31,15 @@ FRM_I2C_Init($) $hash->{"i2c-address"} = @$args[2]; $hash->{"i2c-register"} = @$args[3]; $hash->{"i2c-bytestoread"} = @$args[4]; + + return "no IODev set" unless defined $hash->{IODev}; + return "no FirmataDevice assigned to ".$hash->{IODev}->{NAME} unless defined $hash->{IODev}->{FirmataDevice}; - 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; + eval { + $hash->{IODev}->{FirmataDevice}->i2c_read(@$args[2],@$args[3],@$args[4]); + }; + return "error calling i2c_read: ".$@ if ($@); + return undef; } sub FRM_I2C_Attr(@) { diff --git a/fhem/FHEM/20_FRM_IN.pm b/fhem/FHEM/20_FRM_IN.pm index a765f7404..c651ffcc5 100755 --- a/fhem/FHEM/20_FRM_IN.pm +++ b/fhem/FHEM/20_FRM_IN.pm @@ -37,17 +37,15 @@ sub FRM_IN_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_INPUT)) { - my $firmata = $hash->{IODev}->{FirmataDevice}; - $firmata->observe_digital($hash->{PIN},\&FRM_IN_observer,$hash); - if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { - $main::attr{$hash->{NAME}}{"stateFormat"} = "reading"; - } - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_INPUT); + return $ret if (defined $ret); + my $firmata = $hash->{IODev}->{FirmataDevice}; + $firmata->observe_digital($hash->{PIN},\&FRM_IN_observer,$hash); + if (! (defined AttrVal($hash->{NAME},"stateFormat",undef))) { + $main::attr{$hash->{NAME}}{"stateFormat"} = "reading"; } - return 1; - + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } sub diff --git a/fhem/FHEM/20_FRM_OUT.pm b/fhem/FHEM/20_FRM_OUT.pm index 146ef4e19..da9d8cc57 100755 --- a/fhem/FHEM/20_FRM_OUT.pm +++ b/fhem/FHEM/20_FRM_OUT.pm @@ -25,11 +25,10 @@ sub FRM_OUT_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_OUTPUT)) { - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; - } - return 1; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_OUTPUT); + return $ret if (defined $ret); + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } sub diff --git a/fhem/FHEM/20_FRM_PWM.pm b/fhem/FHEM/20_FRM_PWM.pm index 7d6df485a..9394494a7 100755 --- a/fhem/FHEM/20_FRM_PWM.pm +++ b/fhem/FHEM/20_FRM_PWM.pm @@ -29,13 +29,12 @@ sub FRM_PWM_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_PWM)) { - my $firmata = $hash->{IODev}->{FirmataDevice}; - $main::defs{$hash->{NAME}}{resolution}=$firmata->{metadata}{pwm_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{pwm_resolutions}); - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; - } - return 1; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_PWM); + return $ret if (defined $ret); + my $firmata = $hash->{IODev}->{FirmataDevice}; + $main::defs{$hash->{NAME}}{resolution}=$firmata->{metadata}{pwm_resolutions}{$hash->{PIN}} if (defined $firmata->{metadata}{pwm_resolutions}); + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } sub diff --git a/fhem/FHEM/20_FRM_SERVO.pm b/fhem/FHEM/20_FRM_SERVO.pm index 18a8663f1..309ad8f55 100755 --- a/fhem/FHEM/20_FRM_SERVO.pm +++ b/fhem/FHEM/20_FRM_SERVO.pm @@ -30,14 +30,13 @@ sub FRM_SERVO_Init($$) { my ($hash,$args) = @_; - if (FRM_Init_Pin_Client($hash,$args,PIN_SERVO)) { - my $firmata = $hash->{IODev}->{FirmataDevice}; - $main::defs{$hash->{NAME}}{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 - main::readingsSingleUpdate($hash,"state","Initialized",1); - return undef; - } - return 1; + my $ret = FRM_Init_Pin_Client($hash,$args,PIN_SERVO); + return $ret if (defined $ret); + my $firmata = $hash->{IODev}->{FirmataDevice}; + $main::defs{$hash->{NAME}}{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 + main::readingsSingleUpdate($hash,"state","Initialized",1); + return undef; } sub FRM_SERVO_Attr(@) {