10_FRM.pm 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228
  1. ########################################################################################
  2. #
  3. # $Id: 10_FRM.pm 15941 2018-01-20 21:20:20Z jensb $
  4. #
  5. # FHEM module to communicate with Firmata devices
  6. #
  7. ########################################################################################
  8. #
  9. # LICENSE AND COPYRIGHT
  10. #
  11. # Copyright (C) 2013 ntruchess
  12. # Copyright (C) 2015 jensb
  13. #
  14. # All rights reserved
  15. #
  16. # This script is free software; you can redistribute it and/or modify
  17. # it under the terms of the GNU General Public License as published by
  18. # the Free Software Foundation; either version 2 of the License, or
  19. # (at your option) any later version.
  20. #
  21. # The GNU General Public License can be found at
  22. # http://www.gnu.org/copyleft/gpl.html.
  23. # A copy is found in the textfile GPL.txt and important notices to the license
  24. # from the author is found in LICENSE.txt distributed with these scripts.
  25. #
  26. # This script is distributed in the hope that it will be useful,
  27. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. # GNU General Public License for more details.
  30. #
  31. # This copyright notice MUST APPEAR in all copies of the script!
  32. #
  33. ########################################################################################
  34. package main;
  35. use vars qw{%attr %defs};
  36. use strict;
  37. use warnings;
  38. use GPUtils qw(:all);
  39. #add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
  40. BEGIN {
  41. if (!grep(/FHEM\/lib$/,@INC)) {
  42. foreach my $inc (grep(/FHEM$/,@INC)) {
  43. push @INC,$inc."/lib";
  44. };
  45. };
  46. };
  47. use Device::Firmata;
  48. use Device::Firmata::Constants qw/ :all /;
  49. use Device::Firmata::Protocol;
  50. use Device::Firmata::Platform;
  51. sub FRM_Set($@);
  52. sub FRM_Attr(@);
  53. my %sets = (
  54. "reset" => "",
  55. "reinit" => ""
  56. );
  57. my %gets = (
  58. "firmware" => "",
  59. "version" => ""
  60. );
  61. my @clients = qw(
  62. FRM_IN
  63. FRM_OUT
  64. FRM_AD
  65. FRM_PWM
  66. FRM_I2C
  67. FRM_SERVO
  68. FRM_RGB
  69. FRM_ROTENC
  70. FRM_STEPPER
  71. OWX
  72. OWX_ASYNC
  73. I2C_LCD
  74. I2C_DS1307
  75. I2C_PC.*
  76. I2C_MCP23.*
  77. I2C_SHT.*
  78. I2C_BME280
  79. I2C_BMP180
  80. I2C_BH1750
  81. I2C_TSL2561
  82. FRM_LCD
  83. I2C_K30
  84. I2C_LM.*
  85. );
  86. =item FRM_Initialize($)
  87. Returns:
  88. nothing
  89. Description:
  90. FHEM module initialization function ($hash is FRM)
  91. =cut
  92. sub FRM_Initialize($) {
  93. my $hash = shift @_;
  94. require "$main::attr{global}{modpath}/FHEM/DevIo.pm";
  95. # Provider
  96. $hash->{Clients} = join (':',@clients);
  97. $hash->{ReadyFn} = "FRM_Ready";
  98. $hash->{ReadFn} = "FRM_Read";
  99. $hash->{I2CWrtFn} = "FRM_I2C_Write";
  100. $hash->{IOOpenFn} = "FRM_Serial_Open";
  101. $hash->{IOWriteFn} = "FRM_Serial_Write";
  102. $hash->{IOCloseFn} = "FRM_Serial_Close";
  103. # Consumer
  104. $hash->{DefFn} = "FRM_Define";
  105. $hash->{UndefFn} = "FRM_Undef";
  106. $hash->{GetFn} = "FRM_Get";
  107. $hash->{SetFn} = "FRM_Set";
  108. $hash->{AttrFn} = "FRM_Attr";
  109. $hash->{NotifyFn} = "FRM_Notify";
  110. $hash->{AttrList} = "model:nano dummy:1,0 sampling-interval i2c-config resetDeviceOnConnect:0,1 software-serial-config errorExclude disable:0,1 $main::readingFnAttributes";
  111. }
  112. =item FRM_Define($$)
  113. Returns:
  114. undef on success or error message
  115. Description:
  116. FHEM module DefFn ($hash is FRM)
  117. =cut
  118. sub FRM_Define($$) {
  119. my ( $hash, $def ) = @_;
  120. my ($name, $type, $dev, $global) = split("[ \t]+", $def);
  121. $hash->{DeviceName} = $dev;
  122. $hash->{NOTIFYDEV} = "global";
  123. if ( $dev eq "none" ) {
  124. Log3 $name,3,"device is none, commands will be echoed only";
  125. $main::attr{$name}{dummy} = 1;
  126. }
  127. if ($main::init_done && !AttrVal($name,'disable',0)) {
  128. return FRM_Start($hash);
  129. }
  130. readingsSingleUpdate($hash, 'state', 'defined', 1);
  131. return undef;
  132. }
  133. =item FRM_Undef($)
  134. Returns:
  135. undef
  136. Description:
  137. FHEM module UndefFn ($hash is FRM)
  138. =cut
  139. sub FRM_Undef($) {
  140. my $hash = shift;
  141. FRM_forall_clients($hash,\&FRM_Client_Unassign,undef);
  142. if (defined $hash->{DeviceName}) {
  143. DevIo_Disconnected($hash);
  144. };
  145. TcpServer_Close($hash);
  146. foreach my $d ( sort keys %main::defs ) { # close and dispose open tcp-connection (if any) to free open filedescriptors
  147. if ( defined( my $dev = $main::defs{$d} )) {
  148. if ( defined( $main::defs{$d}{SNAME} )
  149. && $main::defs{$d}{SNAME} eq $hash->{NAME}) {
  150. FRM_Tcp_Connection_Close($main::defs{$d});
  151. }
  152. }
  153. }
  154. FRM_FirmataDevice_Close($hash);
  155. FRM_ClearConfiguration($hash);
  156. return undef;
  157. }
  158. =item FRM_Start
  159. Returns:
  160. undef on success or error messages
  161. Description:
  162. FRM internal function ($hash is FRM)
  163. =cut
  164. sub FRM_Start {
  165. my ($hash) = @_;
  166. my $name = $hash->{NAME};
  167. my ($dev, $global) = split("[ \t]+", $hash->{DEF});
  168. $hash->{DeviceName} = $dev;
  169. my $isServer = 1 if($dev && $dev =~ m/^(IPV6:)?\d+$/);
  170. # my $isClient = 1 if($dev && $dev =~ m/^(IPV6:)?.*:\d+$/);
  171. # return "Usage: define <name> FRM {<device>[@<baudrate>] | [IPV6:]<tcp-portnr> [global]}"
  172. # if(!($isServer || $isClient) ||
  173. # ($isClient && $global) ||
  174. # ($global && $global ne "global"));
  175. # clear old device ids to force full init
  176. FRM_ClearConfiguration($hash);
  177. # show version of perl-firmata driver
  178. $main::defs{$name}{DRIVER_VERSION} = $Device::Firmata::VERSION;
  179. # make sure that fhem only runs once
  180. if($isServer) {
  181. # set initial state
  182. readingsSingleUpdate($hash, 'state', 'defined', 1);
  183. # start TCP server
  184. my $ret = TcpServer_Open($hash, $dev, $global);
  185. if (!$ret) {
  186. readingsSingleUpdate($hash, 'state', 'listening', 1);
  187. }
  188. return $ret;
  189. }
  190. # close old DevIO (if any)
  191. DevIo_CloseDev($hash);
  192. readingsSingleUpdate($hash, 'state', 'defined', 1);
  193. # open DevIO
  194. my $ret = DevIo_OpenDev($hash, 0, "FRM_DoInit");
  195. return $ret;
  196. }
  197. =item FRM_Notify
  198. Returns:
  199. nothing
  200. Description:
  201. FHEM module NotifyFn ($hash is FRM)
  202. =cut
  203. sub FRM_Notify {
  204. my ($hash,$dev) = @_;
  205. my $name = $hash->{NAME};
  206. my $type = $hash->{TYPE};
  207. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) && !AttrVal($name,'disable',0) ) {
  208. FRM_Start($hash);
  209. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  210. }
  211. }
  212. =item FRM_is_firmata_connected
  213. Returns:
  214. true if Firmata device and Firmata device IO are defined
  215. Description:
  216. FRM internal utility function($hash is FRM)
  217. =cut
  218. sub FRM_is_firmata_connected {
  219. my ($hash) = @_;
  220. return defined($hash->{FirmataDevice}) && defined ($hash->{FirmataDevice}->{io});
  221. }
  222. =item FRM_Set($@)
  223. Returns:
  224. undef or error message
  225. Description:
  226. FHEM module SetFn ($hash is FRM)
  227. =cut
  228. sub FRM_Set($@) {
  229. my ($hash, @a) = @_;
  230. return "Need at least one parameters" if(@a < 2);
  231. return "Unknown argument $a[1], choose one of " . join(" ", sort keys %sets)
  232. if(!defined($sets{$a[1]}));
  233. my $command = $a[1];
  234. my $value = $a[2];
  235. COMMAND_HANDLER: {
  236. $command eq "reset" and do {
  237. return $hash->{NAME}." is not connected" unless (FRM_is_firmata_connected($hash) && (defined $hash->{FD} or ($^O=~/Win/ and defined $hash->{USBDev})));
  238. $hash->{FirmataDevice}->system_reset();
  239. FRM_ClearConfiguration($hash);
  240. if (defined $hash->{SERVERSOCKET}) {
  241. # dispose preexisting connections
  242. foreach my $e ( sort keys %main::defs ) {
  243. if ( defined( my $dev = $main::defs{$e} )) {
  244. if ( defined( $dev->{SNAME} ) && ( $dev->{SNAME} eq $hash->{NAME} )) {
  245. FRM_Tcp_Connection_Close($dev);
  246. }
  247. }
  248. }
  249. FRM_FirmataDevice_Close($hash);
  250. last;
  251. } else {
  252. DevIo_Disconnected($hash);
  253. FRM_FirmataDevice_Close($hash);
  254. return DevIo_OpenDev($hash, 0, "FRM_DoInit");
  255. }
  256. };
  257. $command eq "reinit" and do {
  258. FRM_forall_clients($hash,\&FRM_Init_Client,undef);
  259. last;
  260. };
  261. }
  262. return undef;
  263. }
  264. =item FRM_Get($@)
  265. Returns:
  266. requested data or error message
  267. Description:
  268. FHEM module GetFn ($hash is FRM)
  269. =cut
  270. sub FRM_Get($@) {
  271. my ($hash, @a) = @_;
  272. return "Need at least one parameters" if(@a < 2);
  273. return "Unknown argument $a[1], choose one of " . join(" ", sort keys %gets)
  274. if(!defined($gets{$a[1]}));
  275. my $name = shift @a;
  276. my $cmd = shift @a;
  277. ARGUMENT_HANDLER: {
  278. $cmd eq "firmware" and do {
  279. if (FRM_is_firmata_connected($hash)) {
  280. return $hash->{FirmataDevice}->{metadata}->{firmware};
  281. } else {
  282. return "not connected to FirmataDevice";
  283. }
  284. };
  285. $cmd eq "version" and do {
  286. if (FRM_is_firmata_connected($hash)) {
  287. return $hash->{FirmataDevice}->{metadata}->{firmware_version};
  288. } else {
  289. return "not connected to FirmataDevice";
  290. }
  291. };
  292. }
  293. }
  294. =item FRM_Read($)
  295. Returns:
  296. nothing
  297. Description:
  298. FHEM module ReadFn, called by IODev of FRM ($hash is FRM, $chash is TCP client session)
  299. called from the global loop, when the select for hash->{FD} reports data
  300. =cut
  301. sub FRM_Read($) {
  302. my ( $hash ) = @_;
  303. if($hash->{SERVERSOCKET}) { # Accept and create a child
  304. my $chash = TcpServer_Accept($hash, "FRM");
  305. return if(!$chash);
  306. $chash->{DeviceName}=$hash->{PORT}; # required for DevIo_CloseDev and FRM_Ready
  307. $chash->{TCPDev}=$chash->{CD};
  308. # dispose preexisting connections
  309. foreach my $e ( sort keys %main::defs ) {
  310. if ( defined( my $dev = $main::defs{$e} )) {
  311. if ( $dev != $chash && defined( $dev->{SNAME} ) && ( $dev->{SNAME} eq $chash->{SNAME} )) {
  312. FRM_Tcp_Connection_Close($dev);
  313. }
  314. }
  315. }
  316. FRM_FirmataDevice_Close($hash);
  317. FRM_DoInit($chash);
  318. return;
  319. }
  320. my $device = $hash->{FirmataDevice} or return;
  321. $device->poll();
  322. FRM_SetupDevice($hash);
  323. }
  324. =item FRM_Ready($)
  325. Returns:
  326. number of bytes waiting to be read or nothing on error
  327. Description:
  328. FHEM module RedyFn, called by IODev of FRM ($hash is IODev/master, $shash is FRM/slave)
  329. =cut
  330. sub FRM_Ready($) {
  331. my ($hash) = @_;
  332. my $name = $hash->{NAME};
  333. if ($name=~/^^FRM:.+:\d+$/) { # this is a closed tcp-connection, remove it
  334. FRM_Tcp_Connection_Close($hash);
  335. FRM_FirmataDevice_Close($hash);
  336. return;
  337. }
  338. # reopen connection to DevIO if closed
  339. return DevIo_OpenDev($hash, 1, "FRM_DoInit") if($hash->{STATE} eq "disconnected");
  340. # This is relevant for windows/USB only
  341. my $po = $hash->{USBDev};
  342. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  343. if($po) {
  344. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  345. }
  346. return ($InBytes && $InBytes>0);
  347. }
  348. =item FRM_Tcp_Connection_Close($)
  349. Returns:
  350. undef
  351. Description:
  352. FRM internal function for IODev ($hash is IODev/master, $shash is FRM/slave)
  353. =cut
  354. sub FRM_Tcp_Connection_Close($) {
  355. my $hash = shift;
  356. TcpServer_Close($hash);
  357. if ($hash->{SNAME}) {
  358. my $shash = $main::defs{$hash->{SNAME}};
  359. readingsSingleUpdate($shash, 'state', 'listening', 1);
  360. if (defined $shash) {
  361. delete $shash->{SocketDevice} if (defined $shash->{SocketDevice});
  362. }
  363. }
  364. my $dev = $hash->{DeviceName};
  365. my $name = $hash->{NAME};
  366. if (defined $name) {
  367. delete $main::readyfnlist{"$name.$dev"} if (defined $dev);
  368. delete $main::attr{$name};
  369. delete $main::defs{$name};
  370. }
  371. return undef;
  372. }
  373. =item FRM_FirmataDevice_Close($)
  374. Returns:
  375. nothing
  376. Description:
  377. FRM internal function ($hash is FRM)
  378. =cut
  379. sub FRM_FirmataDevice_Close($) {
  380. my $hash = shift;
  381. my $name = $hash->{NAME};
  382. my $device = $hash->{FirmataDevice};
  383. if (defined $device) {
  384. if (defined $device->{io}) {
  385. delete $hash->{FirmataDevice}->{io}->{handle} if defined $hash->{FirmataDevice}->{io}->{handle};
  386. delete $hash->{FirmataDevice}->{io};
  387. }
  388. delete $device->{protocol} if defined $device->{protocol};
  389. delete $hash->{FirmataDevice};
  390. }
  391. }
  392. =item FRM_Attr(@)
  393. Returns:
  394. nothing
  395. Description:
  396. FHEM module AttrFn ($hash is FRM)
  397. =cut
  398. sub FRM_Attr(@) {
  399. my ($command,$name,$attribute,$value) = @_;
  400. my $hash = $main::defs{$name};
  401. if ($command eq "set") {
  402. ARGUMENT_HANDLER: {
  403. ($attribute eq "sampling-interval" or
  404. $attribute eq "i2c-config") and do {
  405. $main::attr{$name}{$attribute}=$value;
  406. FRM_apply_attribute($main::defs{$name},$attribute);
  407. last;
  408. };
  409. $attribute eq "disable" and do {
  410. if ($main::init_done) {
  411. if ($value) {
  412. FRM_Undef($hash);
  413. readingsSingleUpdate($hash, 'state', 'disabled', 1);
  414. } else {
  415. FRM_Start($hash);
  416. }
  417. }
  418. last;
  419. };
  420. }
  421. }
  422. }
  423. =item FRM_apply_attribute()
  424. Returns:
  425. nothing
  426. Description:
  427. FRM internal function ($hash is FRM)
  428. =cut
  429. sub FRM_apply_attribute {
  430. my ($hash,$attribute) = @_;
  431. my $firmata = $hash->{FirmataDevice};
  432. my $name = $hash->{NAME};
  433. if (defined $firmata) {
  434. if ($attribute eq "sampling-interval") {
  435. $firmata->sampling_interval(AttrVal($name,$attribute,"1000"));
  436. } elsif ($attribute eq "i2c-config") {
  437. my $i2cattr = AttrVal($name,$attribute,undef);
  438. if (defined $i2cattr) {
  439. my @a = split(" ", $i2cattr);
  440. my $i2cpins = $firmata->{metadata}{i2c_pins};
  441. my $err;
  442. if (defined $i2cpins and scalar @$i2cpins) {
  443. eval {
  444. foreach my $i2cpin (@$i2cpins) {
  445. $firmata->pin_mode($i2cpin,PIN_I2C);
  446. }
  447. $firmata->i2c_config(@a);
  448. $firmata->observe_i2c(\&FRM_i2c_observer,$hash);
  449. };
  450. $err = $@ if ($@);
  451. } else {
  452. $err = "Error, arduino doesn't support I2C";
  453. }
  454. Log3 $name,2,$err if ($err);
  455. }
  456. }
  457. }
  458. }
  459. =item FRM_DoInit($)
  460. Returns:
  461. undef on success or 1 if Firmata platform attach fails
  462. Description:
  463. FRM internal function for IODev ($hash is IODev/master or FRM, $shash is FRM/slave)
  464. try to reset Firmata device and start Firmata device initialization
  465. =cut
  466. sub FRM_DoInit($) {
  467. my ($hash) = @_;
  468. my $sname = $hash->{SNAME}; #is this a serversocket-connection?
  469. my $shash = defined $sname ? $main::defs{$sname} : $hash;
  470. my $name = $shash->{NAME};
  471. Log3 $name, 5, "$name FRM_DoInit";
  472. readingsSingleUpdate($shash, 'state', "connected", 1);
  473. my $firmata_io = Firmata_IO->new($hash, $name);
  474. my $device = Device::Firmata::Platform->attach($firmata_io) or return 1;
  475. $shash->{FirmataDevice} = $device;
  476. if (defined $sname) {
  477. $shash->{SocketDevice} = $hash;
  478. #as FRM_Read gets the connected socket hash, but calls firmatadevice->poll():
  479. $hash->{FirmataDevice} = $device;
  480. }
  481. $device->observe_string(\&FRM_string_observer, $shash);
  482. if (AttrVal($name, 'resetDeviceOnConnect', 1)) {
  483. $device->system_reset();
  484. FRM_ClearConfiguration($shash);
  485. }
  486. $shash->{SETUP_START} = gettimeofday();
  487. $shash->{SETUP_STAGE} = 1; # detect connect mode (fhem startup, device startup or device reconnect) and query versions
  488. $shash->{SETUP_TRIES} = 1;
  489. return FRM_SetupDevice($shash);
  490. }
  491. =item FRM_ClearConfiguration()
  492. Returns:
  493. nothing
  494. Description:
  495. FRM internal function ($hash is FRM)
  496. delete all version and capability readings
  497. =cut
  498. sub FRM_ClearConfiguration($) {
  499. my $hash = shift;
  500. my $name = $hash->{NAME};
  501. delete $main::defs{$name}{protocol_version};
  502. delete $main::defs{$name}{firmware};
  503. delete $main::defs{$name}{firmware_version};
  504. delete $main::defs{$name}{input_pins};
  505. delete $main::defs{$name}{output_pins};
  506. delete $main::defs{$name}{analog_pins};
  507. delete $main::defs{$name}{pwm_pins};
  508. delete $main::defs{$name}{servo_pins};
  509. delete $main::defs{$name}{i2c_pins};
  510. delete $main::defs{$name}{onewire_pins};
  511. delete $main::defs{$name}{stepper_pins};
  512. delete $main::defs{$name}{encoder_pins};
  513. delete $main::defs{$name}{serial_pins};
  514. delete $main::defs{$name}{pullup_pins};
  515. delete $main::defs{$name}{analog_resolutions};
  516. delete $main::defs{$name}{pwm_resolutions};
  517. delete $main::defs{$name}{servo_resolutions};
  518. delete $main::defs{$name}{encoder_resolutions};
  519. delete $main::defs{$name}{stepper_resolutions};
  520. delete $main::defs{$name}{serial_ports};
  521. }
  522. =item FRM_SetupDevice()
  523. Returns:
  524. undef
  525. Description:
  526. FRM internal function ($hash is IODev/master or FRM)
  527. Monitor data received from Firmata device immediately after connect, perform
  528. protocol, firmware and capability queries and configure device according to
  529. the FRM device attributes.
  530. =cut
  531. sub FRM_SetupDevice($);
  532. sub FRM_SetupDevice($) {
  533. my ($hash) = @_;
  534. if (defined($hash->{SNAME})) {
  535. $hash = $main::defs{$hash->{SNAME}};
  536. }
  537. return undef if (!defined($hash->{SETUP_START}));
  538. my $name = $hash->{NAME};
  539. Log3 $name, 5, "$name setup stage $hash->{SETUP_STAGE}";
  540. my $now = gettimeofday();
  541. my $elapsed = $now - $hash->{SETUP_START};
  542. my $device = $hash->{FirmataDevice};
  543. if ($hash->{SETUP_STAGE} == 1) { # protocol and firmware version
  544. RemoveInternalTimer($hash);
  545. InternalTimer($now + (($elapsed < 1)? 0.1 : 1), 'FRM_SetupDevice', $hash, 0);
  546. # wait for protocol and firmware version
  547. my $fhemRestart = !defined($main::defs{$name}{protocol_version});
  548. my $versionsReceived = $device->{metadata}{firmware} && $device->{metadata}{firmware_version} && $device->{metadata}{protocol_version};
  549. if ($versionsReceived) {
  550. # clear old version and capability readings if not already done
  551. if (!$fhemRestart) {
  552. FRM_ClearConfiguration($hash);
  553. }
  554. # protocol and firmware versions have been received
  555. $main::defs{$name}{firmware} = $device->{metadata}{firmware};
  556. $main::defs{$name}{firmware_version} = $device->{metadata}{firmware_version};
  557. $main::defs{$name}{protocol_version} = $device->{protocol}->get_max_supported_protocol_version($device->{metadata}{protocol_version});
  558. Log3 $name, 3, $name." Firmata Firmware Version: ".$device->{metadata}{firmware}." ".$device->{metadata}{firmware_version}." (using Protocol Version: ".$main::defs{$name}{protocol_version}.")";
  559. # query capabilities
  560. $device->analog_mapping_query();
  561. $device->capability_query();
  562. # wait for capabilities
  563. $hash->{SETUP_STAGE} = 2;
  564. } elsif ($elapsed >= 3) {
  565. # protocol and firmware version still missing
  566. if ($hash->{SETUP_TRIES} < 3) {
  567. # requery versions
  568. Log3 $name, 3, "$name querying Firmata versions";
  569. $device->protocol_version_query();
  570. $device->firmware_version_query();
  571. # restart setup
  572. $hash->{SETUP_START} = gettimeofday();
  573. $hash->{SETUP_STAGE} = 1;
  574. $hash->{SETUP_TRIES}++;
  575. } else {
  576. # retry limit exceeded, abort
  577. $hash->{SETUP_STAGE} = 4;
  578. FRM_SetupDevice($hash);
  579. }
  580. } elsif ($elapsed >= 0.2 && defined($hash->{PORT})) {
  581. # if we don't receive the protocol version within 200 millis, the device has reconnected (or has an old firmware)
  582. my $deviceRestart = $device->{metadata}{protocol_version};
  583. if (!$deviceRestart && !$fhemRestart) {
  584. # probably a reconnect
  585. Log3 $name, 3, "$name Firmata device has reconnected";
  586. #if ($skipSetupOnReconnect) {
  587. # # skip capability queries and device setup, just reinit client modules
  588. # $hash->{SETUP_STAGE} = 3;
  589. # FRM_SetupDevice($hash);
  590. # return undef;
  591. #}
  592. # clear old version and capability readings
  593. FRM_ClearConfiguration($hash);
  594. # query versions
  595. Log3 $name, 3, "$name querying Firmata versions";
  596. $device->protocol_version_query();
  597. $device->firmware_version_query();
  598. }
  599. }
  600. } elsif ($hash->{SETUP_STAGE} == 2) { # device capabilities
  601. RemoveInternalTimer($hash);
  602. InternalTimer(gettimeofday() + 1, 'FRM_SetupDevice', $hash, 0);
  603. my $capabilitiesReceived = $device->{metadata}{capabilities} && ($device->{metadata}{analog_mappings} || ($elapsed >= 5));
  604. if ($capabilitiesReceived) {
  605. # device capabilities have been received, convert to readings
  606. my $inputpins = $device->{metadata}{input_pins};
  607. $main::defs{$name}{input_pins} = join(",", sort{$a<=>$b}(@$inputpins)) if (defined $inputpins and scalar @$inputpins);
  608. my $outputpins = $device->{metadata}{output_pins};
  609. $main::defs{$name}{output_pins} = join(",", sort{$a<=>$b}(@$outputpins)) if (defined $outputpins and scalar @$outputpins);
  610. my $analogpins = $device->{metadata}{analog_pins};
  611. $main::defs{$name}{analog_pins} = join(",", sort{$a<=>$b}(@$analogpins)) if (defined $analogpins and scalar @$analogpins);
  612. my $pwmpins = $device->{metadata}{pwm_pins};
  613. $main::defs{$name}{pwm_pins} = join(",", sort{$a<=>$b}(@$pwmpins)) if (defined $pwmpins and scalar @$pwmpins);
  614. my $servopins = $device->{metadata}{servo_pins};
  615. $main::defs{$name}{servo_pins} = join(",", sort{$a<=>$b}(@$servopins)) if (defined $servopins and scalar @$servopins);
  616. my $i2cpins = $device->{metadata}{i2c_pins};
  617. $main::defs{$name}{i2c_pins} = join(",", sort{$a<=>$b}(@$i2cpins)) if (defined $i2cpins and scalar @$i2cpins);
  618. my $onewirepins = $device->{metadata}{onewire_pins};
  619. $main::defs{$name}{onewire_pins} = join(",", sort{$a<=>$b}(@$onewirepins)) if (defined $onewirepins and scalar @$onewirepins);
  620. my $stepperpins = $device->{metadata}{stepper_pins};
  621. $main::defs{$name}{stepper_pins} = join(",", sort{$a<=>$b}(@$stepperpins)) if (defined $stepperpins and scalar @$stepperpins);
  622. my $encoderpins = $device->{metadata}{encoder_pins};
  623. $main::defs{$name}{encoder_pins} = join(",", sort{$a<=>$b}(@$encoderpins)) if (defined $encoderpins and scalar @$encoderpins);
  624. my $serialpins = $device->{metadata}{serial_pins};
  625. $main::defs{$name}{serial_pins} = join(",", sort{$a<=>$b}(@$serialpins)) if (defined $serialpins and scalar @$serialpins);
  626. my $pulluppins = $device->{metadata}{pullup_pins};
  627. $main::defs{$name}{pullup_pins} = join(",", sort{$a<=>$b}(@$pulluppins)) if (defined $pulluppins and scalar @$pulluppins);
  628. if (defined $device->{metadata}{analog_resolutions}) {
  629. my @analog_resolutions;
  630. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{analog_resolutions}})) {
  631. push @analog_resolutions,$pin.":".$device->{metadata}{analog_resolutions}{$pin};
  632. }
  633. $main::defs{$name}{analog_resolutions} = join(",",@analog_resolutions) if (scalar @analog_resolutions);
  634. }
  635. if (defined $device->{metadata}{pwm_resolutions}) {
  636. my @pwm_resolutions;
  637. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{pwm_resolutions}})) {
  638. push @pwm_resolutions,$pin.":".$device->{metadata}{pwm_resolutions}{$pin};
  639. }
  640. $main::defs{$name}{pwm_resolutions} = join(",",@pwm_resolutions) if (scalar @pwm_resolutions);
  641. }
  642. if (defined $device->{metadata}{servo_resolutions}) {
  643. my @servo_resolutions;
  644. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{servo_resolutions}})) {
  645. push @servo_resolutions,$pin.":".$device->{metadata}{servo_resolutions}{$pin};
  646. }
  647. $main::defs{$name}{servo_resolutions} = join(",",@servo_resolutions) if (scalar @servo_resolutions);
  648. }
  649. if (defined $device->{metadata}{encoder_resolutions}) {
  650. my @encoder_resolutions;
  651. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{encoder_resolutions}})) {
  652. push @encoder_resolutions,$pin.":".$device->{metadata}{encoder_resolutions}{$pin};
  653. }
  654. $main::defs{$name}{encoder_resolutions} = join(",",@encoder_resolutions) if (scalar @encoder_resolutions);
  655. }
  656. if (defined $device->{metadata}{stepper_resolutions}) {
  657. my @stepper_resolutions;
  658. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{stepper_resolutions}})) {
  659. push @stepper_resolutions,$pin.":".$device->{metadata}{stepper_resolutions}{$pin};
  660. }
  661. $main::defs{$name}{stepper_resolutions} = join(",",@stepper_resolutions) if (scalar @stepper_resolutions);
  662. }
  663. if (defined $device->{metadata}{serial_resolutions}) {
  664. my @serial_ports;
  665. foreach my $pin (sort{$a<=>$b}(keys %{$device->{metadata}{serial_resolutions}})) {
  666. push @serial_ports,$pin.":".int($device->{metadata}{serial_resolutions}{$pin}/2);
  667. }
  668. $main::defs{$name}{serial_ports} = join(",",@serial_ports) if (scalar @serial_ports);
  669. }
  670. # setup device
  671. FRM_apply_attribute($hash, "sampling-interval");
  672. FRM_apply_attribute($hash, "i2c-config");
  673. FRM_serial_setup($hash);
  674. # ready, init client modules
  675. $hash->{SETUP_STAGE} = 3;
  676. FRM_SetupDevice($hash);
  677. } elsif ($elapsed >= 5) {
  678. # capabilities receive timeout, abort
  679. $hash->{SETUP_STAGE} = 4;
  680. FRM_SetupDevice($hash);
  681. }
  682. } elsif ($hash->{SETUP_STAGE} == 3) { # client modules
  683. # client module initialization
  684. FRM_forall_clients($hash, \&FRM_Init_Client, undef);
  685. readingsSingleUpdate($hash, 'state', "Initialized", 1);
  686. # done, terminate setup sequence
  687. $hash->{SETUP_STAGE} = 5;
  688. FRM_SetupDevice($hash);
  689. } elsif ($hash->{SETUP_STAGE} == 4) { # abort setup
  690. # device setup has failed, cleanup connection
  691. if (defined $hash->{SERVERSOCKET}) {
  692. Log3 $name, 3, "$name no response from Firmata, closing TCP connection";
  693. foreach my $e (sort keys %main::defs) {
  694. if (defined(my $dev = $main::defs{$e})) {
  695. if (defined($dev->{SNAME}) && ($dev->{SNAME} eq $hash->{NAME})) {
  696. FRM_Tcp_Connection_Close($dev);
  697. }
  698. }
  699. }
  700. } else {
  701. Log3 $name, 3, "$name no response from Firmata, closing DevIo";
  702. DevIo_Disconnected($hash);
  703. }
  704. FRM_FirmataDevice_Close($hash);
  705. # cleanup setup
  706. $hash->{SETUP_STAGE} = 5;
  707. FRM_SetupDevice($hash);
  708. } elsif ($hash->{SETUP_STAGE} == 5) { # finish setup
  709. # terminate setup sequence
  710. RemoveInternalTimer($hash);
  711. delete $hash->{SETUP_START};
  712. delete $hash->{SETUP_STAGE};
  713. delete $hash->{SETUP_TRIES};
  714. } else {
  715. # invalid state, abort
  716. $hash->{SETUP_STAGE} = 4;
  717. FRM_SetupDevice($hash);
  718. }
  719. return undef;
  720. }
  721. =item FRM_forall_clients($$$)
  722. Returns:
  723. undef
  724. Description:
  725. FRM internal function ($hash is FRM)
  726. Call a function $1 with arguments $2 for all FHEM devices that use device $0 as IODev.
  727. =cut
  728. sub
  729. FRM_forall_clients($$$) {
  730. my ($hash,$fn,$args) = @_;
  731. foreach my $d ( sort keys %main::defs ) {
  732. if ( defined( $main::defs{$d} )
  733. && defined( $main::defs{$d}{IODev} )
  734. && $main::defs{$d}{IODev} == $hash ) {
  735. &$fn($main::defs{$d},$args);
  736. }
  737. }
  738. return undef;
  739. }
  740. =item FRM_Init_Client($@)
  741. Exception:
  742. if calling InitFn of client $0 raises an exception
  743. Description:
  744. FRM public client function ($chash is FRM client)
  745. trigger InitFn of FRM client, typically called after initialization of connection to Firmata device
  746. =cut
  747. sub
  748. FRM_Init_Client($@) {
  749. my ($chash,$args) = @_;
  750. if (!defined $args and defined $chash->{DEF}) {
  751. my @a = split("[ \t][ \t]*", $chash->{DEF});
  752. $args = \@a;
  753. }
  754. my $cname = $chash->{NAME};
  755. my $iodev = defined($chash->{IODev})? $chash->{IODev}{NAME} : "?";
  756. Log3 $iodev, 5, "$iodev initializing '$cname'";
  757. if (!defined($main::modules{$main::defs{$cname}{TYPE}}{InitFn})) {
  758. Log3 $iodev, 5, "$iodev error initializing '$cname': InitFn not implemented";
  759. }
  760. my $ret = CallFn($cname,"InitFn",$chash,$args);
  761. if ($ret) {
  762. Log3 $iodev,2,"$iodev error initializing '$cname': $ret";
  763. }
  764. }
  765. =item FRM_Init_Pin_Client($$$)
  766. Returns:
  767. undef on success or error message
  768. Exception:
  769. if calling InitFn of client $0 raises an exception
  770. Description:
  771. FRM public client function ($chash is FRM client)
  772. register FRM client at IODev for I/O operations and set requested pin mode in Firmata device
  773. =cut
  774. sub
  775. FRM_Init_Pin_Client($$$) {
  776. my ($chash,$args,$mode) = @_;
  777. my $u = "wrong syntax: define <name> FRM_XXX pin";
  778. return $u unless defined $args and int(@$args) > 0;
  779. my $pin = @$args[0];
  780. $chash->{PIN} = $pin;
  781. eval {
  782. FRM_Client_AssignIOPort($chash);
  783. FRM_Client_FirmataDevice($chash)->pin_mode($pin,$mode);
  784. };
  785. if ($@) {
  786. readingsSingleUpdate($chash, 'state', "error initializing: pin $pin", 1);
  787. $@ =~ /^(.*)( at.*FHEM.*)$/;
  788. return $1;
  789. }
  790. my $name = $chash->{NAME};
  791. my $iodev = defined($chash->{IODev})? $chash->{IODev}{NAME} : "?";
  792. Log3 $name, 5, "$name initialized pin $pin of $iodev";
  793. return undef;
  794. }
  795. =item FRM_Client_Define($$)
  796. Returns:
  797. undef on success or error message
  798. Exception:
  799. if calling InitFn of client $0 raises an exception
  800. Description:
  801. FRM public client function ($chash is FRM client)
  802. trigger InitFn of FRM client
  803. =cut
  804. sub
  805. FRM_Client_Define($$) {
  806. my ($chash, $def) = @_;
  807. my @a = split("[ \t][ \t]*", $def);
  808. readingsSingleUpdate($chash, 'state', 'defined', 1);
  809. if ($main::init_done) {
  810. eval {
  811. FRM_Init_Client($chash,[@a[2..scalar(@a)-1]]);
  812. };
  813. if ($@) {
  814. $@ =~ /^(.*)( at.*FHEM.*)$/;
  815. return $1;
  816. }
  817. }
  818. return undef;
  819. }
  820. =item FRM_Client_Undef($$)
  821. Returns:
  822. undef
  823. Description:
  824. FRM public client function ($chash is FRM client)
  825. try to set pin mode to analog input if supported or to digital input
  826. =cut
  827. sub
  828. FRM_Client_Undef($$) {
  829. my ($chash, $name) = @_;
  830. my $pin = $chash->{PIN};
  831. eval {
  832. my $firmata = FRM_Client_FirmataDevice($chash);
  833. $firmata->pin_mode($pin,PIN_ANALOG);
  834. };
  835. if ($@) {
  836. eval {
  837. my $firmata = FRM_Client_FirmataDevice($chash);
  838. $firmata->pin_mode($pin,PIN_INPUT);
  839. # @TODO PIN_PULLUP
  840. $firmata->digital_write($pin,0);
  841. };
  842. }
  843. return undef;
  844. }
  845. =item FRM_Client_Unassign($)
  846. Description:
  847. FRM internal function ($chash FRM client)
  848. remove IODev assignment from FRM client device
  849. =cut
  850. sub
  851. FRM_Client_Unassign($) {
  852. my ($chash) = @_;
  853. delete $chash->{IODev} if defined $chash->{IODev};
  854. readingsSingleUpdate($chash, 'state', 'defined', 1);
  855. }
  856. =item FRM_Client_AssignIOPort($$)
  857. Exception:
  858. if IODev of of client $0 is still undefined after returning from AssignIoPort() or
  859. if another client is already assigned to the same PIN as client
  860. Description:
  861. FRM public client function ($chash is FRM client)
  862. =cut
  863. sub
  864. FRM_Client_AssignIOPort($@) {
  865. my ($chash,$iodev) = @_;
  866. my $name = $chash->{NAME};
  867. # use proposed $iodev or assigned {IODev} (FHEM will additionally check IODev attribute if not defined)
  868. $iodev = defined($iodev)? $iodev : (defined($chash->{IODev})? $chash->{IODev}{NAME} : undef);
  869. Log3 $name, 5, "$name FRM_Client_AssignIOPort before IODev " . (defined($chash->{IODev})? $chash->{IODev}{NAME} : "-" ) . " -> " . (defined($iodev)? $iodev : "?");
  870. AssignIoPort($chash, $iodev);
  871. die "unable to assign IODev to '$name'" unless defined ($chash->{IODev});
  872. Log3 $name, 5, "$name FRM_Client_AssignIOPort after IODev $chash->{IODev}{NAME}";
  873. if (defined($chash->{IODev}->{SNAME})) {
  874. $chash->{IODev} = $main::defs{$chash->{IODev}->{SNAME}};
  875. $attr{$name}{IODev} = $chash->{IODev}{NAME};
  876. }
  877. foreach my $d ( sort keys %main::defs ) {
  878. if ( defined( my $dev = $main::defs{$d} )) {
  879. if ( $dev != $chash
  880. && defined( $dev->{IODev} )
  881. && defined( $dev->{PIN} )
  882. && $dev->{IODev} == $chash->{IODev}
  883. && defined( $chash->{PIN})
  884. && grep {$_ == $chash->{PIN}} split(" ",$dev->{PIN}) ) {
  885. delete $chash->{IODev};
  886. delete $attr{$name}{IODev};
  887. die "Device '$main::defs{$d}{NAME}' already defined for pin $chash->{PIN}";
  888. }
  889. }
  890. }
  891. }
  892. =item FRM_Client_FirmataDevice($)
  893. Returns:
  894. perl-firmata handle for given FRM client
  895. Exception:
  896. if IODev is not defined or not connected
  897. Description:
  898. FRM public client function ($chash is FRM client, $iodev is FRM)
  899. =cut
  900. sub FRM_Client_FirmataDevice($) {
  901. my $chash = shift;
  902. my $iodev = $chash->{IODev};
  903. die $chash->{NAME}." no IODev assigned" unless defined $iodev;
  904. die $chash->{NAME}.", ".$iodev->{NAME}." is not connected" unless (defined $iodev->{FirmataDevice} and (defined $iodev->{FD} or ($^O=~/Win/ and defined $iodev->{USBDev})));
  905. return $iodev->{FirmataDevice};
  906. }
  907. =item FRM_Catch($)
  908. Returns:
  909. undef or exception message if parameter $0 is a FHEM stack trace
  910. Description:
  911. FRM public utility function
  912. =cut
  913. sub FRM_Catch($) {
  914. my $exception = shift;
  915. if ($exception) {
  916. $exception =~ /^(.*)( at.*FHEM.*)$/;
  917. return $1;
  918. }
  919. return undef;
  920. }
  921. =item Firmata_IO
  922. Description:
  923. FRM internal wrapper class to provide perl-firmata compatible read and write methods
  924. =cut
  925. package Firmata_IO;
  926. sub new($$) {
  927. my ($class,$hash,$name) = @_;
  928. return bless {
  929. hash => $hash,
  930. name => $name,
  931. }, $class;
  932. }
  933. sub data_write {
  934. my ( $self, $buf ) = @_;
  935. my $hash = $self->{hash};
  936. main::Log3 $self->{name},5,"$self->{name} FRM:>".unpack "H*",$buf;
  937. main::DevIo_SimpleWrite($hash,$buf,undef);
  938. }
  939. sub data_read {
  940. my ( $self, $bytes ) = @_;
  941. my $hash = $self->{hash};
  942. my $string = main::DevIo_SimpleRead($hash);
  943. if (defined $string ) {
  944. main::Log3 $self->{name},5,"$self->{name} FRM:<".unpack "H*",$string;
  945. }
  946. return $string;
  947. }
  948. package main;
  949. =item FRM_I2C_Write
  950. # im master muss eine I2CWrtFn definiert werden, diese wird vom client mit
  951. # CallFn(<mastername>, "I2CWrtFn", <masterhash>, \%sendpackage);
  952. # aufgerufen.
  953. # Der Master muss mit AssignIoPort() dem Client zugeordnet werden;
  954. # %sendpackage muss folgende keys enthalten:
  955. #
  956. # i2caddress => <xx>
  957. # direction => <i2cwrite|i2cread>
  958. # data => <xx [xx ...] (kann für read leer bleiben)>
  959. #
  960. # der Master fügt zu %sendpackage noch folgende keys hinzu:
  961. #
  962. # received (durch leerzeichen getrennte 1byte hexwerte)
  963. # mastername_* (alle mit mastername_ beginnenden keys können als internal im client angelegt weden)
  964. # unter anderem: mastername_SENDSTAT (enthält "Ok" wenn Übertragung erfolgreich)
  965. #
  966. # danach ruft er über:
  967. # CallFn(<clientname>, "I2CRecFn", <clienthash>, $sendpackage);
  968. # die I2CRecFn im client auf. Dort werden die Daten verarbeitet und
  969. # im Master wird der Hash sendpackage gelöscht.
  970. #
  971. # $package->{i2caddress}; # single byte value
  972. # $package->{direction}; # i2cread|i2cwrite
  973. # $package->{data}; # space separated list of values
  974. # $package->{reg}; # register
  975. # $package->{nbyte}; # number of bytes to read
  976. #
  977. # $firmata->i2c_read($address,$register,$bytestoread);
  978. # $firmata->i2c_write($address,@data);
  979. Description:
  980. FHEM module I2CWrtFn ($hash is FRM)
  981. =cut
  982. sub FRM_I2C_Write
  983. {
  984. my ($hash,$package) = @_;
  985. if (FRM_is_firmata_connected($hash) && defined($package) && defined($package->{i2caddress})) {
  986. my $firmata = $hash->{FirmataDevice};
  987. COMMANDHANDLER: {
  988. $package->{direction} eq "i2cwrite" and do {
  989. if (defined $package->{reg}) {
  990. $firmata->i2c_write($package->{i2caddress},$package->{reg},split(" ",$package->{data}));
  991. } else {
  992. $firmata->i2c_write($package->{i2caddress},split(" ",$package->{data}));
  993. }
  994. last;
  995. };
  996. $package->{direction} eq "i2cread" and do {
  997. delete $hash->{I2C_ERROR};
  998. if (defined $package->{reg}) {
  999. $firmata->i2c_readonce($package->{i2caddress},$package->{reg},defined $package->{nbyte} ? $package->{nbyte} : 1);
  1000. } else {
  1001. $firmata->i2c_readonce($package->{i2caddress},defined $package->{nbyte} ? $package->{nbyte} : 1);
  1002. }
  1003. last;
  1004. };
  1005. }
  1006. }
  1007. }
  1008. =item FRM_i2c_observer
  1009. Returns:
  1010. nothing
  1011. Description:
  1012. FRM internal Firmata device receive callback ($hash is FRM)
  1013. =cut
  1014. sub FRM_i2c_observer {
  1015. my ($data,$hash) = @_;
  1016. Log3 $hash->{NAME},5,"onI2CMessage address: '".$data->{address}."', register: '".$data->{register}."' data: [".(join(',',@{$data->{data}}))."]";
  1017. FRM_forall_clients($hash,\&FRM_i2c_update_device,$data);
  1018. }
  1019. =item FRM_i2c_update_device
  1020. Returns:
  1021. nothing
  1022. Description:
  1023. FRM internal receive bridge for I2C receive from Firmata device to FRM I2C client ($chash is FRM client that called FRM_I2C_Write)
  1024. If I2C address of FRM client matches, the received data is passed on.
  1025. =cut
  1026. sub FRM_i2c_update_device {
  1027. my ($chash,$data) = @_;
  1028. if (defined $chash->{I2C_Address} and $chash->{I2C_Address} eq $data->{address}) {
  1029. my $sendStat = "Ok";
  1030. if (defined($chash->{IODev}->{I2C_ERROR})) {
  1031. $sendStat = $chash->{IODev}->{I2C_ERROR};
  1032. }
  1033. CallFn($chash->{NAME}, "I2CRecFn", $chash, {
  1034. i2caddress => $data->{address},
  1035. direction => "i2cread",
  1036. reg => $data->{register},
  1037. nbyte => scalar(@{$data->{data}}),
  1038. received => join (' ',@{$data->{data}}),
  1039. $chash->{IODev}->{NAME}."_SENDSTAT" => $sendStat,
  1040. });
  1041. } elsif (defined $chash->{"i2c-address"} && $chash->{"i2c-address"}==$data->{address}) {
  1042. my $replydata = $data->{data};
  1043. my @values = split(" ",ReadingsVal($chash->{NAME},"values",""));
  1044. splice(@values,$data->{register},@$replydata, @$replydata);
  1045. readingsBeginUpdate($chash);
  1046. $chash->{STATE}="active";
  1047. readingsBulkUpdate($chash,"values",join (" ",@values),1);
  1048. readingsEndUpdate($chash,1);
  1049. }
  1050. }
  1051. =item FRM_string_observer
  1052. Returns:
  1053. nothing
  1054. Description:
  1055. FRM internal Firmata device receive callback ($hash is FRM)
  1056. =cut
  1057. sub FRM_string_observer {
  1058. my ($string,$hash) = @_;
  1059. my $errorExclude = AttrVal($hash->{NAME}, "errorExclude", undef);
  1060. if (defined($errorExclude) && length($errorExclude) > 0 && ($string =~ $errorExclude)) {
  1061. Log3 $hash->{NAME},5,"received String_data: ".$string;
  1062. readingsSingleUpdate($hash,"stringMessage",$string,1);
  1063. } else {
  1064. Log3 $hash->{NAME},3,"received String_data: ".$string;
  1065. readingsSingleUpdate($hash,"error",$string,1);
  1066. if ($string =~ "I2C.*") {
  1067. $hash->{I2C_ERROR} = substr($string, 5);
  1068. }
  1069. }
  1070. }
  1071. =item FRM_serial_observer
  1072. Returns:
  1073. nothing
  1074. Description:
  1075. FRM internal Firmata device receive callback ($hash is FRM)
  1076. =cut
  1077. sub FRM_serial_observer {
  1078. my ($data,$hash) = @_;
  1079. #Log3 $hash->{NAME},5,"onSerialMessage port: '".$data->{port}."' data: [".(join(',',@{$data->{data}}))."]";
  1080. FRM_forall_clients($hash,\&FRM_serial_update_device,$data);
  1081. }
  1082. =item FRM_serial_update_device
  1083. Returns:
  1084. nothing
  1085. Description:
  1086. FRM internal receive bridge for serial receive from Firmata device to FRM serial client ($chash is FRM client)
  1087. If IODevPort of FRM client matches, the received data is passed on.
  1088. =cut
  1089. sub FRM_serial_update_device {
  1090. my ($chash,$data) = @_;
  1091. if (defined $chash->{IODevPort} and $chash->{IODevPort} eq $data->{port}) {
  1092. my $buf = pack("C*", @{$data->{data}});
  1093. #Log3 $chash->{NAME},5,"FRM_serial_update_device port: " . length($buf) . " bytes on serial port " . $data->{port} . " for " . $chash->{NAME};
  1094. $chash->{IODevRxBuffer} = "" if (!defined($chash->{IODevRxBuffer}));
  1095. $chash->{IODevRxBuffer} = $chash->{IODevRxBuffer} . $buf;
  1096. CallFn($chash->{NAME}, "ReadFn", $chash);
  1097. }
  1098. }
  1099. =item FRM_serial_setup
  1100. Returns:
  1101. nothing
  1102. Description:
  1103. FRM internal function ($hash is FRM, $chash is FRM client)
  1104. =cut
  1105. sub FRM_serial_setup {
  1106. my ($hash) = @_;
  1107. foreach my $port ( keys %{$hash->{SERIAL}} ) {
  1108. my $chash = $defs{$hash->{SERIAL}{$port}};
  1109. if (defined($chash)) {
  1110. FRM_Serial_Setup($chash);
  1111. }
  1112. }
  1113. }
  1114. =item FRM_poll
  1115. Returns:
  1116. true if something was read from the Firmata device or nothing
  1117. Description:
  1118. FRM public function ($hash is FRM)
  1119. =cut
  1120. sub FRM_poll {
  1121. my ($hash) = @_;
  1122. if (defined $hash->{SocketDevice} and defined $hash->{SocketDevice}->{FD}) {
  1123. my ($rout, $rin) = ('', '');
  1124. vec($rin, $hash->{SocketDevice}->{FD}, 1) = 1;
  1125. my $nfound = select($rout=$rin, undef, undef, 0.1);
  1126. my $mfound = vec($rout, $hash->{SocketDevice}->{FD}, 1);
  1127. if($mfound && FRM_is_firmata_connected($hash)) {
  1128. $hash->{FirmataDevice}->poll();
  1129. }
  1130. return $mfound;
  1131. } elsif (defined $hash->{FD}) {
  1132. my ($rout, $rin) = ('', '');
  1133. vec($rin, $hash->{FD}, 1) = 1;
  1134. my $nfound = select($rout=$rin, undef, undef, 0.1);
  1135. my $mfound = vec($rout, $hash->{FD}, 1);
  1136. if($mfound && FRM_is_firmata_connected($hash)) {
  1137. $hash->{FirmataDevice}->poll();
  1138. }
  1139. return $mfound;
  1140. } else {
  1141. # This is relevant for windows/USB only
  1142. my $po = $hash->{USBDev};
  1143. my ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags);
  1144. if($po) {
  1145. ($BlockingFlags, $InBytes, $OutBytes, $ErrorFlags) = $po->status;
  1146. }
  1147. if ($InBytes && $InBytes>0 && FRM_is_firmata_connected($hash)) {
  1148. $hash->{FirmataDevice}->poll();
  1149. }
  1150. }
  1151. }
  1152. =item FRM_OWX_Init
  1153. Returns:
  1154. undef or error message
  1155. Description:
  1156. obsolete FRM client function ($chash is FRM client)
  1157. =cut
  1158. sub FRM_OWX_Init($$) {
  1159. my ($chash,$args) = @_;
  1160. my $ret = FRM_Init_Pin_Client($chash,$args,PIN_ONEWIRE);
  1161. return $ret if (defined $ret);
  1162. eval {
  1163. my $firmata = FRM_Client_FirmataDevice($chash);
  1164. my $pin = $chash->{PIN};
  1165. $chash->{FRM_OWX_CORRELATIONID} = 0;
  1166. $firmata->observe_onewire($pin,\&FRM_OWX_observer,$chash);
  1167. $chash->{FRM_OWX_REPLIES} = {};
  1168. $chash->{DEVS} = [];
  1169. if ( AttrVal($chash->{NAME},"buspower","") eq "parasitic" ) {
  1170. $firmata->onewire_config($pin,1);
  1171. }
  1172. };
  1173. return GP_Catch($@) if ($@);
  1174. ReadingsSingleUpdate($chash, 'state', 'Initialized', 1);
  1175. InternalTimer(gettimeofday()+10, "OWX_Discover", $chash,0);
  1176. return undef;
  1177. }
  1178. =item FRM_OWX_observer
  1179. Returns:
  1180. nothing
  1181. Description:
  1182. obsolete Firmata device receive callback ($chash is FRM client)
  1183. =cut
  1184. sub FRM_OWX_observer
  1185. {
  1186. my ( $data,$chash ) = @_;
  1187. my $command = $data->{command};
  1188. COMMAND_HANDLER: {
  1189. $command eq "READ_REPLY" and do {
  1190. my $id = $data->{id};
  1191. my $request = (defined $id) ? $chash->{FRM_OWX_REQUESTS}->{$id} : undef;
  1192. unless (defined $request) {
  1193. return unless (defined $data->{device});
  1194. my $owx_device = FRM_OWX_firmata_to_device($data->{device});
  1195. my %requests = %{$chash->{FRM_OWX_REQUESTS}};
  1196. foreach my $key (keys %requests) {
  1197. if ($requests{$key}->{device} eq $owx_device) {
  1198. $request = $requests{$key};
  1199. $id = $key;
  1200. last;
  1201. };
  1202. };
  1203. };
  1204. return unless (defined $request);
  1205. my $owx_data = pack "C*",@{$data->{data}};
  1206. my $owx_device = $request->{device};
  1207. $chash->{FRM_OWX_REPLIES}->{$owx_device} = $owx_data;
  1208. delete $chash->{FRM_OWX_REQUESTS}->{$id};
  1209. last;
  1210. };
  1211. ($command eq "SEARCH_REPLY" or $command eq "SEARCH_ALARMS_REPLY") and do {
  1212. my @owx_devices = ();
  1213. foreach my $device (@{$data->{devices}}) {
  1214. push @owx_devices, FRM_OWX_firmata_to_device($device);
  1215. }
  1216. if ($command eq "SEARCH_REPLY") {
  1217. $chash->{DEVS} = \@owx_devices;
  1218. #$main::attr{$chash->{NAME}}{"ow-devices"} = join " ",@owx_devices;
  1219. } else {
  1220. $chash->{ALARMDEVS} = \@owx_devices;
  1221. }
  1222. last;
  1223. };
  1224. }
  1225. }
  1226. =item FRM_OWX_device_to_firmata
  1227. Description:
  1228. obsolete FRM internal OWX utility function
  1229. =cut
  1230. sub FRM_OWX_device_to_firmata
  1231. {
  1232. my @device;
  1233. foreach my $hbyte (unpack "A2xA2A2A2A2A2A2xA2", shift) {
  1234. push @device, hex $hbyte;
  1235. }
  1236. return {
  1237. family => shift @device,
  1238. crc => pop @device,
  1239. identity => \@device,
  1240. }
  1241. }
  1242. =item FRM_OWX_firmata_to_device
  1243. Description:
  1244. obsolete FRM internal OWX utility function
  1245. =cut
  1246. sub FRM_OWX_firmata_to_device
  1247. {
  1248. my $device = shift;
  1249. return sprintf ("%02X.%02X%02X%02X%02X%02X%02X.%02X",$device->{family},@{$device->{identity}},$device->{crc});
  1250. }
  1251. =item FRM_OWX_Verify
  1252. Description:
  1253. obsolete FRM client function ($chash is FRM client)
  1254. =cut
  1255. sub FRM_OWX_Verify {
  1256. my ($chash,$dev) = @_;
  1257. foreach my $found (@{$chash->{DEVS}}) {
  1258. if ($dev eq $found) {
  1259. return 1;
  1260. }
  1261. }
  1262. return 0;
  1263. }
  1264. =item FRM_OWX_Alarms
  1265. Description:
  1266. obsolete FRM client function ($chash is FRM client)
  1267. =cut
  1268. sub FRM_OWX_Alarms {
  1269. my ($chash) = @_;
  1270. my $ret = eval {
  1271. my $firmata = FRM_Client_FirmataDevice($chash);
  1272. my $pin = $chash->{PIN};
  1273. return 0 unless ( defined $firmata and defined $pin );
  1274. $chash->{ALARMDEVS} = undef;
  1275. $firmata->onewire_search_alarms($chash->{PIN});
  1276. my $times = AttrVal($chash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  1277. for (my $i=0;$i<$times;$i++) {
  1278. if (FRM_poll($chash->{IODev})) {
  1279. if (defined $chash->{ALARMDEVS}) {
  1280. return 1;
  1281. }
  1282. } else {
  1283. select (undef,undef,undef,0.05);
  1284. }
  1285. }
  1286. $chash->{ALARMDEVS} = [];
  1287. return 1;
  1288. };
  1289. if ($@) {
  1290. Log3 $chash->{NAME},4,"FRM_OWX_Alarms: ".GP_Catch($@);
  1291. return 0;
  1292. }
  1293. return $ret;
  1294. }
  1295. =item FRM_OWX_Reset
  1296. Description:
  1297. obsolete FRM client function ($chash is FRM client)
  1298. =cut
  1299. sub FRM_OWX_Reset {
  1300. my ($chash) = @_;
  1301. my $ret = eval {
  1302. my $firmata = FRM_Client_FirmataDevice($chash);
  1303. my $pin = $chash->{PIN};
  1304. return undef unless ( defined $firmata and defined $pin );
  1305. $firmata->onewire_reset($pin);
  1306. return 1;
  1307. };
  1308. if ($@) {
  1309. Log3 $chash->{NAME},4,"FRM_OWX_Alarms: ".GP_Catch($@);
  1310. return 0;
  1311. }
  1312. return $ret;
  1313. }
  1314. =item FRM_OWX_Complex
  1315. Description:
  1316. obsolete FRM client function ($chash is FRM client)
  1317. =cut
  1318. sub FRM_OWX_Complex ($$$$) {
  1319. my ( $chash, $owx_dev, $data, $numread ) = @_;
  1320. my $res = "";
  1321. my $ret = eval {
  1322. my $firmata = FRM_Client_FirmataDevice($chash);
  1323. my $pin = $chash->{PIN};
  1324. return 0 unless ( defined $firmata and defined $pin );
  1325. my $ow_command = {};
  1326. #-- has match ROM part
  1327. if ($owx_dev) {
  1328. $ow_command->{"select"} = FRM_OWX_device_to_firmata($owx_dev);
  1329. #-- padding first 9 bytes into result string, since we have this
  1330. # in the serial interfaces as well
  1331. $res .= "000000000";
  1332. }
  1333. #-- has data part
  1334. if ($data) {
  1335. my @data = unpack "C*", $data;
  1336. $ow_command->{"write"} = \@data;
  1337. $res.=$data;
  1338. }
  1339. #-- has receive part
  1340. if ( $numread > 0 ) {
  1341. $ow_command->{"read"} = $numread;
  1342. #Firmata sends 0-address on read after skip
  1343. $owx_dev = '00.000000000000.00' unless defined $owx_dev;
  1344. my $id = $chash->{FRM_OWX_CORRELATIONID};
  1345. $ow_command->{"id"} = $chash->{FRM_OWX_CORRELATIONID};
  1346. $chash->{FRM_OWX_REQUESTS}->{$id} = {
  1347. command => $ow_command,
  1348. device => $owx_dev
  1349. };
  1350. delete $chash->{FRM_OWX_REPLIES}->{$owx_dev};
  1351. $chash->{FRM_OWX_CORRELATIONID} = ($id + 1) & 0xFFFF;
  1352. }
  1353. $firmata->onewire_command_series( $pin, $ow_command );
  1354. if ($numread) {
  1355. my $times = AttrVal($chash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  1356. for (my $i=0;$i<$times;$i++) {
  1357. if (FRM_poll($chash->{IODev})) {
  1358. if (defined $chash->{FRM_OWX_REPLIES}->{$owx_dev}) {
  1359. $res .= $chash->{FRM_OWX_REPLIES}->{$owx_dev};
  1360. return $res;
  1361. }
  1362. } else {
  1363. select (undef,undef,undef,0.05);
  1364. }
  1365. }
  1366. }
  1367. return $res;
  1368. };
  1369. if ($@) {
  1370. Log3 $chash->{NAME},4,"FRM_OWX_Alarms: ".GP_Catch($@);
  1371. return 0;
  1372. }
  1373. return $ret;
  1374. }
  1375. =item FRM_OWX_Discover
  1376. Returns:
  1377. 0: error
  1378. 1: OK
  1379. Description:
  1380. obsolete FRM client function ($chash is FRM client)
  1381. Discover devices on the 1-Wire bus via Firmata internal firmware
  1382. =cut
  1383. sub FRM_OWX_Discover ($) {
  1384. my ($chash) = @_;
  1385. my $ret = eval {
  1386. my $firmata = FRM_Client_FirmataDevice($chash);
  1387. my $pin = $chash->{PIN};
  1388. return 0 unless ( defined $firmata and defined $pin );
  1389. my $old_devices = $chash->{DEVS};
  1390. $chash->{DEVS} = undef;
  1391. $firmata->onewire_search($chash->{PIN});
  1392. my $times = AttrVal($chash,"ow-read-timeout",1000) / 50; #timeout in ms, defaults to 1 sec
  1393. for (my $i=0;$i<$times;$i++) {
  1394. if (FRM_poll($chash->{IODev})) {
  1395. if (defined $chash->{DEVS}) {
  1396. return 1;
  1397. }
  1398. } else {
  1399. select (undef,undef,undef,0.05);
  1400. }
  1401. }
  1402. $chash->{DEVS} = $old_devices;
  1403. return 1;
  1404. };
  1405. if ($@) {
  1406. Log3 $chash->{NAME},4,"FRM_OWX_Alarms: ".GP_Catch($@);
  1407. return 0;
  1408. }
  1409. return $ret;
  1410. }
  1411. =item FRM_Serial_Open
  1412. Returns:
  1413. 0: error
  1414. 1: OK
  1415. Description:
  1416. FHEM module IOOpenFn for FRM client that uses FRM as IODev ($chash is FRM client)
  1417. =cut
  1418. sub FRM_Serial_Open {
  1419. my ($chash) = @_;
  1420. if (!defined $chash->{IODevPort} || !defined $chash->{IODevParameters}) {
  1421. Log3 $chash->{NAME},3,"$chash->{IODev}{NAME} Serial_Open: serial port or baudrate not defined by $chash->{NAME}";
  1422. return 0;
  1423. }
  1424. $chash->{IODev}{SERIAL}{$chash->{IODevPort}} = $chash->{NAME};
  1425. Log3 $chash->{NAME},5,"$chash->{IODev}{NAME} Serial_Open: serial port $chash->{IODevPort} registered for $chash->{NAME}";
  1426. return FRM_Serial_Setup($chash);
  1427. }
  1428. =item FRM_Serial_Setup
  1429. Returns:
  1430. 0: error
  1431. 1: OK
  1432. Description:
  1433. FRM internal function ($chash is FRM client that uses FRM as IODev)
  1434. =cut
  1435. sub FRM_Serial_Setup {
  1436. my ($chash) = @_;
  1437. if (FRM_is_firmata_connected($chash->{IODev})) {
  1438. my $firmata = FRM_Client_FirmataDevice($chash);
  1439. if (!defined $firmata ) {
  1440. Log3 $chash->{NAME},3,"$chash->{IODev}{NAME} Serial_Setup: no Firmata device available";
  1441. return 0;
  1442. }
  1443. # configure port by claiming pins, setting baud rate and start reading
  1444. my $port = $chash->{IODevPort};
  1445. if ($chash->{IODevParameters} =~ m/(\d+)(,([78])(,([NEO])(,([012]))?)?)?/) {
  1446. my $baudrate = $1;
  1447. if ($port > 7) {
  1448. # software serial port, get serial pins from attribute
  1449. my $err;
  1450. my $serialattr = AttrVal($chash->{IODev}{NAME}, "software-serial-config", undef);
  1451. if (defined $serialattr) {
  1452. my @a = split(":", $serialattr);
  1453. if (scalar @a == 3 && $a[0] == $port) {
  1454. $chash->{PIN_RX} = $a[1];
  1455. $chash->{PIN_TX} = $a[2];
  1456. # activate port
  1457. $firmata->serial_config($port, $baudrate, $a[1], $a[2]);
  1458. } else {
  1459. $err = "Error, invalid software-serial-config, must be <software serial port number>:<RX pin number>:<TX pin number>";
  1460. }
  1461. } else {
  1462. $err = "Error, attribute software-serial-config required for using software serial port $port";
  1463. }
  1464. if ($err) {
  1465. Log3 $chash->{NAME},2,"$chash->{IODev}{NAME}: $err";
  1466. return 0;
  1467. }
  1468. } else {
  1469. # hardware serial port, get serial pins by port number from capability metadata
  1470. my $rxPinType = 2*$port;
  1471. my $txPinType = $rxPinType + 1;
  1472. my $rxPin = undef;
  1473. my $txPin = undef;
  1474. foreach my $pin ( keys %{$firmata->{metadata}{serial_resolutions}} ) {
  1475. if ($firmata->{metadata}{serial_resolutions}{$pin} == $rxPinType) {
  1476. $rxPin = $pin;
  1477. }
  1478. if ($firmata->{metadata}{serial_resolutions}{$pin} == $txPinType) {
  1479. $txPin = $pin;
  1480. }
  1481. }
  1482. if (!defined $rxPin || !defined $txPin) {
  1483. Log3 $chash->{NAME},3,"$chash->{IODev}{NAME} Serial_Setup: serial pins of port $port not available on Arduino";
  1484. return 0;
  1485. }
  1486. $chash->{PIN_RX} = $rxPin;
  1487. $chash->{PIN_TX} = $txPin;
  1488. # activate port
  1489. $firmata->serial_config($port, $baudrate);
  1490. }
  1491. $firmata->observe_serial($port, \&FRM_serial_observer, $chash->{IODev});
  1492. $firmata->serial_read($port, 0); # continuously read and send all available bytes
  1493. Log3 $chash->{NAME},5,"$chash->{IODev}{NAME} Serial_Setup: serial port $chash->{IODevPort} opened with $baudrate baud for $chash->{NAME}";
  1494. } else {
  1495. Log3 $chash->{NAME},3,"$chash->{IODev}{NAME} Serial_Setup: invalid baudrate definition $chash->{IODevParameters} for port $port by $chash->{NAME}";
  1496. return 0;
  1497. }
  1498. }
  1499. return 1;
  1500. }
  1501. =item FRM_Serial_Write
  1502. Returns:
  1503. number of bytes written
  1504. Description:
  1505. FHEM module IOWriteFn for FRM client that uses FRM as IODev ($chash is FRM client)
  1506. =cut
  1507. sub FRM_Serial_Write {
  1508. my ($chash, $msg) = @_;
  1509. my $firmata = FRM_Client_FirmataDevice($chash);
  1510. my $port = $chash->{IODevPort};
  1511. return 0 unless ( defined $firmata and defined $port );
  1512. if (FRM_is_firmata_connected($chash->{IODev}) && defined($msg)) {
  1513. my @data = unpack("C*", $msg);
  1514. #my $size = scalar(@data);
  1515. #Log3 $chash->{NAME},3,"$chash->{IODev}{NAME} Serial_Write: $size bytes on serial port $chash->{IODevPort} $msg by $chash->{NAME}";
  1516. $firmata->serial_write($port, @data);
  1517. return length($msg);
  1518. } else {
  1519. return 0;
  1520. }
  1521. }
  1522. =item FRM_Serial_Close
  1523. Returns:
  1524. 0: error
  1525. 1: OK
  1526. Description:
  1527. FHEM module IOCloseFn for FRM client that uses FRM as IODev ($chash is FRM client)
  1528. =cut
  1529. sub FRM_Serial_Close {
  1530. my ($chash) = @_;
  1531. my $port = $chash->{IODevPort};
  1532. return 0 unless ( defined $port );
  1533. if (FRM_is_firmata_connected($chash->{IODev})) {
  1534. my $firmata = FRM_Client_FirmataDevice($chash);
  1535. $firmata->serial_stopreading($port);
  1536. }
  1537. delete $chash->{PIN_RX};
  1538. delete $chash->{PIN_TX};
  1539. delete $chash->{IODev}{SERIAL}{$chash->{IODevPort}};
  1540. #Log3 $chash->{NAME},5,"$chash->{IODev}{NAME} Serial_Close: serial port $chash->{IODevPort} unregistered for $chash->{NAME}";
  1541. return 1;
  1542. }
  1543. 1;
  1544. =pod
  1545. CHANGES
  1546. 18.12.2015 jensb
  1547. o added sub FRM_is_firmata_connected
  1548. - extended connection check including {FirmataDevice}->{io} (gets
  1549. deleted by FRM_FirmataDevice_Close on TCP disconnect while FHEM
  1550. has still a valid reference to {FirmataDevice} when calling
  1551. I2CWrtFn)
  1552. o modified sub FRM_Set, FRM_Get, FRM_I2C_Write, FRM_poll:
  1553. - use sub FRM_is_firmata_connected to check if Firmata is still
  1554. connected before performing IO operations (to prevent FHEM crash)
  1555. o modified sub FRM_Tcp_Connection_Close:
  1556. - set STATE to listening and delete SocketDevice (to present same
  1557. idle state as FRM_Start)
  1558. o help updated
  1559. 22.12.2015 jensb
  1560. o modified sub FRM_DoInit:
  1561. - clear internal readings (device may have changed)
  1562. o added serial pin support
  1563. 05.01.2016 jensb
  1564. o modified FRM_DoInit:
  1565. - do not disconnect DevIo in TCP mode to stay reconnectable
  1566. o use readingsSingleUpdate on state instead of directly changing STATE
  1567. 26.03.2016 jensb
  1568. o asynchronous device setup (FRM_DoInit, FRM_SetupDevice)
  1569. o experimental reconnect detection
  1570. o new attribute to skip device reset on connect
  1571. o help updated
  1572. 31.12.2016 jensb
  1573. o I2C read error detection
  1574. - modified FRM_I2C_Write: delete internal I2C_ERROR reading before performing I2C read operation to detect read errors
  1575. - modified FRM_string_observer: assign Firmata message to internal I2C_ERROR reading when string starts with "I2C"
  1576. - modified FRM_i2c_update_device: assign internal I2C_ERROR to XXX_SENDSTAT of IODev if defined after performing a I2C read
  1577. 27.12.2017 JB
  1578. o I2C write parameter validation
  1579. - modified FRM_I2C_Write: prevent processing if parameters are undefined
  1580. 01.01.2018 JB
  1581. o OWX support
  1582. - modified FRM_Client_AssignIOPort: use already assigned IODev
  1583. 03.01.2018 JB
  1584. o show capability "pullup" as internal "pullup_pins"
  1585. o show version of perl-firmata driver as internal "DRIVER_VERSION"
  1586. 04.01.2018 JB
  1587. o fix capability query for Firmata firmware without AnalogInputFirmata
  1588. o new attribute "disable" and new state "disabled"
  1589. 07.01.2018 JB
  1590. o new attribute "errorExclude" and new reading "stringMessage"
  1591. 10.01.2018 JB
  1592. o new states "defined" and "connected"
  1593. 13.01.2018 JB
  1594. o commented, formatted and refactored
  1595. =cut
  1596. =pod
  1597. =item device
  1598. =item summary Firmata device gateway
  1599. =item summary_DE Firmata Gateway
  1600. =begin html
  1601. <a name="FRM"></a>
  1602. <h3>FRM</h3>
  1603. <ul>
  1604. This module enables FHEM to communicate with a device that implements the <a href="http://www.firmata.org">Firmata</a> protocol
  1605. (e.g. an <a href="http://www.arduino.cc">Arduino</a>).<br><br>
  1606. The connection between FHEM and the Firmata device can be established by serial port, USB, LAN or WiFi.<br><br>
  1607. A single FRM device can serve multiple FRM clients from this list:<br><br>
  1608. <a href="#FRM_IN">FRM_IN</a>,
  1609. <a href="#FRM_OUT">FRM_OUT</a>,
  1610. <a href="#FRM_AD">FRM_AD</a>,
  1611. <a href="#FRM_PWM">FRM_PWM</a>,
  1612. <a href="#FRM_I2C">FRM_I2C</a>,
  1613. <a href="#FRM_SERVO">FRM_SERVO</a>,
  1614. <a href="#FRM_RGB">FRM_RGB</a>,
  1615. <a href="#FRM_ROTENC">FRM_ROTENC</a>,
  1616. <a href="#FRM_STEPPER">FRM_STEPPER</a>,
  1617. <a href="#FRM_LCD">FRM_LCD</a>,
  1618. <a href="#OWX">OWX</a>,
  1619. <a href="#I2C_LCD">I2C_LCD</a>,
  1620. <a href="#I2C_DS1307">I2C_DS1307</a>,
  1621. <a href="#I2C_PCA9532">I2C_PCA9532</a>,
  1622. <a href="#I2C_PCA9685">I2C_PCA9685</a>,
  1623. <a href="#I2C_PCF8574">I2C_PCF8574</a>,
  1624. <a href="#I2C_MCP23008">I2C_MCP23008</a>,
  1625. <a href="#I2C_MCP23017">I2C_MCP23017</a>,
  1626. <a href="#I2C_MCP342x">I2C_MCP342x</a>,
  1627. <a href="#I2C_SHT21">I2C_SHT21</a>,
  1628. <a href="#I2C_SHT3x">I2C_SHT3x</a>,
  1629. <a href="#I2C_BME280">I2C_BME280</a>,
  1630. <a href="#I2C_BMP180">I2C_BMP180</a>,
  1631. <a href="#I2C_BH1750">I2C_BH1750</a>,
  1632. <a href="#I2C_TSL2561">I2C_TSL2561</a>,
  1633. <a href="#I2C_K30">I2C_K30</a>,
  1634. <a href="#I2C_LM75A">I2C_LM75A</a><br><br>
  1635. Each client stands for a pin of the Firmata device configured for a specific use
  1636. (digital/analog in/out) or an integrated circuit connected to Firmata device by I2C.<br><br>
  1637. Note: This module is based on the Perl module <a href="https://github.com/jnsbyr/perl-firmata">Device::Firmata</a>
  1638. (perl-firmata). A suitable version of perl-firmata is distributed with FHEM (see subdirectory FHEM/lib/Device/Firmata).
  1639. You can download the latest version of perl-firmata <a href="https://github.com/jnsbyr/perl-firmata/archive/master.zip">
  1640. as a single zip</a> file from github.<br><br>
  1641. Note: This module may require the Device::SerialPort or Win32::SerialPort module if you attach the device via serial port
  1642. or USB and the operating system sets unsuitable default parameters for serial devices.<br><br>
  1643. <a name="FRMdefine"></a>
  1644. <b>Define</b><br><br>
  1645. <code>define &lt;name&gt; FRM {&lt;device&gt; | &lt;port&gt; [global]}</code> <br><br>
  1646. <ul>
  1647. <li>serial and USB connected devices:<br><br>
  1648. <code>&lt;device&gt;</code> specifies the serial port to communicate with the Firmata device.
  1649. The name of the serial-device depends on your distribution, under
  1650. linux the cdc_acm kernel module is responsible, and usually a
  1651. /dev/ttyACM0 device will be created. If your distribution does not have a
  1652. cdc_acm module, you can force usbserial to handle the Firmata device by the
  1653. following command:<br>
  1654. <code>modprobe usbserial vendor=0x03eb product=0x204b</code></br>
  1655. In this case the device is most probably /dev/ttyUSB0.<br><br>
  1656. You can also specify a baudrate if the device name contains the @
  1657. character, e.g.: /dev/ttyACM0@38400<br><br>
  1658. If the baudrate is "directio" (e.g.: /dev/ttyACM0@directio), then the
  1659. perl module Device::SerialPort is not needed, and FHEM opens the device
  1660. with simple file io. This might work if the operating system uses the same
  1661. defaults for the serial parameters as the Firmata device, e.g. some Linux
  1662. distributions and OSX.<br><br>
  1663. An Arduino compatible device should either use 'StandardFirmata' or 'ConfigurableFirmata' without NetworkFirmata.
  1664. </li><br>
  1665. <li>network connected devices:<br><br>
  1666. <code>&lt;port&gt;</code> specifies the port the FRM device listens on. If <code>global</code> is
  1667. specified the socket is bound to all local IP addresses, otherwise to localhost
  1668. only.<br><br>
  1669. The connection is initiated by the Firmata device in client-mode. Therefore the IP address and port
  1670. of the FHEM server has to be configured in the Firmata device, so it knows where to connect to.<br>
  1671. For multiple Firmata you need separate FRM devices configured to different ports.<br><br>
  1672. An Arduino compatible device should run one of 'StandardFirmataEthernet', 'StandardFirmataWiFi',
  1673. 'ConfigurableFirmata' with NetworkFirmata or 'ConfigurableFirmataWiFi'.
  1674. </li><br>
  1675. <li>no device:<br><br>
  1676. If <code>&lt;device&gt;</code> is set to <code>none</code>, no connection will be opened and you
  1677. can experiment without hardware attached.<br>
  1678. </li>
  1679. </ul>
  1680. <br>
  1681. StandardFirmata supports digital and analog-I/O, servos and I2C. In addition to that ConfigurableFirmata supports 1-Wire and stepper motors.<br><br>
  1682. You can find StandardFirmata, StandardFirmataEthernet and StandardFirmataWiFi in the Arduino IDE in the menu 'File->Examples->Firmata'<br><br>
  1683. <a href="https://github.com/firmata/arduino/tree/configurable/examples/ConfigurableFirmata">ConfigurableFirmata</a>
  1684. can be installed using the library manager of the Arduino IDE.<br><br>
  1685. Further information can be found at the FRM client devices listed above and the
  1686. <a href="http://www.fhemwiki.de/wiki/Arduino_Firmata#Installation_ConfigurableFirmata">FHEM-Wiki</a>.<br><br>
  1687. <a name="FRMset"></a>
  1688. <b>Set</b>
  1689. <ul>
  1690. <li>
  1691. <code>set &lt;name&gt; reinit</code><br>
  1692. reinitializes the FRM client devices attached to this FRM device
  1693. </li><br>
  1694. <li>
  1695. <code>set &lt;name&gt; reset</code><br>
  1696. performs a software reset on the Firmata device and disconnects form the Firmata device - after the Firmata device reconnects the attached FRM client devices are reinitialized
  1697. </li>
  1698. </ul>
  1699. <br><br>
  1700. <a name="FRMattr"></a>
  1701. <b>Attributes</b><br>
  1702. <ul>
  1703. <li>resetDeviceOnConnect {0|1}, default: 1<br>
  1704. Reset the Firmata device immediately after connect to force default Firmata startup state:
  1705. All pins with analog capability are configured as input, all other (digital) pins are configured as output
  1706. and the input pin reporting, the I2C configuration and the serial port configuration are cancelled and will
  1707. be reinitialized.
  1708. </li><br>
  1709. <li>i2c-config &lt;write-read-delay&gt;, no default<br>
  1710. Configure the Arduino for ic2 communication. Definig this attribute will enable I2C on all
  1711. i2c_pins received by the capability-query issued during initialization of FRM.<br>
  1712. As of Firmata 2.3 you can set a delay-time (in microseconds, max. 32767, default: 0) that will be
  1713. inserted into I2C protocol when switching from write to read. This may be necessary because Firmata
  1714. I2C write does not block on the FHEM side so consecutive I2C write/read operations get queued and
  1715. will be executed on the Firmata device in a different time sequence. Use the maximum operation
  1716. time required by the connected I2C devices (e.g. 30000 for the BMP180 with triple oversampling,
  1717. see I2C device manufacturer documentation for details).<br>
  1718. See: <a href="http://www.firmata.org/wiki/Protocol#I2C">Firmata Protocol details about I2C</a>
  1719. </li><br>
  1720. <li>sampling-interval &lt;interval&gt;, default: 1000 ms<br>
  1721. Configure the interval Firmata reports analog data to FRM (in milliseconds, max. 32767).<br>
  1722. This interval applies to the operation of <a href="#FRM_I2C">FRM_I2C</a>.<br>
  1723. See: <a href="http://www.firmata.org/wiki/Protocol#Sampling_Interval">Firmata Protocol details about Sampling Interval</a>
  1724. </li><br>
  1725. <li>software-serial-config &lt;port&gt;:&lt;rx pin&gt;:&lt;tx pin&gt;, no default<br>
  1726. For using a software serial port (port number 8, 9, 10 or 11) two I/O pins must be specified.
  1727. The rx pin must have interrupt capability and the tx pin must have digital output capability.<br>
  1728. See: <a href="https://www.arduino.cc/en/Reference/SoftwareSerial">Arduino SoftwareSerial Library</a>
  1729. </li><br>
  1730. <li>errorExclude &lt;regexp&gt;, no default<br>
  1731. If set will exclude a string message received from the Firmata device that matches the given regexp
  1732. from being logged at verbose=3 and will assign the data to the reading <i>stringMessage</i> instead
  1733. of <i>error</i>. Logging will still be done at verbose=5.
  1734. </li><br>
  1735. <li>disable {0|1}, default: 0<br>
  1736. Disables this devices if set to 1.
  1737. </li>
  1738. </ul>
  1739. <br><br>
  1740. <a name="FRMreadings"></a>
  1741. <b>Readings</b><br>
  1742. <ul>
  1743. <li>state<br>
  1744. Possible values are: <i>defined | disabled</i> and depending on the connection type:<br>
  1745. serial: <i>opened | connected | Initialized | disconnected</i><br>
  1746. network: <i>listening | connected | Initialized</i>
  1747. </li><br>
  1748. <li>error<br>
  1749. Data of last string message received from Firmata device, typically (but not necessarily) an error of the last operation.
  1750. Data prefixed with <i>I2C</i> will additionally be assigned to the internal reading <i>I2C_ERROR</i>.
  1751. </li><br>
  1752. <li>stringMessage<br>
  1753. Data of last string message received from Firmata device that matches attribute <i>errorExclude</i>.
  1754. </li>
  1755. </ul>
  1756. <br><br>
  1757. <a name="FRMinternals"></a>
  1758. <b>Internals</b><br>
  1759. <ul>
  1760. <li><code>DRIVER_VERSION</code><br>
  1761. Version of the Perl module Device::Firmata (perl-firmata), should be 0.63 or higher.
  1762. </li><br>
  1763. <li><code>protocol_version</code><br>
  1764. Firmata protocol version reported by the Firmata device.
  1765. </li><br>
  1766. <li><code>firmware</code><br>
  1767. Firmata firmware name reported by the Firmata device (this is typically the Arduino project name).
  1768. </li><br>
  1769. <li><code>firmware_version</code><br>
  1770. Firmata firmware version reported by the Firmata device.
  1771. </li><br>
  1772. <li><code>xxx_pins | xxx_resolutions | xxx_ports</code><br>
  1773. Pin capability reported by the Firmata device, where <code>xxx</code> can be one of the following:
  1774. <ul>
  1775. <li><code>input | pullup:</code> digital input, see <a href="#FRM_IN">FRM_IN</a></li>
  1776. <li><code>output:</code> digital output, see <a href="#FRM_OUT">FRM_OUT</a></li>
  1777. <li><code>analog:</code> analog input with ADC of given resolution, see <a href="#FRM_AD">FRM_AD</a></li>
  1778. <li><code>pwm:</code> digital output with PWM capability with DAC of given resolution, see <a href="#FRM_PWM">FRM_PWM</a></li>
  1779. <li><code>servo:</code> analog output with servo capability of given resolution, see <a href="#FRM_SERVO">FRM_SERVO</a></li>
  1780. <li><code>i2c:</code> I2C compatible pin, FRM can be used as IODev of another FHEM I2C device</li>
  1781. <li><code>onewire:</code> OneWire compatible pin, FRM can be used as IODev of <a href="#OWX">OWX</a></li>
  1782. <li><code>stepper:</code> stepper output pin of given resolution, see <a href="#FRM_STEPPER">FRM_STEPPER</a></li>
  1783. <li><code>encoder:</code> rotary encoder input pin of given resolution, see <a href="#FRM_ROTENC">FRM_ROTENC</a></li>
  1784. <li><code>serial:</code> serial rx/tx pin of given port, FRM can be used as serial device of another FHEM device,
  1785. see <a href="#FRMnotes">notes</a>
  1786. </li>
  1787. </ul><br>
  1788. <i>Note:</i> A reported capability is a minimum requirement but does not guarantee that this pin function is
  1789. really available. Some reasons for this are (a) boards with same model name may have different hardware and
  1790. firmware revisions with different hardwired special functions for specific pins and (b) a pin function may
  1791. depend on the Arduino platform and specific library version. When something does not work on the fist pin
  1792. you try and you can rule out a wiring problem try some other pins or try to find manufacturer documentation.
  1793. </li>
  1794. </ul>
  1795. <br><br>
  1796. <a name="FRMnotes"></a>
  1797. <b>Notes</b><br>
  1798. <ul>
  1799. <li><code>Digital Pins</code><br>
  1800. WARNING: Stock Firmata has a notable default: At the end of the initialization phase of the Firmata device
  1801. after boot or reset and before a host connection can be established all pins with analog input capability will be configured
  1802. as analog input and all pins with "only" digial I/O capability are configured as outputs and set to off. ConfigurableFirmata
  1803. is a little bit more selective in this respect and will only do this if you enable AnalogInputFirmata or DigitalOutputFirmata
  1804. respectively. If your board has a pin without analog capability and you have wired this pin as a digital input this behaviour
  1805. might damage your circuit. CPUs typically set input mode for GPIO pins when resetting to prevent this.<br><br>
  1806. You should look for the function "<code>void systemResetCallback()</code>" in your Firmata sketch and change
  1807. "<code>Firmata.setPinMode(i, OUTPUT);</code>" to "<code>Firmata.setPinMode(i, INPUT);</code>" to get a save initial state or
  1808. completely customize the default state of each pin according to your needs by replacing the Firmata reset code.
  1809. </li><br>
  1810. <li><code>Serial Ports</code><br>
  1811. A serial device can be connected to a serial port of a network connected Firmata device instead of being directly connected
  1812. to your FHEM computer. This way the Firmata device will become a serial over LAN (SOL) adapter. To use such a remote serial
  1813. port in other FHEM modules you need to set the serial device descriptor to:<br><br>
  1814. <code>FHEM:DEVIO:&lt;FRM device name&gt;:&lt;serial port&gt;@&lt;baud rate&gt;</code><br><br>
  1815. To use a serial port both the RX and TX pin of this port must be available via Firmata, even if one of the pins will not be used.
  1816. Depending on the Firmata version the first hardware serial port (port 0) cannot be used even with network connected
  1817. devices because port 0 is still reserved for the Arduino host communication.
  1818. On some Arduinos you can use software serial ports (ports 8 to 11). FRM supports a maximum of one software serial port that can
  1819. be activated using the software-serial-config attribute.<br><br>
  1820. In current Firmata versions serial setup options (data bits, parity, stop bits) cannot be configured but may be compiled into the
  1821. Firmata Firmware (see function "<code>((HardwareSerial*)serialPort)->begin(baud, options)</code>" in SerialFirmata.cpp of the
  1822. Firmata library).<br><br>
  1823. Not all FHEM modules for serial devices are compatible with this mode of operation. It will not work if (1) the FHEM module requires
  1824. hardware handshake or direct IO of serial pin like CTS or DTR or (2) the FHEM module does not support the syntax of serial device
  1825. descriptor (e.g. the <a href="#HEATRONIC">HEATRONIC</a> module works perfectly with a single line patch).
  1826. </li>
  1827. </ul>
  1828. </ul>
  1829. <br>
  1830. =end html
  1831. =cut