Netaddict



Munin mit einem EM 1010 PC verbinden

Der EM 1010 PC ist ein Funk-Energiemonitor mit USB-Anschluss, der seine Daten von einem oder mehreren Funksendern (z. B. EM 1000-WZ) erhält. Die beigelegte Software ist nur unter Windows lauffähig. Ich verwende allerdings nur Linux-Systeme. Außerdem wäre es interessant, den Verbrauch mit der von mir bevorzugten Monitoringanwendung Munin zu erfassen.

So sieht es aus, wenn man alles richtig gemacht hat.

Daten vom Gerät holen

Wie es grundsätzlich funktioniert, steht hier. Im Softwarepakt Fhem ist ein Script namens „em1010.pl“ enthalten, dass die Daten aus dem EM 1010 via USB ausliest. Ich habe das Script genommen, abgespeckt und so umgeschrieben, dass es als Munin-Plugin fungiert. Auf einem aktuellen Debian braucht man auch die Kernelmodule nicht mehr anfassen, das EM1010 wird beim Anstecken an den PC als serielles USB-Device erkannt und unter /dev/ttyUSB0 eingehängt.

Munin-Plugin

Den Quell-Text des Munin-Plugins, speichert man unter /etc/munin/plugins/em1010 ab. Danach muss man das Script ausführbar machen und in der Datei /etc/munin/plugin-conf.d/munin-node einen Eintrag

[em1010]
user root

hinzufügen und den Munin-Node neu starten.

Im Plugin sind eventuell die Variablen devicenumber und ser an die eigenen Gegebenheiten anzupassen.

em1010
#!/usr/bin/perl
 
use strict;
use warnings;
use Device::SerialPort;
 
sub b($$);
sub w($$);
sub docrc($$);
sub checkcrc($$);
sub getData($);
sub makemsg($);
 
my $ser = "/dev/ttyUSB0";
my $devicenumber = "1";
my $cmd = "";
 
if ($ARGV[0]) {
  $cmd = $ARGV[0];
}
 
#####################
# Open serial port
my $serport = new Device::SerialPort ($ser);
die "Can't open $ser: $!\n" if(!$serport);
$serport->reset_error();
$serport->baudrate(38400);
$serport->databits(8);
$serport->parity('none');
$serport->stopbits(1);
$serport->handshake('none');
 
#########################
sub
b($$)
{
  my ($t,$p) = @_;
  return ord(substr($t,$p,1));
}
 
#########################
sub
w($$)
{
  my ($t,$p) = @_;
  return b($t,$p+1)*256 + b($t,$p);
}
 
#########################
sub
dw($$)
{
  my ($t,$p) = @_;
  return w($t,$p+2)*65536 + w($t,$p);
}
 
#########################
sub
docrc($$)
{
  my ($in, $val) = @_;
  my ($crc, $bits) = (0, 8);
  my $k = (($in >> 8) ^ $val) << 8;
  while($bits--) {
    if(($crc ^ $k) & 0x8000) {
      $crc = ($crc << 1) ^ 0x8005;
    } else {
      $crc <<= 1;
    }
    $k <<= 1;
  }
  return (($in << 8) ^ $crc) & 0xffff;
}
 
#########################
sub
checkcrc($$)
{
  my ($otxt, $len) = @_;
  my $crc = 0x8c27;
  for(my $l = 2; $l < $len+4; $l++) {
    my $b = ord(substr($otxt,$l,1));
    $crc = docrc($crc, 0x10) if($b==0x02 || $b==0x03 || $b==0x10);
    $crc = docrc($crc, $b);
  }
  return ($crc == w($otxt, $len+4));
}
 
