21_OWMULTI.pm 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457
  1. ########################################################################################
  2. #
  3. # OWMULTI.pm
  4. #
  5. # FHEM module to commmunicate with 1-Wire chip DS2438Z - Smart Battery Monitor
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. # Norbert Truchsess
  9. #
  10. # $Id: 21_OWMULTI.pm 15339 2017-10-29 08:14:07Z phenning $
  11. #
  12. ########################################################################################
  13. #
  14. # This programm is free software; you can redistribute it and/or modify
  15. # it under the terms of the GNU General Public License as published by
  16. # the Free Software Foundation; either version 2 of the License, or
  17. # (at your option) any later version.
  18. #
  19. # The GNU General Public License can be found at
  20. # http://www.gnu.org/copyleft/gpl.html.
  21. # A copy is found in the textfile GPL.txt and important notices to the license
  22. # from the author is found in LICENSE.txt distributed with these scripts.
  23. #
  24. # This script is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. # GNU General Public License for more details.
  28. #
  29. ########################################################################################
  30. package main;
  31. use vars qw{%attr %defs %modules $readingFnAttributes $init_done};
  32. use strict;
  33. use warnings;
  34. #add FHEM/lib to @INC if it's not already included. Should rather be in fhem.pl than here though...
  35. BEGIN {
  36. if (!grep(/FHEM\/lib$/,@INC)) {
  37. foreach my $inc (grep(/FHEM$/,@INC)) {
  38. push @INC,$inc."/lib";
  39. };
  40. };
  41. };
  42. use ProtoThreads;
  43. no warnings 'deprecated';
  44. sub Log($$);
  45. my $owx_version="7.01";
  46. #-- flexible channel name
  47. my ($owg_channel,$owg_schannel);
  48. my %gets = (
  49. "id" => ":noArg",
  50. "reading" => ":noArg",
  51. "temperature" => ":noArg",
  52. "VDD" => ":noArg",
  53. "raw" => ":noArg",
  54. "version" => ":noArg"
  55. );
  56. my %sets = (
  57. "interval" => "",
  58. );
  59. my %updates = (
  60. "present" => "",
  61. "reading" => "",
  62. );
  63. ########################################################################################
  64. #
  65. # The following subroutines are independent of the bus interface
  66. #
  67. # Prefix = OWMULTI
  68. #
  69. ########################################################################################
  70. #
  71. # OWMULTI_Initialize
  72. #
  73. # Parameter hash = hash of device addressed
  74. #
  75. ########################################################################################
  76. sub OWMULTI_Initialize ($) {
  77. my ($hash) = @_;
  78. $hash->{DefFn} = "OWMULTI_Define";
  79. $hash->{UndefFn} = "OWMULTI_Undef";
  80. $hash->{GetFn} = "OWMULTI_Get";
  81. $hash->{SetFn} = "OWMULTI_Set";
  82. $hash->{NotifyFn}= "OWMULTI_Notify";
  83. $hash->{InitFn} = "OWMULTI_Init";
  84. $hash->{AttrFn} = "OWMULTI_Attr";
  85. #tempOffset = a temperature offset added to the temperature reading for correction
  86. #tempUnit = a unit of measure: C/F/K
  87. $hash->{AttrList}= "IODev do_not_notify:0,1 showtime:0,1 model:DS2438 verbose:0,1,2,3,4,5 ".
  88. "tempOffset tempUnit:Celsius,Fahrenheit,Kelvin ".
  89. "VName VUnit VFunction WName WUnit WFunction ".
  90. "interval ".
  91. $readingFnAttributes;
  92. #-- temperature and voltage globals - always the raw values from the device
  93. $hash->{owg_val}->[0] = undef;
  94. $hash->{owg_val}->[1] = undef;
  95. $hash->{owg_val}->[2] = undef;
  96. $hash->{owg_val}->[3] = undef;
  97. #-- make sure OWX is loaded so OWX_CRC is available if running with OWServer
  98. main::LoadModule("OWX");
  99. }
  100. #######################################################################################
  101. #
  102. # OWMULTI_Attr - Set one attribute value for device
  103. #
  104. # Parameter hash = hash of device addressed
  105. # a = argument array
  106. #
  107. ########################################################################################
  108. sub OWMULTI_Attr(@) {
  109. my ($do,$name,$key,$value) = @_;
  110. my $hash = $defs{$name};
  111. my $ret;
  112. if ( $do eq "set") {
  113. ARGUMENT_HANDLER: {
  114. #-- interval modified at runtime
  115. $key eq "interval" and do {
  116. #-- check value
  117. return "OWMULTI: set $name interval must be >= 0" if(int($value) < 0);
  118. #-- update timer
  119. $hash->{INTERVAL} = int($value);
  120. if ($init_done) {
  121. RemoveInternalTimer($hash);
  122. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0);
  123. }
  124. last;
  125. };
  126. $key eq "IODev" and do {
  127. AssignIoPort($hash,$value);
  128. if( defined($hash->{IODev}) ) {
  129. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0;
  130. if ($init_done) {
  131. OWMULTI_Init($hash);
  132. }
  133. }
  134. last;
  135. }
  136. }
  137. }
  138. return $ret;
  139. }
  140. ########################################################################################
  141. #
  142. # OWMULTI_Define - Implements DefFn function
  143. #
  144. # Parameter hash = hash of device addressed, def = definition string
  145. #
  146. ########################################################################################
  147. sub OWMULTI_Define ($$) {
  148. my ($hash, $def) = @_;
  149. # define <name> OWMULTI [<model>] <id> [interval]
  150. # e.g.: define flow OWMULTI 525715020000 300
  151. my @a = split("[ \t][ \t]*", $def);
  152. my ($name,$model,$fam,$id,$crc,$interval,$ret);
  153. my $tn = TimeNow();
  154. #-- default
  155. $name = $a[0];
  156. $interval = 300;
  157. $ret = "";
  158. #-- check syntax
  159. return "OWMULTI: Wrong syntax, must be define <name> OWMULTI [<model>] <id> [interval]"
  160. if(int(@a) < 2 || int(@a) > 6);
  161. #-- different types of definition allowed
  162. my $a2 = $a[2];
  163. my $a3 = defined($a[3]) ? $a[3] : "";
  164. #-- no model, 12 characters
  165. if( $a2 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  166. $model = "DS2438";
  167. $fam = "26";
  168. $id = $a[2];
  169. if(int(@a)>=4) { $interval = $a[3]; }
  170. CommandAttr (undef,"$name model DS2438");
  171. #-- no model, 2+12 characters
  172. } elsif( $a2 =~ m/^[0-9|a-f|A-F]{2}\.[0-9|a-f|A-F]{12}$/ ) {
  173. $fam = substr($a[2],0,2);
  174. $id = substr($a[2],3);
  175. if(int(@a)>=4) { $interval = $a[3]; }
  176. if( $fam eq "26" ){
  177. $model = "DS2438";
  178. CommandAttr (undef,"$name model DS2438");
  179. }else{
  180. return "OWMULTI: Wrong 1-Wire device family $fam";
  181. }
  182. #-- model, 12 characters
  183. } elsif( $a3 =~ m/^[0-9|a-f|A-F]{12}$/ ) {
  184. $model = $a[2];
  185. $id = $a[3];
  186. if(int(@a)>=5) { $interval = $a[4]; }
  187. if( $model eq "DS2438" ){
  188. $fam = "26";
  189. CommandAttr (undef,"$name model DS2438");
  190. }else{
  191. return "OWMULTI: Wrong 1-Wire device model $model";
  192. }
  193. } else {
  194. return "OWMULTI: $a[0] ID $a[2] invalid, specify a 12 or 2.12 digit value";
  195. }
  196. #-- determine CRC Code - only if this is a direct interface
  197. $crc = sprintf("%02x",OWX_CRC($fam.".".$id."00"));
  198. #-- define device internals
  199. $hash->{OW_ID} = $id;
  200. $hash->{OW_FAMILY} = $fam;
  201. $hash->{PRESENT} = 0;
  202. $hash->{ERRCOUNT} = 0;
  203. $hash->{ROM_ID} = "$fam.$id.$crc";
  204. $hash->{INTERVAL} = $interval;
  205. #-- Couple to I/O device
  206. AssignIoPort($hash);
  207. if( !defined($hash->{IODev}) or !defined($hash->{IODev}->{NAME}) ){
  208. return "OWMULTI: Warning, no 1-Wire I/O device found for $name.";
  209. } else {
  210. $hash->{ASYNC} = $hash->{IODev}->{TYPE} eq "OWX_ASYNC" ? 1 : 0; #-- false for now
  211. }
  212. $main::modules{OWMULTI}{defptr}{$id} = $hash;
  213. #--
  214. readingsSingleUpdate($hash,"state","defined",1);
  215. Log 3, "OWMULTI: Device $name defined.";
  216. $hash->{NOTIFYDEV} = "global";
  217. if ($init_done) {
  218. OWMULTI_Init($hash);
  219. }
  220. return undef;
  221. }
  222. ########################################################################################
  223. #
  224. # OWMULTI_Notify - implements the Notify Function
  225. #
  226. # Parameter hash = hash of device addressed
  227. #
  228. ########################################################################################
  229. sub OWMULTI_Notify ($$) {
  230. my ($hash,$dev) = @_;
  231. if( grep(m/^(INITIALIZED|REREADCFG)$/, @{$dev->{CHANGED}}) ) {
  232. OWMULTI_Init($hash);
  233. } elsif( grep(m/^SAVE$/, @{$dev->{CHANGED}}) ) {
  234. }
  235. }
  236. ########################################################################################
  237. #
  238. # OWMULTI_Init - implements the Init function
  239. #
  240. # Parameter hash = hash of device addressed
  241. #
  242. ########################################################################################
  243. sub OWMULTI_Init ($) {
  244. my ($hash)=@_;
  245. #-- Start timer for updates
  246. RemoveInternalTimer($hash);
  247. InternalTimer(gettimeofday()+10, "OWMULTI_GetValues", $hash, 0);
  248. return undef;
  249. }
  250. ########################################################################################
  251. #
  252. # OWMULTI_ChannelNames - find the real channel names
  253. #
  254. # Parameter hash = hash of device addressed
  255. #
  256. ########################################################################################
  257. sub OWMULTI_ChannelNames($) {
  258. my ($hash) = @_;
  259. my $name = $hash->{NAME};
  260. my $state = $hash->{READINGS}{"state"}{VAL};
  261. my ($cname,@cnama,$unit);
  262. my ($tunit,$toffset,$tfactor,$tabbr,$vfunc,$wfunc);
  263. #-- Set channel name, channel unit for voltage channel
  264. $cname = defined($attr{$name}{"VName"}) ? $attr{$name}{"VName"} : "voltage|vad";
  265. @cnama = split(/\|/,$cname);
  266. if( int(@cnama)!=2){
  267. push(@cnama,$cnama[0]);
  268. }
  269. #-- unit
  270. $unit = defined($attr{$name}{"VUnit"}) ? $attr{$name}{"VUnit"} : "V";
  271. $unit = ""
  272. if($unit eq "none");
  273. #-- put into readings
  274. $owg_channel = $cnama[0];
  275. $hash->{READINGS}{$owg_channel}{VAL} = " ";
  276. $hash->{READINGS}{$owg_channel}{ABBR} = $cnama[1];
  277. $hash->{READINGS}{$owg_channel}{UNIT} = " ".$unit;
  278. #-- Set channel name, channel unit for sense channel
  279. $cname = defined($attr{$name}{"WName"}) ? $attr{$name}{"WName"} : "sense|s";
  280. @cnama = split(/\|/,$cname);
  281. if( int(@cnama)!=2){
  282. push(@cnama,$cnama[0]);
  283. }
  284. #-- unit
  285. $unit = defined($attr{$name}{"WUnit"}) ? $attr{$name}{"WUnit"} : "V";
  286. if($unit eq "none"){
  287. $unit = ""
  288. }else{
  289. $unit = " ".$unit
  290. }
  291. #-- put into readings
  292. $owg_schannel = $cnama[0];
  293. $hash->{READINGS}{$owg_schannel}{VAL} = " ";
  294. $hash->{READINGS}{$owg_schannel}{ABBR} = $cnama[1];
  295. $hash->{READINGS}{$owg_schannel}{UNIT} = $unit;
  296. #-- temperature scale
  297. $hash->{READINGS}{"temperature"}{UNIT} = defined($attr{$name}{"tempUnit"}) ? $attr{$name}{"tempUnit"} : "Celsius";
  298. $tunit = defined($attr{$name}{"tempUnit"}) ? $attr{$name}{"tempUnit"} : $hash->{READINGS}{"temperature"}{UNIT};
  299. $toffset = defined($attr{$name}{"tempOffset"}) ? $attr{$name}{"tempOffset"} : 0.0 ;
  300. $tfactor = 1.0;
  301. if( $tunit eq "none" ){
  302. $tabbr = "";
  303. }elsif( $tunit eq "Celsius" ){
  304. $tabbr = " °C";
  305. } elsif ($tunit eq "Kelvin" ){
  306. $tabbr = " K";
  307. $toffset += "273.16"
  308. } elsif ($tunit eq "Fahrenheit" ){
  309. $tabbr = " °F";
  310. $toffset = ($toffset+32)/1.8;
  311. $tfactor = 1.8;
  312. } else {
  313. $tabbr="?";
  314. Log 1, "OWMULTI_ChannelNames: unknown unit $tunit";
  315. }
  316. #-- these values are rather complex to obtain, therefore save them in the hash
  317. $hash->{READINGS}{"temperature"}{ABBR} = "T";
  318. $hash->{READINGS}{"temperature"}{UNIT} = $tabbr;
  319. $hash->{tempf}{offset} = $toffset;
  320. $hash->{tempf}{factor} = $tfactor;
  321. }
  322. ########################################################################################
  323. #
  324. # OWMULTI_FormatValues - put together various format strings
  325. #
  326. # Parameter hash = hash of device addressed, fs = format string
  327. #
  328. ########################################################################################
  329. sub OWMULTI_FormatValues($) {
  330. my ($hash) = @_;
  331. my $name = $hash->{NAME};
  332. my ($toffset,$tfactor,$tval,$vfunc,$wfunc,$vval,$wval);
  333. my $svalue = "";
  334. #-- no change in any value if invalid reading
  335. return if( ($hash->{owg_val}->[0] eq "") || ($hash->{owg_val}->[1] eq "") || ($hash->{owg_val}->[2] eq "") || ($hash->{owg_val}->[3] eq ""));
  336. #-- obtain channel names
  337. OWMULTI_ChannelNames($hash);
  338. #-- correct values for proper offset, factor
  339. $toffset = $hash->{tempf}{offset};
  340. $tfactor = $hash->{tempf}{factor};
  341. $tval = int(10*($hash->{owg_val}->[0] + $toffset)*$tfactor+0.5)/10;
  342. #-- attribute V/WFunction defined ?
  343. $vfunc = defined($attr{$name}{"VFunction"}) ? $attr{$name}{"VFunction"} : "V";
  344. $wfunc = defined($attr{$name}{"WFunction"}) ? $attr{$name}{"WFunction"} : "W";
  345. #-- replace by proper values
  346. $vfunc =~ s/VDD/\$hash->{owg_val}->[1]/g;
  347. $vfunc =~ s/V/\$hash->{owg_val}->[2]/g;
  348. $vfunc =~ s/W/\$hash->{owg_val}->[3]/g;
  349. $vfunc =~ s/T/\$tval/g;
  350. $wfunc =~ s/VDD/\$hash->{owg_val}->[1]/g;
  351. $wfunc =~ s/V/\$hash->{owg_val}->[2]/g;
  352. $wfunc =~ s/W/\$hash->{owg_val}->[3]/g;
  353. $wfunc =~ s/T/\$tval/g;
  354. #-- determine the measured value from the function
  355. $vfunc = "\$hash->{owg_val}->[1] = $hash->{owg_val}->[1]; \$hash->{owg_val}->[2] = $hash->{owg_val}->[2]; \$hash->{owg_val}->[3] = $hash->{owg_val}->[3]; \$tval = $tval; ".$vfunc;
  356. #Log 1, "vfunc= ".$vfunc;
  357. $vfunc = eval($vfunc);
  358. if( !$vfunc ){
  359. $vval = 0.0;
  360. } elsif( $vfunc ne "" ){
  361. $vval = int( $vfunc*100+0.5)/100;
  362. } else {
  363. $vval = "???";
  364. }
  365. $wfunc = "\$hash->{owg_val}->[1] = $hash->{owg_val}->[1]; \$hash->{owg_val}->[2] = $hash->{owg_val}->[2]; \$hash->{owg_val}->[3] = $hash->{owg_val}->[3]; \$tval = $tval; ".$wfunc;
  366. #Log 1, "wfunc= ".$wfunc;
  367. $wfunc = eval($wfunc);
  368. if( !$wfunc ){
  369. $wval = 0.0;
  370. } elsif( $wfunc ne "" ){
  371. $wval = int( $wfunc*100+0.5)/100;
  372. } else {
  373. $wval = "???";
  374. }
  375. #-- string buildup for return value, STATE
  376. $svalue .= sprintf( "%s: %5.2f%s (T: %5.1f%s %s: %5.2f%s)",
  377. $hash->{READINGS}{$owg_channel}{ABBR}, $vval,$hash->{READINGS}{$owg_channel}{UNIT},
  378. $tval,$hash->{READINGS}{"temperature"}{UNIT}, $hash->{READINGS}{$owg_schannel}{ABBR}, $wval,$hash->{READINGS}{$owg_schannel}{UNIT});
  379. #-- put into READINGS
  380. readingsBeginUpdate($hash);
  381. readingsBulkUpdate($hash,$owg_channel,$vval);
  382. readingsBulkUpdate($hash,$owg_schannel,$wval);
  383. readingsBulkUpdate($hash,"VDD",sprintf("%4.2f",$hash->{owg_val}->[1]));
  384. readingsBulkUpdate($hash,"temperature",$tval);
  385. #-- STATE
  386. readingsBulkUpdate($hash,"state",$svalue);
  387. readingsEndUpdate($hash,1);
  388. return $svalue;
  389. }
  390. ########################################################################################
  391. #
  392. # OWMULTI_Get - Implements GetFn function
  393. #
  394. # Parameter hash = hash of device addressed, a = argument array
  395. #
  396. ########################################################################################
  397. sub OWMULTI_Get($@) {
  398. my ($hash, @a) = @_;
  399. my $reading = $a[1];
  400. my $name = $hash->{NAME};
  401. my $model = $hash->{OW_MODEL};
  402. my $value = undef;
  403. my $ret = "";
  404. #-- check syntax
  405. return "OWMULTI: Get argument is missing @a"
  406. if(int(@a) != 2);
  407. #-- check argument
  408. my $msg = "OWMULTI: Get with unknown argument $a[1], choose one of ";
  409. $msg .= "$_$gets{$_} " foreach (keys%gets);
  410. return $msg
  411. if(!defined($gets{$a[1]}));
  412. #-- get id
  413. if($a[1] eq "id") {
  414. $value = $hash->{ROM_ID};
  415. return "$name.id => $value";
  416. }
  417. #-- hash of the busmaster
  418. my $master = $hash->{IODev};
  419. #-- Get other values according to interface type
  420. my $interface= $hash->{IODev}->{TYPE};
  421. #-- get version
  422. if( $a[1] eq "version") {
  423. return "$name.version => $owx_version";
  424. }
  425. #-- reset current ERRSTATE
  426. $hash->{ERRSTATE} = 0;
  427. #-- for the other readings we need a new reading
  428. #-- OWX interface
  429. if( $interface eq "OWX" ){
  430. #-- not different from getting all values ..
  431. $ret = OWXMULTI_GetValues($hash);
  432. }elsif( $interface eq "OWX_ASYNC"){
  433. eval {
  434. $ret = OWX_ASYNC_RunToCompletion($hash,OWXMULTI_PT_GetValues($hash));
  435. };
  436. $ret = GP_Catch($@) if $@;
  437. #-- OWFS interface
  438. }elsif( $interface eq "OWServer" ){
  439. $ret = OWFSMULTI_GetValues($hash);
  440. #-- Unknown interface
  441. }else{
  442. return "OWMULTI: Get with wrong IODev type $interface";
  443. }
  444. #-- process result
  445. if( $master->{ASYNCHRONOUS} ){
  446. #return "OWSMULTI: $name getting readings, please wait for completion";
  447. return undef;
  448. }else{
  449. if( defined($ret) ){
  450. return "OWMULTI: Could not get values from device $name, reason $ret";
  451. }
  452. #-- return the special reading
  453. if ($reading eq "reading") {
  454. return "OWMULTI: $name.reading => ".$hash->{READINGS}{"state"}{VAL};
  455. }
  456. if ($reading eq "temperature") {
  457. return "OWMULTI: $name.temperature => ".
  458. $hash->{READINGS}{"temperature"}{VAL};
  459. }
  460. if ($reading eq "VDD") {
  461. return "OWMULTI: $name.VDD => ".
  462. $hash->{owg_val}->[1];
  463. }
  464. if ( $reading eq "raw") {
  465. return "OWMULTI: $name.raw => ".
  466. $hash->{owg_val}->[2]." V ".$hash->{owg_val}->[3]." V";
  467. }
  468. }
  469. return undef;
  470. }
  471. #######################################################################################
  472. #
  473. # OWMULTI_GetValues - Updates the readings from device
  474. #
  475. # Parameter hash = hash of device addressed
  476. #
  477. ########################################################################################
  478. sub OWMULTI_GetValues($) {
  479. my $hash = shift;
  480. my $name = $hash->{NAME};
  481. my $value = "";
  482. my $ret = "";
  483. #-- check if device needs to be initialized
  484. OWMULTI_InitializeDevice($hash)
  485. if( $hash->{READINGS}{"state"}{VAL} eq "defined");
  486. RemoveInternalTimer($hash);
  487. #-- auto-update for device disabled;
  488. return undef
  489. if( $hash->{INTERVAL} == 0 );
  490. #-- restart timer for updates
  491. InternalTimer(time()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0);
  492. #-- reset current ERRSTATE
  493. $hash->{ERRSTATE} = 0;
  494. #-- Get values according to interface type
  495. my $interface= $hash->{IODev}->{TYPE};
  496. if( $interface eq "OWX" ){
  497. $ret = OWXMULTI_GetValues($hash);
  498. }elsif( $interface eq "OWX_ASYNC" ){
  499. eval {
  500. OWX_ASYNC_Schedule( $hash, OWXMULTI_PT_GetValues($hash) );
  501. };
  502. $ret = GP_Catch($@) if $@;
  503. }elsif( $interface eq "OWServer" ){
  504. $ret = OWFSMULTI_GetValues($hash);
  505. }else{
  506. return "OWMULTI: GetValues with wrong IODev type $interface";
  507. }
  508. #-- process results
  509. if( defined($ret) ){
  510. return "OWMULTI: Could not get values from device $name, reason $ret";
  511. }
  512. return undef;
  513. }
  514. ########################################################################################
  515. #
  516. # OWMULTI_InitializeDevice - delayed setting of initial readings and channel names
  517. #
  518. # Parameter hash = hash of device addressed
  519. #
  520. ########################################################################################
  521. sub OWMULTI_InitializeDevice($) {
  522. my ($hash) = @_;
  523. my $name = $hash->{NAME};
  524. #-- Initial readings
  525. $hash->{owg_val}->[0] = "";
  526. $hash->{owg_val}->[1] = "";
  527. $hash->{owg_val}->[2] = "";
  528. $hash->{owg_val}->[3] = "";
  529. #-- Set state to initialized
  530. readingsSingleUpdate($hash,"state","initialized",1);
  531. }
  532. #######################################################################################
  533. #
  534. # OWMULTI_Set - Set one value for device
  535. #
  536. # Parameter hash = hash of device addressed
  537. # a = argument string
  538. #
  539. ########################################################################################
  540. sub OWMULTI_Set($@) {
  541. my ($hash, @a) = @_;
  542. #-- for the selector: which values are possible
  543. return join(" ", sort keys %sets) if(@a == 2);
  544. #-- check syntax
  545. return "OWMULTI: Set needs one parameter"
  546. if(int(@a) != 3);
  547. #-- check argument
  548. return "OWMULTI: Set with unknown argument $a[1], choose one of ".join(",", sort keys %sets)
  549. if(!defined($sets{$a[1]}));
  550. #-- define vars
  551. my $key = $a[1];
  552. my $value = $a[2];
  553. my $ret = undef;
  554. my $name = $hash->{NAME};
  555. my $model = $hash->{OW_MODEL};
  556. #-- set new timer interval
  557. if($key eq "interval") {
  558. # check value
  559. return "OWMULTI: set $name interval must be >= 0"
  560. if(int($value) < 0);
  561. # update timer
  562. $hash->{INTERVAL} = int($value);
  563. RemoveInternalTimer($hash);
  564. InternalTimer(gettimeofday()+$hash->{INTERVAL}, "OWMULTI_GetValues", $hash, 0);
  565. return undef;
  566. }
  567. #-- set other values depending on interface type
  568. my $interface = $hash->{IODev}->{TYPE};
  569. my $offset = $hash->{tempf}{offset};
  570. my $factor = $hash->{tempf}{factor};
  571. #-- find upper and lower boundaries for given offset/factor
  572. my $mmin = (-55+$offset)*$factor;
  573. my $mmax = (125+$offset)*$factor;
  574. return sprintf("OWMULTI: Set with wrong value $value for $key, range is [%3.1f,%3.1f]",$mmin,$mmax)
  575. if($value < $mmin || $value > $mmax);
  576. #-- seems to be ok, put into the device
  577. $a[2] = int($value/$factor-$offset);
  578. #-- OWX interface
  579. if( $interface eq "OWX" ){
  580. $ret = OWXMULTI_SetValues($hash,@a);
  581. }elsif( $interface eq "OWX_ASYNC" ){
  582. eval {
  583. OWX_ASYNC_Schedule( $hash, OWXMULTI_PT_SetValues($hash,@a) );
  584. };
  585. $ret = GP_Catch($@) if $@;
  586. #-- OWFS interface
  587. }elsif( $interface eq "OWServer" ){
  588. $ret = OWFSMULTI_SetValues($hash,@a);
  589. } else {
  590. return "OWMULTI: Set with wrong IODev type $interface";
  591. }
  592. #-- process results
  593. if( defined($ret) ){
  594. return "OWMULTI: Could not set device $name, reason: ".$ret;
  595. }
  596. #-- process results - we have to reread the device
  597. OWMULTI_GetValues($hash);
  598. Log 4, "OWMULTI: Set $hash->{NAME} $key $value";
  599. return undef;
  600. }
  601. ########################################################################################
  602. #
  603. # OWMULTI_Undef - Implements UndefFn function
  604. #
  605. # Parameter hash = hash of device addressed
  606. #
  607. ########################################################################################
  608. sub OWMULTI_Undef ($) {
  609. my ($hash) = @_;
  610. delete($main::modules{OWMULTI}{defptr}{$hash->{OW_ID}});
  611. RemoveInternalTimer($hash);
  612. return undef;
  613. }
  614. ########################################################################################
  615. #
  616. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  617. # via OWFS
  618. #
  619. # Prefix = OWFSMULTI
  620. #
  621. ########################################################################################
  622. #
  623. # OWFSMULTI_GetValues - Get reading from one device
  624. #
  625. # Parameter hash = hash of device addressed
  626. #
  627. ########################################################################################
  628. sub OWFSMULTI_GetValues($) {
  629. my ($hash) = @_;
  630. #-- ID of the device
  631. my $owx_add = substr($hash->{ROM_ID},0,15);
  632. #-- hash of the busmaster
  633. my $master = $hash->{IODev};
  634. my $name = $hash->{NAME};
  635. #-- reset presence
  636. $hash->{PRESENT} = 0;
  637. #-- get values - or should we rather get the uncached ones ?
  638. $hash->{owg_val}->[0] = OWServer_Read($master,"/$owx_add/temperature");
  639. $hash->{owg_val}->[1] = OWServer_Read($master,"/$owx_add/VDD");
  640. $hash->{owg_val}->[2] = OWServer_Read($master,"/$owx_add/VAD");
  641. $hash->{owg_val}->[3] = OWServer_Read($master,"/$owx_add/vis");
  642. return "no return from OWServer"
  643. if( (!defined($hash->{owg_val}->[0])) || (!defined($hash->{owg_val}->[1])) || (!defined($hash->{owg_val}->[2])) || (!defined($hash->{owg_val}->[3])) );
  644. return "empty return from OWServer"
  645. if( ($hash->{owg_val}->[0] eq "") || ($hash->{owg_val}->[1] eq "") || ($hash->{owg_val}->[2] eq "") || ($hash->{owg_val}->[3] eq "") );
  646. #-- and now from raw to formatted values
  647. $hash->{PRESENT} = 1;
  648. my $value = OWMULTI_FormatValues($hash);
  649. return undef;
  650. }
  651. #######################################################################################
  652. #
  653. # OWFSMULTI_SetValues - Set values in device
  654. #
  655. # Parameter hash = hash of device addressed
  656. # a = argument array
  657. #
  658. ########################################################################################
  659. sub OWFSMULTI_SetValues($@) {
  660. my ($hash, @a) = @_;
  661. }
  662. ########################################################################################
  663. #
  664. # The following subroutines in alphabetical order are only for a 1-Wire bus connected
  665. # directly to the FHEM server
  666. #
  667. # Prefix = OWXMULTI
  668. #
  669. ########################################################################################
  670. #
  671. # OWXMULTI_BinValues - Process reading from one device - translate binary into raw
  672. #
  673. # Parameter hash = hash of device addressed
  674. # context = mode for evaluating the binary data
  675. # proc = processing instruction, also passed to OWX_Read.
  676. # bitwise interpretation !!
  677. # if 0, nothing special
  678. # if 1 = bit 0, a reset will be performed not only before, but also after
  679. # the last operation in OWX_Read
  680. # if 2 = bit 1, the initial reset of the bus will be suppressed
  681. # if 8 = bit 3, the fillup of the data with 0xff will be suppressed
  682. # if 16= bit 4, the insertion will be at the top of the queue
  683. # owx_dev = ROM ID of slave device
  684. # crcpart = part of the data that needs to be part of the CRC check
  685. # numread = number of bytes to receive
  686. # res = result string
  687. #
  688. #
  689. ########################################################################################
  690. sub OWXMULTI_BinValues($$$$$$$) {
  691. my ($hash, $context, $proc, $owx_dev, $crcpart, $numread, $res) = @_;
  692. #-- hash of the busmaster
  693. my $master = $hash->{IODev};
  694. my $name = $hash->{NAME};
  695. #-- inherit previous error
  696. my $error = $hash->{ERRSTATE};
  697. my @data = [];
  698. my ($value,$lsb,$msb,$sign);
  699. my $msg;
  700. OWX_WDBGL($name,4,"OWXMULTI_BinValues: called for device $name in context $context with data ",$res);
  701. #-- always check for success, unused are reset, numread
  702. return unless ($context =~ /^ds2438.getv[ad]d$/);
  703. #-- we have to get rid of the first 11 bytes
  704. if( length($res) == 20 ){
  705. $res=substr($res,11);
  706. }
  707. @data=split(//,$res);
  708. #-- process results
  709. if ((ord($data[0]) & 112)!=0) {
  710. $msg = "$name: conversion not complete or data invalid in context $context ";
  711. $error = 1;
  712. }elsif (OWX_CRC8(substr($res,0,8),$data[8]) eq "\0x00") {
  713. $msg = "$name: invalid CRC ";
  714. $error = 1;
  715. }else{
  716. $msg = "$name: no error, ";
  717. }
  718. OWX_WDBGL($name,5-4*$error,"OWXMULTI_BinValues: ".$msg,$res);
  719. #-- this must be different for the different device types
  720. # family = 26 => DS2438
  721. #-- transform binary rep of VDD
  722. if( $context eq "ds2438.getvdd") {
  723. #-- possible addtional check: $data[0] must be 08
  724. #-- temperature
  725. $lsb = ord($data[1]);
  726. $msb = ord($data[2]) & 127;
  727. $sign = ord($data[2]) & 128;
  728. #-- test with -55 degrees
  729. #$lsb = 0;
  730. #$sign = 1;
  731. #$msb = 73;
  732. #-- 2's complement form = signed bytes
  733. $hash->{owg_val}->[0] = $msb+ $lsb/256.;
  734. if( $sign !=0 ){
  735. $hash->{owg_val}->[0] = -128+$hash->{owg_val}->[0];
  736. }
  737. #-- voltage
  738. $lsb = ord($data[3]);
  739. $msb = ord($data[4]) & 3;
  740. #-- test with 5V
  741. #$lsb = 244;
  742. #$msb = 1;
  743. #-- supply voltage
  744. $hash->{owg_val}->[1] = ($msb*256+ $lsb)/100.;
  745. #-- transform binary rep of VAD
  746. }elsif( $context eq "ds2438.getvad") {
  747. #-- possible addtional check: $data[0] must be 08
  748. #-- voltage
  749. $lsb = ord($data[3]);
  750. $msb = ord($data[4]) & 3;
  751. #-- test with 7.2 V
  752. #$lsb = 208;
  753. #$msb = 2;
  754. #-- external voltage
  755. $hash->{owg_val}->[2] = ($msb*256+ $lsb)/100.;
  756. #-- current
  757. $lsb = ord($data[5]);
  758. $msb = ord($data[6]) & 3;
  759. #-- external current
  760. $hash->{owg_val}->[3] = ($msb*256.+ $lsb)/4096;
  761. #-- and now from raw to formatted values
  762. if( $error ){
  763. $hash->{ERRCOUNT}++;
  764. $hash->{ERRSTATE} = 1;
  765. }else{
  766. $hash->{PRESENT} = 1;
  767. OWMULTI_FormatValues($hash);
  768. }
  769. }
  770. return undef;
  771. }
  772. ########################################################################################
  773. #
  774. # OWXMULTI_GetValues - Get reading from one device
  775. #
  776. # Parameter hash = hash of device addressed
  777. # final= 1 if FormatValues is to be called
  778. #
  779. ########################################################################################
  780. sub OWXMULTI_GetValues($) {
  781. my ($hash) = @_;
  782. my ($res,$ret);
  783. #-- ID of the device
  784. my $owx_dev = $hash->{ROM_ID};
  785. #-- hash of the busmaster
  786. my $master = $hash->{IODev};
  787. #-- reset presence
  788. $hash->{PRESENT} = 0;
  789. #------------------------------------------------------------------------------------
  790. #-- switch the device to current measurement off, VDD only
  791. #-- issue the match ROM command \x55 and the write scratchpad command
  792. #-- OLD OWX interface
  793. if( !$master->{ASYNCHRONOUS} ){
  794. OWX_Reset($master);
  795. #if( OWX_Complex($master,$owx_dev,"\x4E\x00\x08",0) eq 0 ){
  796. if( OWX_Complex($master,$owx_dev,"\x4E\x00\x09",0) eq 0 ){
  797. return "$owx_dev write status failed";
  798. }
  799. #-- copy scratchpad to register
  800. #-- issue the match ROM command \x55 and the copy scratchpad command
  801. OWX_Reset($master);
  802. if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){
  803. return "$owx_dev copy scratchpad failed";
  804. }
  805. #-- initiate temperature conversion
  806. #-- conversion needs some 12 ms !
  807. #-- issue the match ROM command \x55 and the start conversion command
  808. OWX_Reset($master);
  809. if( OWX_Complex($master,$owx_dev,"\x44",0) eq 0 ){
  810. return "$owx_dev temperature conversion failed";
  811. }
  812. select(undef,undef,undef,0.012);
  813. #-- initiate voltage conversion
  814. #-- conversion needs some 6 ms !
  815. #-- issue the match ROM command \x55 and the start conversion command
  816. OWX_Reset($master);
  817. if( OWX_Complex($master,$owx_dev,"\xB4",0.01) eq 0 ){
  818. return "$owx_dev voltage conversion failed";
  819. }
  820. select(undef,undef,undef,0.006);
  821. #-- from memory to scratchpad
  822. #-- copy needs some 12 ms !
  823. #-- issue the match ROM command \x55 and the recall memory command
  824. OWX_Reset($master);
  825. if( OWX_Complex($master,$owx_dev,"\xB8\x00",0.02) eq 0 ){
  826. return "$owx_dev recall memory failed";
  827. }
  828. select(undef,undef,undef,0.012);
  829. #-- NOW ask the specific device
  830. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  831. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  832. OWX_Reset($master);
  833. $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9);
  834. #Log 1,"OWXMULTI: data length from reading device is ".length($res)." bytes";
  835. return "$owx_dev not accessible in 2nd step"
  836. if( $res eq 0 );
  837. return "$owx_dev has returned invalid data"
  838. if( length($res)!=20);
  839. $ret = OWXMULTI_BinValues($hash,"ds2438.getvdd",undef,$owx_dev,undef,undef,substr($res,11));
  840. return $ret if (defined $ret);
  841. #------------------------------------------------------------------------------------
  842. #-- switch the device to current measurement off, V external only
  843. #-- issue the match ROM command \x55 and the write scratchpad command
  844. OWX_Reset($master);
  845. #if( OWX_Complex($master,$owx_dev,"\x4E\x00\x00",0) eq 0 ){
  846. if( OWX_Complex($master,$owx_dev,"\x4E\x00\x01",0) eq 0 ){
  847. return "$owx_dev write status failed";
  848. }
  849. #-- copy scratchpad to register
  850. #-- issue the match ROM command \x55 and the copy scratchpad command
  851. OWX_Reset($master);
  852. if( OWX_Complex($master,$owx_dev,"\x48\x00",0) eq 0){
  853. return "$owx_dev copy scratchpad failed";
  854. }
  855. #-- initiate voltage conversion
  856. #-- conversion needs some 6 ms !
  857. #-- issue the match ROM command \x55 and the start conversion command
  858. OWX_Reset($master);
  859. if( OWX_Complex($master,$owx_dev,"\xB4",0.01) eq 0 ){
  860. return "$owx_dev voltage conversion failed";
  861. }
  862. select(undef,undef,undef,0.006);
  863. #-- from memory to scratchpad
  864. #-- copy needs some 12 ms !
  865. #-- issue the match ROM command \x55 and the recall memory command
  866. OWX_Reset($master);
  867. if( OWX_Complex($master,$owx_dev,"\xB8\x00",0.02) eq 0 ){
  868. return "$owx_dev recall memory failed";
  869. }
  870. select(undef,undef,undef,0.012);
  871. #-- NOW ask the specific device
  872. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  873. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  874. my $context = "ds2438.getvad";
  875. OWX_Reset($master);
  876. $res=OWX_Complex($master,$owx_dev,"\xBE\x00",9);
  877. #-- process results
  878. return "$owx_dev not accessible in 2nd step"
  879. if( $res eq 0 );
  880. return "$owx_dev has returned invalid data"
  881. if( length($res)!=20);
  882. return OWXMULTI_BinValues($hash,$context,undef,$owx_dev,undef,undef,substr($res,11));
  883. #-- NEW OWX interface
  884. }else{
  885. #-- switch the device to current measurement off, VDD only
  886. #-- issue the match ROM command \x55 and the write scratchpad command
  887. #### master slave context proc owx_dev data crcpart numread startread callback delay
  888. #OWX_Qomplex($master, $hash, "write SP", 0, $owx_dev, "\x4E\x00\x08", 0, 0, 0, undef, undef);
  889. #-- switch the device to current measurement on, VDD only
  890. #-- issue the match ROM command \x55 and the write scratchpad command
  891. #### master slave context proc owx_dev data crcpart numread startread callback delay
  892. OWX_Qomplex($master, $hash, "write SP", 0, $owx_dev, "\x4E\x00\x09", 0, 2, 0, undef, 0.015);
  893. #-- copy scratchpad to register
  894. #-- issue the match ROM command \x55 and the copy scratchpad command
  895. #### master slave context proc owx_dev data crcpart numread startread callback delay
  896. OWX_Qomplex($master, $hash, "copy SP", 0, $owx_dev, "\x48\x00", 0, 1, 0, undef, 0.015);
  897. #-- initiate temperature conversion
  898. #-- conversion needs some 12 ms !
  899. #-- issue the match ROM command \x55 and the start conversion command
  900. #### master slave context proc owx_dev data crcpart numread startread callback delay
  901. OWX_Qomplex($master, $hash, "T conversion", 0, $owx_dev, "\x44", 0, 0, 0, undef, 0.015);
  902. #-- initiate voltage conversion
  903. #-- conversion needs some 6 ms !
  904. #-- issue the match ROM command \x55 and the start conversion command
  905. #### master slave context proc owx_dev data crcpart numread startread callback delay
  906. OWX_Qomplex($master, $hash, "V conversion", 0, $owx_dev, "\xB4", 0, 0, 0, undef, 0.015);
  907. #-- from memory to scratchpad
  908. #-- copy needs some 12 ms !
  909. #-- issue the match ROM command \x55 and the recall memory command
  910. #### master slave context proc owx_dev data crcpart numread startread callback delay
  911. OWX_Qomplex($master, $hash, "recall", 0, $owx_dev, "\xB8\x00", 0, 2, 0, undef, 0.015);
  912. #-- NOW ask the specific device
  913. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  914. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  915. #### master slave context proc owx_dev data crcpart numread startread callback delay
  916. # 1 provides additional reset after last operation
  917. OWX_Qomplex($master, $hash, "ds2438.getvdd", 1, $owx_dev, "\xBE\x00", 0, 20, 0, \&OWXMULTI_BinValues, 0.015);
  918. #-- switch the device to current measurement off, V external only
  919. #-- issue the match ROM command \x55 and the write scratchpad command
  920. #### master slave context proc owx_dev data crcpart numread startread callback delay
  921. #OWX_Qomplex($master, $hash, "write SP", 0, $owx_dev, "\x4E\x00\x00", 0, 0, 0, undef, undef);
  922. #-- switch the device to current measurement on, V external only
  923. #-- issue the match ROM command \x55 and the write scratchpad command
  924. #### master slave context proc owx_dev data crcpart numread startread callback delay
  925. OWX_Qomplex($master, $hash, "write SP", 0, $owx_dev, "\x4E\x00\x01", 0, 1, 0, undef, 0.015);
  926. #-- copy scratchpad to register
  927. #-- issue the match ROM command \x55 and the copy scratchpad command
  928. #### master slave context proc owx_dev data crcpart numread startread callback delay
  929. OWX_Qomplex($master, $hash, "copy SP", 0, $owx_dev, "\x48\x00", 0, 1, 0, undef, 0.015);
  930. #-- initiate voltage conversion
  931. #-- conversion needs some 6 ms !
  932. #-- issue the match ROM command \x55 and the start conversion command
  933. #### master slave context proc owx_dev data crcpart numread startread callback delay
  934. OWX_Qomplex($master, $hash, "V conversion", 0, $owx_dev, "\xB4", 0, 0, 0, undef, 0.015);
  935. #-- from memory to scratchpad
  936. #-- copy needs some 12 ms !
  937. #-- issue the match ROM command \x55 and the recall memory command
  938. #### master slave context proc owx_dev data crcpart numread startread callback delay
  939. OWX_Qomplex($master, $hash, "recall", 0, $owx_dev, "\xB8\x00", 0, 1, 0, undef, 0.015);
  940. #-- NOW ask the specific device
  941. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  942. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  943. #### master slave context proc owx_dev data crcpart numread startread callback delay
  944. # 1 provides additional reset after last operation
  945. OWX_Qomplex($master, $hash, "ds2438.getvad", 1, $owx_dev, "\xBE\x00", 0, 20, 0, \&OWXMULTI_BinValues, 0.015);
  946. return undef;
  947. }
  948. }
  949. #######################################################################################
  950. #
  951. # OWXMULTI_SetValues - Set values in device
  952. #
  953. # Parameter hash = hash of device addressed
  954. # a = argument array
  955. #
  956. ########################################################################################
  957. sub OWXMULTI_SetValues($@) {
  958. my ($hash, @a) = @_;
  959. my ($i,$j,$k);
  960. my $name = $hash->{NAME};
  961. #-- ID of the device
  962. my $owx_dev = $hash->{ROM_ID};
  963. #-- hash of the busmaster
  964. my $master = $hash->{IODev};
  965. #-- define vars
  966. my $key = $a[1];
  967. my $value = $a[2];
  968. #-- issue the match ROM command \x55 and the write scratchpad command \x4E,
  969. # followed by the write EEPROM command \x48
  970. #
  971. # so far writing the EEPROM does not work properly.
  972. # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM
  973. # 2. \x48 appended to match ROM => command not ok.
  974. # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM
  975. my $select=sprintf("\x4E%c%c\x48",0,0);
  976. OWX_Reset($master);
  977. my $res=OWX_Complex($master,$owx_dev,$select,0);
  978. if( $res eq 0 ){
  979. return "OWXMULTI: Device $owx_dev not accessible";
  980. }
  981. return undef;
  982. }
  983. ########################################################################################
  984. #
  985. # OWXMULTI_PT_GetValues - Get reading from one device async
  986. #
  987. # Parameter hash = hash of device addressed
  988. # final= 1 if FormatValues is to be called
  989. #
  990. ########################################################################################
  991. sub OWXMULTI_PT_GetValues($) {
  992. my ($hash) = @_;
  993. return PT_THREAD(sub {
  994. my ($thread) = @_;
  995. my ($i,$j,$k,$res,$ret,$response);
  996. #-- ID of the device
  997. my $owx_dev = $hash->{ROM_ID};
  998. #-- hash of the busmaster
  999. my $master = $hash->{IODev};
  1000. PT_BEGIN($thread);
  1001. #------------------------------------------------------------------------------------
  1002. #-- switch the device to current measurement off, VDD only
  1003. #-- issue the match ROM command \x55 and the write scratchpad command
  1004. #"ds2438.writestatusvdd"
  1005. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x4E\x00\x08",0);
  1006. PT_WAIT_THREAD($thread->{pt_execute});
  1007. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1008. #-- copy scratchpad to register
  1009. #-- issue the match ROM command \x55 and the copy scratchpad command
  1010. #"ds2438.copyscratchpadvdd"
  1011. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x48\x00",0);
  1012. PT_WAIT_THREAD($thread->{pt_execute});
  1013. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1014. #-- initiate temperature conversion
  1015. #-- conversion needs some 12 ms !
  1016. #-- issue the match ROM command \x55 and the start conversion command
  1017. #"ds2438.temperaturconversionvdd"
  1018. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x44",0);
  1019. $thread->{ExecuteTime} = gettimeofday() + 0.03; # was 0.012
  1020. PT_WAIT_THREAD($thread->{pt_execute});
  1021. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1022. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1023. delete $thread->{ExecuteTime};
  1024. #-- initiate voltage conversion
  1025. #-- conversion needs some 6 ms !
  1026. #-- issue the match ROM command \x55 and the start conversion command
  1027. #"ds2438.voltageconversionvdd"
  1028. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xB4",0);
  1029. $thread->{ExecuteTime} = gettimeofday() + 0.02; # was 0.006
  1030. PT_WAIT_THREAD($thread->{pt_execute});
  1031. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1032. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1033. delete $thread->{ExecuteTime};
  1034. #-- from memory to scratchpad
  1035. #-- copy needs some 12 ms !
  1036. #-- issue the match ROM command \x55 and the recall memory command
  1037. #"ds2438.recallmemoryvdd"
  1038. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xB8\x00",0);
  1039. $thread->{ExecuteTime} = gettimeofday() + 0.03; # was 0.012
  1040. PT_WAIT_THREAD($thread->{pt_execute});
  1041. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1042. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1043. delete $thread->{ExecuteTime};
  1044. #-- NOW ask the specific device
  1045. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  1046. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  1047. #"ds2438.getvdd"
  1048. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xBE\x00",9);
  1049. PT_WAIT_THREAD($thread->{pt_execute});
  1050. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1051. $res = $thread->{pt_execute}->PT_RETVAL();
  1052. unless (defined $res and length($res)==9) {
  1053. PT_EXIT("$owx_dev has returned invalid data");
  1054. }
  1055. $ret = OWXMULTI_BinValues($hash,"ds2438.getvdd",undef,$owx_dev,undef,undef,$res);
  1056. if ($ret) {
  1057. die $ret;
  1058. }
  1059. #------------------------------------------------------------------------------------
  1060. #-- switch the device to current measurement off, V external only
  1061. #-- issue the match ROM command \x55 and the write scratchpad command
  1062. #"ds2438.writestatusvad"
  1063. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x4E\x00\x00",0);
  1064. PT_WAIT_THREAD($thread->{pt_execute});
  1065. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1066. #-- copy scratchpad to register
  1067. #-- issue the match ROM command \x55 and the copy scratchpad command
  1068. #"ds2438.copyscratchpadvad"
  1069. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\x48\x00",0);
  1070. PT_WAIT_THREAD($thread->{pt_execute});
  1071. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1072. #-- initiate voltage conversion
  1073. #-- conversion needs some 6 ms !
  1074. #-- issue the match ROM command \x55 and the start conversion command
  1075. #"ds2438.voltageconversionvad"
  1076. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xB4",0);
  1077. $thread->{ExecuteTime} = gettimeofday() + 0.02; # was 0.006
  1078. PT_WAIT_THREAD($thread->{pt_execute});
  1079. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1080. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1081. delete $thread->{ExecuteTime};
  1082. #-- from memory to scratchpad
  1083. #-- copy needs some 12 ms !
  1084. #-- issue the match ROM command \x55 and the recall memory command
  1085. #"ds2438.recallmemoryvad"
  1086. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xB8\x00",0);
  1087. $thread->{ExecuteTime} = gettimeofday() + 0.03; # was 0.012
  1088. PT_WAIT_THREAD($thread->{pt_execute});
  1089. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1090. PT_YIELD_UNTIL(gettimeofday() >= $thread->{ExecuteTime});
  1091. delete $thread->{ExecuteTime};
  1092. #-- NOW ask the specific device
  1093. #-- issue the match ROM command \x55 and the read scratchpad command \xBE
  1094. #-- reading 9 + 2 + 9 data bytes = 20 bytes
  1095. #"ds2438.getvad"
  1096. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,"\xBE\x00", 9);
  1097. PT_WAIT_THREAD($thread->{pt_execute});
  1098. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1099. #-- process results
  1100. $res = $thread->{pt_execute}->PT_RETVAL();
  1101. unless (defined $res and length($res)==9) {
  1102. PT_EXIT("$owx_dev has returned invalid data");
  1103. }
  1104. $ret = OWXMULTI_BinValues($hash,"ds2438.getvad",undef,$owx_dev,undef,undef,$res);
  1105. if ($ret) {
  1106. die $ret;
  1107. }
  1108. PT_END;
  1109. });
  1110. }
  1111. #######################################################################################
  1112. #
  1113. # OWXMULTI_PT_SetValues - Set values in device async
  1114. #
  1115. # Parameter hash = hash of device addressed
  1116. # a = argument array
  1117. #
  1118. ########################################################################################
  1119. sub OWXMULTI_PT_SetValues($@) {
  1120. my ($hash, @a) = @_;
  1121. return PT_THREAD(sub {
  1122. my ($thread) = @_;
  1123. my ($i,$j,$k);
  1124. my $name = $hash->{NAME};
  1125. #-- ID of the device
  1126. my $owx_dev = $hash->{ROM_ID};
  1127. #-- hash of the busmaster
  1128. my $master = $hash->{IODev};
  1129. PT_BEGIN($thread);
  1130. #-- define vars
  1131. my $key = $a[1];
  1132. my $value = $a[2];
  1133. #-- issue the match ROM command \x55 and the write scratchpad command \x4E,
  1134. # followed by the write EEPROM command \x48
  1135. #
  1136. # so far writing the EEPROM does not work properly.
  1137. # 1. \x48 directly appended to the write scratchpad command => command ok, no effect on EEPROM
  1138. # 2. \x48 appended to match ROM => command not ok.
  1139. # 3. \x48 sent by WriteBytePower after match ROM => command ok, no effect on EEPROM
  1140. my $select=sprintf("\x4E%c%c\x48",0,0);
  1141. #"setvalues"
  1142. $thread->{pt_execute} = OWX_ASYNC_PT_Execute($master,1,$owx_dev,$select, 0);
  1143. PT_WAIT_THREAD($thread->{pt_execute});
  1144. die $thread->{pt_execute}->PT_CAUSE() if ($thread->{pt_execute}->PT_STATE() == PT_ERROR);
  1145. PT_END;
  1146. });
  1147. }
  1148. 1;
  1149. =pod
  1150. =item device
  1151. =item summary to control 1-Wire chip DS2438Z - Smart Battery Monitor
  1152. =begin html
  1153. <a name="OWMULTI"></a>
  1154. <h3>OWMULTI</h3>
  1155. <p>FHEM module to commmunicate with 1-Wire multi-sensors, currently the DS2438 smart battery
  1156. monitor<br /> <br />This 1-Wire module works with the OWX interface module or with the OWServer interface module
  1157. (prerequisite: Add this module's name to the list of clients in OWServer).
  1158. Please define an <a href="#OWX">OWX</a> device or <a href="#OWServer">OWServer</a> device first.</p>
  1159. <h4>Example</h4>
  1160. <p>
  1161. <code>define OWX_M OWMULTI 7C5034010000 45</code>
  1162. <br />
  1163. <code>attr OWX_M VName humidity|rH</code>
  1164. <br />
  1165. <code>attr OWX_M VUnit %</code>
  1166. <br />
  1167. <code>attr OWX_M VFunction (161.29 * V / VDD - 25.8065)/(1.0546 - 0.00216 * T)</code>
  1168. </p>
  1169. <a name="OWMULTIdefine"></a>
  1170. <h4>Define</h4>
  1171. <p>
  1172. <code>define &lt;name&gt; OWMULTI [&lt;model&gt;] &lt;id&gt; [&lt;interval&gt;]</code> or <br/>
  1173. <code>define &lt;name&gt; OWMULTI &lt;fam&gt;.&lt;id&gt; [&lt;interval&gt;]</code>
  1174. <br /><br /> Define a 1-Wire multi-sensor</p>
  1175. <ul>
  1176. <li>
  1177. <code>[&lt;model&gt;]</code><br /> Defines the sensor model (and thus 1-Wire family
  1178. id), currently the following values are permitted: <ul>
  1179. <li>model DS2438 with family id 26 (default if the model parameter is omitted).
  1180. Measured is a temperature value, an external voltage and the current supply
  1181. voltage</li>
  1182. </ul>
  1183. </li>
  1184. <li>
  1185. <code>&lt;fam&gt;</code>
  1186. <br />2-character unique family id, see above
  1187. </li>
  1188. <li>
  1189. <code>&lt;id&gt;</code>
  1190. <br />12-character unique ROM id of the converter device without family id and CRC
  1191. code </li>
  1192. <li>
  1193. <code>&lt;interval&gt;</code>
  1194. <br />Measurement interval in seconds. The default is 300 seconds, a value of 0 disables the automatic update. </li>
  1195. </ul>
  1196. <a name="OWMULTIset"></a>
  1197. <h4>Set</h4>
  1198. <ul>
  1199. <li><a name="owmulti_interval">
  1200. <code>set &lt;name&gt; interval &lt;int&gt;</code></a><br /> Measurement
  1201. interval in seconds. The default is 300 seconds, a value of 0 disables the automatic update. </li>
  1202. </ul>
  1203. <a name="OWMULTIget"></a>
  1204. <h4>Get</h4>
  1205. <ul>
  1206. <li><a name="owmulti_id">
  1207. <code>get &lt;name&gt; id</code></a>
  1208. <br /> Returns the full 1-Wire device id OW_FAMILY.ROM_ID.CRC </li>
  1209. <li><a name="owmulti_reading">
  1210. <code>get &lt;name&gt; reading</code></a><br />Obtain all three measurement values. </li>
  1211. <li><a name="owmulti_temperature">
  1212. <code>get &lt;name&gt; temperature</code></a><br />Obtain the temperature value. </li>
  1213. <li><a name="owmulti_vdd">
  1214. <code>get &lt;name&gt; VDD</code></a><br />Obtain the current supply voltage. </li>
  1215. <li><a name="owmulti_raw">
  1216. <code>get &lt;name&gt; raw</code></a><br />Obtain the raw readings for V and W.</li>
  1217. </ul>
  1218. <a name="OWMULTIattr"></a>
  1219. <h4>Attributes</h4>
  1220. <ul><li><a name="owtherm_interval2">
  1221. <code>attr &lt;name&gt; interval &lt;int&gt;</code></a><br /> Measurement
  1222. interval in seconds. The default is 300 seconds, a value of 0 disables the automatic update.</li>
  1223. <li><a name="owmulti_vname"><code>attr &lt;name&gt; VName
  1224. &lt;string&gt;[|&lt;string&gt;]</code></a>
  1225. <br />name for the voltage channel [|short name used in state reading]. </li>
  1226. <li><a name="owmulti_vunit"><code>attr &lt;name&gt; VUnit
  1227. &lt;string&gt;</code></a>
  1228. <br />unit of measurement for the voltage channel used in state reading (default "V", set to "none" for empty).</li>
  1229. <li><a name="owmulti_vfunction"><code>attr &lt;name&gt; VFunction
  1230. &lt;string&gt;</code></a>
  1231. <br />arbitrary functional expression involving the values VDD, V, W and T. Example see
  1232. above. <ul>
  1233. <li>VDD is replaced by the measured supply voltage in Volt,</li>
  1234. <li>V by the measured external voltage channel,</li>
  1235. <li>W by the measured external sense channel,</li>
  1236. <li>T by the measured and corrected temperature in its unit</li>
  1237. </ul></li>
  1238. <li><a name="owmulti_wname"><code>attr &lt;name&gt; WName
  1239. &lt;string&gt;[|&lt;string&gt;]</code></a>
  1240. <br />name for the sense channel [|short name used in state reading]. </li>
  1241. <li><a name="owmulti_wunit"><code>attr &lt;name&gt; WUnit
  1242. &lt;string&gt;</code></a>
  1243. <br />unit of measurement for the sense channel used in state reading (default “V", set to "none" for empty).</li>
  1244. <li><a name="owmulti_wfunction"><code>attr &lt;name&gt; WFunction
  1245. &lt;string&gt;</code></a>
  1246. <br />arbitrary functional expression involving the values VDD, V, W and T. Example and usage see
  1247. above.</li>
  1248. <li><a name="owmulti_tempOffset"><code>attr &lt;name&gt; tempOffset &lt;float&gt;</code>
  1249. </a>
  1250. <br />temperature offset in &deg;C added to the raw temperature reading. </li>
  1251. <li><a name="owmulti_tempUnit"><code>attr &lt;name&gt; tempUnit
  1252. Celsius|Kelvin|Fahrenheit</code>
  1253. </a>
  1254. <br />unit of measurement (temperature scale), default is Celsius = &deg;C </li>
  1255. <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
  1256. </ul>
  1257. =end html
  1258. =cut