fork(2) download
  1. #########################################################################
  2. # fhem Modul für Victron BMV 600 Batteriemonitor
  3. # Die Beschreibung und Kommentierung dieses Moduls ist in deutsch gehalten,
  4. # um anderen das erstellen eigener Module zu erleichtern.
  5. # Als Basis diente das Modul 00_TAHR.pm und 00_WHR962.pm
  6. #
  7. # # Kommentarbereich
  8. package main;
  9.  
  10. use strict; #
  11. use warnings; #
  12. use Time::HiRes qw(gettimeofday); #
  13.  
  14. sub BMV600_Read($); #
  15. sub BMV600_Ready($); #
  16. sub BMV600_setbits($$); #
  17. sub BMV600_SetReading($$$$); #
  18.  
  19. my $buf1 = ""; # Hilfsvariable für auslesen des Buffers
  20. my $Zeit = 0; # Hilfsvariable Zeit
  21. my $Umin = 15; #Hilfsvariable Minimale Batteriespannung (um ungültige Werte abzufangen) in V
  22. my $Umax = 38; #dito, nur uMax
  23.  
  24. my %BMV600_sets = ( # Hier werden Befehle an den Batterie Monitor geschickt
  25.  
  26. );
  27.  
  28.  
  29. #########################################################################
  30. sub BMV600_Initialize($)
  31. {
  32. my ($hash) = @_;
  33.  
  34. require "$attr{global}{modpath}/FHEM/DevIo.pm";
  35.  
  36. $hash->{ReadFn} = "BMV600_Read";
  37. $hash->{ReadyFn} = "BMV600_Ready";
  38. $hash->{DefFn} = "BMV600_Define";
  39. $hash->{UndefFn} = "BMV600_Undef";
  40. $hash->{SetFn} = "BMV600_Set";
  41. $hash->{AttrList} =
  42. "do_not_notify:1,0 loglevel:0,1,2,3,4,5,6 " . $readingFnAttributes;
  43. }
  44.  
  45. ######################################################################### #
  46. sub BMV600_Define($$)
  47. {
  48. my ( $hash, $def ) = @_;
  49. my @a = split( "[ \t][ \t]*", $def );
  50.  
  51. return "wrong syntax: define <name> BMV600 [devicename|none]"
  52. if ( @a != 3 );
  53.  
  54. DevIo_CloseDev($hash);
  55. my $name = $a[0];
  56. my $dev = $a[2];
  57.  
  58. if ( $dev eq "none" )
  59. {
  60. Log3 undef, 1, "BMV600 device is none, commands will be echoed only";
  61. }
  62.  
  63. $hash->{DeviceName} = $dev;
  64. my $ret = DevIo_OpenDev( $hash, 0, "BMV600_Poll" );
  65. return $ret;
  66. }
  67.  
  68. #########################################################################
  69. sub #
  70. BMV600_Undef($$) #
  71. { #
  72. my ( $hash, $arg ) = @_; #
  73. DevIo_CloseDev($hash); #
  74. RemoveInternalTimer($hash); #
  75. } #
  76.  
  77. #########################################################################
  78. sub BMV600_Set($@)
  79. {
  80. my ( $hash, @a ) = @_;
  81. my $name = $hash->{NAME};
  82. return "\"set BMV600\" needs at least an argument" if ( @a < 2 );
  83.  
  84. my $cmd = $BMV600_sets{ $a[1] };
  85. return "Unknown argument $a[1], choose one of "
  86. . join( " ", sort keys %BMV600_sets )
  87. if ( !defined($cmd) );
  88.  
  89. Log3 $name, 3, "DevIo_SimpleWrite: $hash $cmd";
  90. DevIo_SimpleWrite( $hash, $cmd, 1 );
  91. }
  92.  
  93.  
  94. sub BMV600_Read($)
  95. {
  96. my ($hash) = @_;
  97. my $name = $hash->{NAME};
  98. my ( $data, $crc );
  99. my $buf = DevIo_SimpleRead($hash);
  100. my $tn = TimeNow();
  101. my ( $key, $val ) = ( "key", "val" );
  102. my $Power = 0;
  103.  
  104. ###### Daten der seriellen Schnittstelle holen und an $buf1 anhaengen
  105.  
  106. return "" if ( !defined($buf) );
  107. $buf1 .= $buf;
  108.  
  109. #Log3 $name, 5, "Current buffer content: " $buf1;
  110.  
  111. my $pos_cs0 = index($buf1,"\nChecksum"); #sucht nach neuer Zeile mit Checksum
  112. my $pos_cs1 = -1;
  113. if($pos_cs0 >= 0)
  114. {
  115. $pos_cs1 = index($buf1,"\nChecksum",$pos_cs0+1);
  116. }
  117. if(($pos_cs0 >= 0) && ($pos_cs1 >= 0)) #wenn 2mal Checksum dann beginnt er das lesen
  118. {
  119. readingsBeginUpdate($hash);
  120. my @e = split("\n",$buf1); #Splittet die Daten bei einer neuen Zeile auf
  121. my $V_first = index($e[1],"V") >= 0; #schaut in die Schlaufe und fängt bei V an
  122. for my $i (0 .. $#e)
  123. {
  124.  
  125. my @e_ = split(" ",$e[$i]);
  126. if($e_[0] eq "Checksum")
  127. {
  128. next;
  129. }
  130. if($e_[0] eq "V")
  131. {
  132. if(($e_[1] /1000 ) > $Umin && ($e_[1] /1000 ) < $Umax ) #nur Werte übernehmen die im gültigen bereich liegen
  133. {
  134. #readingsBulkUpdate($hash,"_V",($e_[1] ));
  135. readingsBulkUpdate($hash,"Spannung",sprintf("%.1f",$e_[1]/1000));
  136. $Power = $e_[1] / 1000; #für Leistungsberechnung als Reading
  137. }
  138. }
  139.  
  140.  
  141. if($e_[0] eq "I")
  142. {
  143. #readingsBulkUpdate($hash,"_I",($e_[1])); # aktueller Strom in mA
  144. readingsBulkUpdate($hash,"Strom",sprintf("%.1f",$e_[1] / 1000)); # aktueller Strom in A
  145. $Power = ($Power * $e_[1]) / 1000; #für Leistungsberechnung als Reading
  146. }
  147.  
  148. if($e_[0] eq "SOC")
  149. {
  150. if(($e_[1]) > -1 && ($e_[1]) < 1001 )
  151. {
  152. #readingsBulkUpdate($hash,"_SOC",$e_[1]); # Ladezustand in %
  153. readingsBulkUpdate($hash,"Ladezustand",sprintf("%.2f",($e_[1]) / 10)); # Ladezustand in %
  154. }
  155. }
  156. if($e_[0] eq "TTG")
  157. {
  158. #readingsBulkUpdate($hash,"_TTG",$e_[1]); #Restlaufzeit in Minuten
  159. readingsBulkUpdate($hash,"Restlaufzeit_h",sprintf("%.2f",($e_[1]) / 60)); #Restlaufzeit in stunden
  160. }
  161. if($e_[0] eq "VS")
  162. {
  163. #readingsBulkUpdate($hash,"_VS",$e_[1]);
  164. readingsBulkUpdate($hash,"Starterbatteriespannung",sprintf("%.2f",($e_[1]) / 1000)); #Spannung der Starterbatterie
  165. }
  166. if($e_[0] eq "CE")
  167. {
  168. #readingsBulkUpdate($hash,"_CE",$e_[1]); #entnommene Kapazität in Ah
  169. readingsBulkUpdate($hash,"Amperestunden",sprintf("%.1f",$e_[1] /1000 ));
  170. }
  171. if($e_[0] eq "H1") # mAh Wert der bisher tiefsten Entladung
  172. {
  173. #readingsBulkUpdate($hash,"_H1",$e_[1]);
  174. readingsBulkUpdate($hash,"Entladung_tiefste_Ah",sprintf("%.2f",$e_[1] /1000 ));
  175. }
  176. if($e_[0] eq "H2") # mAh Wert der letzten Entladung
  177. {
  178. #readingsBulkUpdate($hash,"_H2",$e_[1]);
  179. readingsBulkUpdate($hash,"Entladung_letzte_Ah",sprintf("%.2f",$e_[1] / 1000));
  180. }
  181. if($e_[0] eq "H3") # mAh Wert der durchschnittlichen Entladung
  182. {
  183. #readingsBulkUpdate($hash,"_H3",$e_[1]);
  184. readingsBulkUpdate($hash,"Entladung_Durchschnitt_Ah",sprintf("%.2f",$e_[1] / 1000));
  185. }
  186. if($e_[0] eq "H4") # Anzahl der Ladezyklen
  187. {
  188. #readingsBulkUpdate($hash,"_H4",$e_[1]);
  189. readingsBulkUpdate($hash,"Ladezyklen_Anzahl",$e_[1]);
  190. }
  191. if($e_[0] eq "H5") # Anzahl der völligen Entladungen
  192. {
  193. #readingsBulkUpdate($hash,"_H5",$e_[1]);
  194. readingsBulkUpdate($hash,"Entladung_voellige_Anzahl",$e_[1]);
  195. }
  196. if($e_[0] eq "H6") # mAh Gesamtwert (kumuliert) der bisher entnommenen Ah
  197. {
  198. #readingsBulkUpdate($hash,"_H6",$e_[1]);
  199. readingsBulkUpdate($hash,"Entladung_gesamt_Ah",sprintf("%.2f",$e_[1] / 1000));
  200. }
  201. if($e_[0] eq "H7") # mV Minimalwert der Batteriespannung
  202. {
  203. #readingsBulkUpdate($hash,"_H7",$e_[1]);
  204. readingsBulkUpdate($hash,"Spannung_minmal",sprintf("%.2f",$e_[1] / 1000));
  205. }
  206. if($e_[0] eq "H8") # mV Maximalwert der Batteriespannung
  207. {
  208. #readingsBulkUpdate($hash,"_H8",$e_[1]);
  209. readingsBulkUpdate($hash,"Spannung_maximal",sprintf("%.2f",$e_[1] / 1000));
  210. }
  211. if($e_[0] eq "H9") # Sekunden Anzahl der Tage seit der letzten Volladung
  212. {
  213. #readingsBulkUpdate($hash,"_H9",$e_[1]);
  214. readingsBulkUpdate($hash,"Volladung_letzte_vor_Stunden",sprintf("%.2f",$e_[1] / 3600));
  215. }
  216. if($e_[0] eq "H10") # Anzahl der automatisch durchgeführten Synchronistionen
  217. {
  218. #readingsBulkUpdate($hash,"_H10",$e_[1]);
  219. readingsBulkUpdate($hash,"autom_Synchronisationen_Anzahl",$e_[1]);
  220. }
  221. if($e_[0] eq "H11") # Anzahl der Unterspannungs Alarme
  222. {
  223. #readingsBulkUpdate($hash,"_H11",$e_[1]);
  224. readingsBulkUpdate($hash,"Alarm_Unterspannung_Anzahl",$e_[1]);
  225. }
  226. if($e_[0] eq "H12") # Anzahl der Überspannungs Alarme.
  227. {
  228. #readingsBulkUpdate($hash,"_H12",$e_[1]);
  229. readingsBulkUpdate($hash,"Alarm_Ueberspannungs_Anzahl",$e_[1]);
  230. }
  231. if($e_[0] eq "H13") # Anzahl der Alarme für leere Starterbatterie. nur beim BMV-602
  232. {
  233. #readingsBulkUpdate($hash,"_H13",$e_[1]);
  234. readingsBulkUpdate($hash,"Alarm_leere_Starterbatterie_Anzahl",$e_[1]);
  235. }
  236. if($e_[0] eq "H14") # Anzahl der Alarme bezüglich der Zahl der Überspannungsalarme nur beim BMV-602
  237. {
  238. #readingsBulkUpdate($hash,"_H14",$e_[1]);
  239. readingsBulkUpdate($hash,"Alarm_Ueberspannung_Starterbatterie_Anzahl",$e_[1]);
  240. }
  241. if($e_[0] eq "H15") # Minimale Starter-Batterie-Spannung. nur beim BMV-602
  242. {
  243. #readingsBulkUpdate($hash,"_H15",$e_[1]);
  244. readingsBulkUpdate($hash,"Starterbatteriespannung_minimal",sprintf("%.2f",$e_[1] / 1000));
  245. }
  246. if($e_[0] eq "H16") # Maximale Starterbatteriespannung nur beim BMV-602
  247. {
  248. #readingsBulkUpdate($hash,"_H16",$e_[1]);
  249. readingsBulkUpdate($hash,"Starterbatteriespannung_maximal",sprintf("%.2f",$e_[1] / 1000));
  250. }
  251. if($e_[0] eq "Alarm")
  252. {
  253. #readingsBulkUpdate($hash,"_Alarm",$e_[1]);
  254. readingsBulkUpdate($hash,"Alarmzustand",$e_[1]);
  255. }
  256. if($e_[0] eq "AR")
  257. {
  258. # Alarm Reason :
  259. #Low Voltage 1
  260. #High Voltage 2
  261. #Low SOC 4
  262. #Low Starter Voltage 8
  263. #High Starter Voltage 16
  264. if($e_[1] eq "0"){readingsBulkUpdate($hash,"Alarm_Reason","-");}
  265. elsif($e_[1] eq "1"){readingsBulkUpdate($hash,"Alarm_Reason","Low Voltage");}
  266. elsif($e_[1] eq "2"){readingsBulkUpdate($hash,"Alarm_Reason","High Voltage");}
  267. elsif($e_[1] eq "4"){readingsBulkUpdate($hash,"Alarm_Reason","Low SOC");}
  268. elsif($e_[1] eq "8"){readingsBulkUpdate($hash,"Alarm_Reason","Low Starter Voltage");}
  269. elsif($e_[1] eq "16"){readingsBulkUpdate($hash,"Alarm_Reason","High Starter Voltage");}
  270. else{readingsBulkUpdate($hash,"Alarm_Reason","Multi-Alarm");}
  271. #readingsBulkUpdate($hash,"_AR",$e_[1]);
  272. }
  273. if($e_[0] eq "FW")
  274. {
  275. #readingsBulkUpdate($hash,"_FW",$e_[1]);
  276. readingsBulkUpdate($hash,"BMV_Firmware",$e_[1]);
  277. }
  278. if($e_[0] eq "BMV")
  279. {
  280. #readingsBulkUpdate($hash,"_BMV",$e_[1]);
  281. readingsBulkUpdate($hash,"BMV_Modell",$e_[1]);
  282. }
  283.  
  284. }
  285. readingsBulkUpdate($hash,"Power",sprintf("%.2f",$Power));
  286. #readingsBulkUpdate($hash,"Umin(System)",$Umin);
  287. #readingsBulkUpdate($hash,"Umax(System)",$Umax);
  288. readingsEndUpdate( $hash, 1 );
  289. $buf1 = "";
  290. }
  291. }
  292.  
  293. #########################################################################
  294. sub BMV600_Ready($)
  295. {
  296. my ($hash) = @_;
  297.  
  298. return DevIo_OpenDev( $hash, 1, undef )
  299. if ( $hash->{STATE} eq "disconnected" );
  300.  
  301. # This is relevant for windows/USB only
  302. my $po = $hash->{USBDev};
  303. my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
  304. return ( $InBytes > 0 );
  305. }
  306.  
  307. #########################################################################
  308. sub BMV600_Poll($)
  309. {
  310. my ($hash) = @_;
  311. my $name = $hash->{NAME};
  312. push @{ $hash->{SENDBUFFER} }, $BMV600_sets{"logmode"};
  313. DevIo_SimpleWrite( $hash, "07F0009B01014A070F", 1 );
  314. Log3 $name, 5, "RS232 Modus PC-Master";
  315. }
  316.  
  317. #########################################################################
  318. sub BMV600_SetReading($$$$)
  319. {
  320. my ( $hash, $tn, $key, $val ) = @_;
  321. my $name = $hash->{NAME};
  322. Log3 $name, 4, "$name: $key $val";
  323. $hash->{READINGS}{$key}{TIME} = $tn;
  324. $hash->{READINGS}{$key}{VAL} = $val;
  325. DoTrigger( $name, "$key: $val" );
  326. }
  327.  
  328.  
  329. 1;
  330.  
Runtime error #stdin #stdout #stderr 0.01s 6592KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
Global symbol "%attr" requires explicit package name at prog.pl line 34.
Global symbol "$readingFnAttributes" requires explicit package name at prog.pl line 42.
syntax error at prog.pl line 60, near "Log3 undef"
Execution of prog.pl aborted due to compilation errors.