#########################
sub
esc($)
{
  my ($b) = @_;
 
  my $out = "";
  $out .= chr(0x10) if($b==0x02 || $b==0x03 || $b==0x10);
  $out .= chr($b);
}
#########################
sub
makemsg($)
{
  my ($data) = @_;
  my $len = length($data);
  $data = chr($len&0xff) . chr(int($len/256)) . $data;
 
  my $out = pack('H*', "0200");
  my $crc = 0x8c27;
  for(my $l = 0; $l < $len+2; $l++) {
    my $b = ord(substr($data,$l,1));
    $crc = docrc($crc, 0x10) if($b==0x02 || $b==0x03 || $b==0x10);
    $crc = docrc($crc, $b);
    $out .= esc($b);
  }
  $out .= esc($crc&0xff);
  $out .= esc($crc/256);
  $out .= chr(0x03);
  return $out;
}
 
#########################
sub
getData($)
{
  my ($d) = @_;
  $d = makemsg(pack('H*', $d));
 
  for(my $rep = 0; $rep < 3; $rep++) {
 
    $serport->write($d);
 
    my $retval = "";
    my $esc = 0;
    my $started = 0;
    my $complete = 0;
    for(;;) {
      my ($rout, $rin) = ('', '');
      vec($rin, $serport->FILENO, 1) = 1;
      my $nfound = select($rout=$rin, undef, undef, 1.0);
 
      die("Select error $nfound / $!\n") if($nfound < 0);
      last if($nfound == 0);
 
      my $buf = $serport->input();
      die "EOF on $ser\n" if(!defined($buf) || length($buf) == 0);
 
      for(my $i = 0; $i < length($buf); $i++) {
        my $b = ord(substr($buf,$i,1));
 
        if(!$started && $b != 0x02) { next; }
        $started = 1;
        if($esc) { $retval .= chr($b); $esc = 0; next; }
        if($b == 0x10) { $esc = 1; next; }
        $retval .= chr($b);
        if($b == 0x03) { $complete = 1; last; }
      }
      if($complete) {
        my $l = length($retval);
        if($l < 8)                  { printf("Msg too short\n"); last; }
        if(b($retval,1) != 0)       { printf("Bad second byte\n"); last; }
        if(w($retval,2) != $l-7)    { printf("Length mismatch\n"); last; }
        if(!checkcrc($retval,$l-7)) { printf("Bad CRC\n"); last; }
        return substr($retval, 4, $l-7);
      }
    }
  }
 
  printf "Timeout reading the answer\n";
  exit(1);
}
#########################
sub
hexdump($)
{
  my ($d) = @_;
  for(my $i = 0; $i < length($d); $i += 16) {
    my $h = unpack("H*", substr($d, $i, 16));
    $h =~ s/(....)/$1 /g;
    printf "RAW    %-40s\n", $h;
  }
}
 
#########################
sub
outputData()
{
  my $d = getData(sprintf("7a%02x",$devicenumber-1));
 
  if($d eq ((pack('H*',"00") x 45) . pack('H*',"FF") x 6)) {
    printf("No device no. $devicenumber present\n");
    return;
  }
 
  my $pulses=w($d,13);
  my $pulses_max=w($d,15);
  my $ec=w($d,49) / 10;
  my $cur_energy=0;
  my $cur_power=0;
  my $cur_power_max=0;
  my $iec=0;
 
  if ($ec eq 0) {
    # Sensor 5..
    $iec = 1000;
    $cur_power  = $pulses / 100;
    $cur_power_max  = $pulses_max / 100;
  } else {
    # Sensor 1..4
    $iec = $ec;
    $cur_energy = $pulses / $ec; # ec = U/kWh
    $cur_power = $cur_energy / 5 * 60; # 5minute interval scaled to 1h
  }
  printf("power.value %.3f\n", $cur_power);
}
 
#############
# config or data output
if($cmd eq "config") {
  print <<EOT;
graph_title Power consumption
graph_vlabel kW
power.label power
graph_category homeautomation
graph_info Monitor the electrical energy consumption of the whole building via a EM 1010 PC
EOT
  exit(0);
}
else {
  outputData();
}
 

 
XHTML 1.1 CSS 2 Creative Commons License
Netaddict Version 6.0 - © 1996-2017 by netaddict.de - Kontakt