Modul für Waterkotte Wärmepumpe mit Resümat CD4 Steuerung

Begonnen von StefanStrobel, 17 Dezember 2013, 20:17:11

Vorheriges Thema - Nächstes Thema

StefanStrobel

Ich habe nun seit ein paar Monate Fhem auf einer FritzBox 7390 mit einem CUL und einem Enocean Stick, ESA2000 Sensoren an meinen Stromzählern und inzwischen auch eine serielle Anbindung an meine Waterkotte Wärmepumpe.

Das Modul für die Wärmepumpe habe ich als Einstiegsprojekt aus verschiedenen Vorlagen zusammengebaut. Die Analyse des Protokolls war glücklicherweise schon in verschiedenen Foren veröffentlicht.

Falls jemand auch so eine Wärmepumpe hat, hier mein Modul.
Da es mein erstes Perl-Programm war, bin ich für Optimierungshinweise dankbar.

#########################################################################
# fhem Modul für Waterkotte Wärmepumpe mit Resümat CD4 Steuerung
# Vorlage: Modul WHR962, diverse Foreneinträge sowie Artikel über Auswertung der
# Wärmepumpe mit Linux / Perl im Linux Magazin aus 2010
# insbesondere:
#      http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=2  (Speicheradressen-Liste)
#      http://www.ip-symcon.de/forum/threads/2092-ComPort-und-Waterkotte-abfragen                      (Protokollbeschreibung)
#      http://www.haustechnikdialog.de/Forum/t/6144/Waterkotte-5017-3-an-den-Computer-anschliessen?page=4    (Beispiel Befehls-Strings)
#
# Benutzung auf eigenes Risiko!   
# use at your own risk. It works for me but it could destroy your heating if things go very wrong.
#                      

package main;

use strict;                         
use warnings;                       
use Time::HiRes qw(gettimeofday);   

#
# list of Readings / values that can explicitely be requested
# from the WP with the GET command
# more could be added from the hash %frameReadings if needed
#
my %WKRCD4_gets = ( 
   "Hzg-TempBasisSoll"   => "Hzg-TempBasisSoll",
   "WW-Temp-Soll"      => "Temp-WW-Soll"
);

# list of Readings / values that can be written to the WP
# I included just one for testing.
# more could be added but testing is required
#
my %WKRCD4_sets = ( 
   "Hzg-TempBasisSoll"   => "Hzg-TempBasisSoll"
);

# Definition of the values that can be read / written
# with the relative address, number of bytes and
# format to be used in sprintf when formatting the value
# unpack code to be used in pack / unpack commands
# min / max for setting values
#
my %frameReadings = (
'Versions-Nummer'        => { addr => 0x0000, bytes => 0x0002,                  unp => 'n' },
'Temp-Aussen'            => { addr => 0x0008, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Ruecklauf-Soll'    => { addr => 0x0014, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Ruecklauf'         => { addr => 0x0018, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Vorlauf'           => { addr => 0x001C, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-WW-Soll'           => { addr => 0x0020, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-WW'                => { addr => 0x0024, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Raum'              => { addr => 0x0028, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-WQuelle-Ein'       => { addr => 0x0030, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-WQuelle-Aus'       => { addr => 0x0034, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Verdampfer'        => { addr => 0x0038, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Kondensator'       => { addr => 0x003C, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Temp-Saugleitung'       => { addr => 0x0040, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Druck-Verdampfer'       => { addr => 0x0048, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Druck-Kondensator'      => { addr => 0x004C, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-TempEinsatz'        => { addr => 0x00F4, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 15.0, max => 20.0 },
'Hzg-TempBasisSoll'      => { addr => 0x00F8, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 20.0, max => 24.0 },
'Hzg-KlSteilheit'        => { addr => 0x00FC, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 15.0, max => 30.0 },
'Hzg-KlBegrenz'          => { addr => 0x0100, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-TempRlSoll'         => { addr => 0x0050, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-TempRlIst'          => { addr => 0x0054, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-TmpRaumSoll'        => { addr => 0x0105, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-RaumEinfluss'       => { addr => 0x0109, bytes => 0x0001,                  unp => 'C' },
'Hzg-ExtAnhebung'        => { addr => 0x010A, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => -5.0, max => 5.0 },
'Hzg-Zeit-Ein'           => { addr => 0x010E, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC'},
'Hzg-Zeit-Aus'           => { addr => 0x0111, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Hzg-AnhebungEin'        => { addr => 0x0114, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Hzg-AnhebungAus'        => { addr => 0x0117, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Hzg-St2Begrenz'         => { addr => 0x011A, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Hzg-Hysterese'          => { addr => 0x011E, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 1.0, max => 3.0  },
'Hzg-PumpenNachl'        => { addr => 0x0122, bytes => 0x0001,                  unp => 'C',  min => 0,   max => 120  },
'Klg-Abschaltung'        => { addr => 0x0123, bytes => 0x0001,                  unp => 'C' },
'Klg-Temp-Einsatz'       => { addr => 0x0124, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Klg-TeBasisSoll'        => { addr => 0x0128, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Klg-KlSteilheit'        => { addr => 0x012C, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Klg-KlBegrenz'          => { addr => 0x0130, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Klg-KlSollwert'         => { addr => 0x0058, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Klg-Temp-Rl'            => { addr => 0x005C, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Ww-Abschaltung'         => { addr => 0x0134, bytes => 0x0001 },
'Ww-Zeit-Ein'            => { addr => 0x0135, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Ww-Zeit-Aus'            => { addr => 0x0138, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Ww-Temp-Ist'            => { addr => 0x0060, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Ww-Temp-Soll'           => { addr => 0x013b, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 35, max => 55 },
'Ww-Hysterese'           => { addr => 0x0143, bytes => 0x0004, fmat => '%0.1f', unp => 'f<', min => 5,  max => 10},
'Uhrzeit'                => { addr => 0x0064, bytes => 0x0003, fmat => '%3$02d:%2$02d:%1$02d', unp => 'CCC' },
'Datum'                  => { addr => 0x0067, bytes => 0x0003, fmat => '%02d.%02d.%02d', unp => 'CCC'},
'BetrStundenKompressor'  => { addr => 0x006A, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'BetrStundenHzgPu'       => { addr => 0x006E, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'BetrStundenWwPu'        => { addr => 0x0072, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'BetrStundenSt2'         => { addr => 0x0076, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },
'Zeit'                   => { addr => 0x0064, bytes => 0x0006, fmat=> '%4$02d.%5$02d.%6$02d %3$02d:%2$02d:%1$02d', unp => 'CCCCCC'},
'SetBetriebsMode'        => { addr => 0x014E, bytes => 0x0003,              unp => 'N', pack => 'xC*'},
'Display-Zeile-1'        => { addr => 0x008E, bytes => 0x0002,              unp => 'n' },
'Display-Zeile-2'        => { addr => 0x0090, bytes => 0x0001,              unp => 'C' },
'Status-Gesamt'          => { addr => 0x00D2, bytes => 0x0001,              unp => 'C' },
'Status-Heizung'         => { addr => 0x00D4, bytes => 0x0003,                  unp => 'B24' },
'Status-Kuehlung'        => { addr => 0x00DA, bytes => 0x0003,                  unp => 'B24' },
'Mode-Heizung'           => { addr => 0x00DF, bytes => 0x0001,                  unp => 'B8' },
'Mode-Kuehlung'          => { addr => 0x00E0, bytes => 0x0001,                  unp => 'B8' },
'Mode-Warmwasser'        => { addr => 0x00E1, bytes => 0x0001,                  unp => 'B8' }
);

#
# FHEM module intitialisation
# defines the functions to be called from FHEM
#########################################################################
sub WKRCD4_Initialize($)
{
   my ($hash) = @_;

   require "$attr{global}{modpath}/FHEM/DevIo.pm";

   $hash->{ReadFn}  = "WKRCD4_Read";
   $hash->{ReadyFn} = "WKRCD4_Ready";
   $hash->{DefFn}   = "WKRCD4_Define";
   $hash->{UndefFn} = "WKRCD4_Undef";
   $hash->{SetFn}   = "WKRCD4_Set";
   $hash->{GetFn}   = "WKRCD4_Get";
   $hash->{AttrList} =
     "do_not_notify:1,0 loglevel:0,1,2,3,4,5,6 " . $readingFnAttributes;
}

#
# Define command
# init internal values, open device,
# set internal timer to send read command / wakeup
#########################################################################                           #
sub WKRCD4_Define($$)
{
   my ( $hash, $def ) = @_;
   my @a = split( "[ \t][ \t]*", $def );

   return "wrong syntax: define <name> WKRCD4 [devicename|none] interval"
     if ( @a < 3 );

   DevIo_CloseDev($hash);
   my $name = $a[0];
   my $dev  = $a[2];
   my $interval  = 60;
   
   if ( $dev eq "none" ) {
      Log3 undef, 1, "WKRCD4 device is none, commands will be echoed only";
      return undef;
   }
   
   if(int(@a) == 4) {
      $interval= $a[3];
      if ($interval < 20) {
         return "interval too small, please use something > 20, default is 60";
      }
   }

   $hash->{buffer}          = "";
   
   $hash->{DeviceName}       = $dev;
   $hash->{INTERVAL}         = $interval;

   $hash->{SerialRequests}      = 0;
   $hash->{SerialGoodReads}    = 0;
   $hash->{SerialBadReads}    = 0;

   # send wakeup string (read 2 values preceeded with AT)
   $hash->{LastRequestAdr}      = 8;
   $hash->{LastRequestLen}    = 4;
   $hash->{LastRequest}         = gettimeofday();
   my $ret = DevIo_OpenDev( $hash, 0, "WKRCD4_Wakeup" );
   
   # initial read after 3 secs, there timer is set to interval for update and wakeup
   InternalTimer(gettimeofday()+3, "WKRCD4_GetUpdate", $hash, 0);   

   return $ret;
}

#
# undefine command when device is deleted
#########################################################################
sub WKRCD4_Undef($$)   
{                     
   my ( $hash, $arg ) = @_;       
   DevIo_CloseDev($hash);         
   RemoveInternalTimer($hash);   
   return undef;                 
}   


#
# Encode the data to be sent to the device (0x10 gets doubled)
#########################################################################
sub Encode10 (@) {
   my @a = ();
   for my $byte (@_) {
      push @a, $byte;
      push @a, $byte if $byte == 0x10;
   }
   return @a;
}

#
# create a command for the WP as byte array
#########################################################################
sub WPCMD($$$$;@)
{
   my ($hash, $cmd, $addr, $len, @value ) = @_;
   my $name = $hash->{NAME};
   my @frame = ();
   
   if ($cmd eq "read") {
      @frame = (0x01, 0x15, Encode10($addr>>8, $addr%256), Encode10($len>>8, $len%256));   
   } elsif ($cmd eq "write") {
      @frame = (0x01, 0x13, Encode10($addr>>8, $addr%256), Encode10(@value));
   } else {
      Log3 $name, 3, "undefined cmd ($cmd) in WPCMD";
      return 0;
   }
   my $crc = CRC16(@frame);
   return (0xff, 0x10, 0x02, @frame, 0x10, 0x03, $crc >> 8, $crc % 256, 0xff);
}

#
# GET command
#########################################################################
sub WKRCD4_Get($@)
{
   my ( $hash, @a ) = @_;
   return "\"get WKRCD4\" needs at least an argument" if ( @a < 2 );

   my $name = shift @a;
   my $attr = shift @a;
   my $arg = join("", @a);
   
   if(!$WKRCD4_gets{$attr}) {
      my @cList = keys %WKRCD4_gets;
      return "Unknown argument $attr, choose one of " . join(" ", @cList);
   }

   # get Hash pointer for the attribute requested from the global hash
   my $properties = $frameReadings{$WKRCD4_sets{$attr}};
   if(!$properties) {
      return "No Entry in frameReadings found for $attr";
   }

   # get details about the attribute requested from its hash
   my $addr  = $properties->{addr};
   my $bytes = $properties->{bytes};
   Log3 $name, 4, sprintf ("Read %02x bytes starting from %02x for $attr", $bytes, $addr);

   # create command for WP
   my $cmd = pack('C*', WPCMD($hash, 'read', $addr, $bytes));

   # set internal variables to track what is happending
   $hash->{LastRequestAdr} = $addr;
   $hash->{LastRequestLen} = $bytes;
   $hash->{LastRequest}     = gettimeofday();
   $hash->{SerialRequests}++;

   Log3 $name, 4, "Get -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd);
   DevIo_SimpleWrite( $hash, $cmd , 0 );
   
   return sprintf ("Read %02x bytes starting from %02x", $bytes, $addr);
}
   
#
# SET command
#########################################################################
sub WKRCD4_Set($@)
{
   my ( $hash, @a ) = @_;
   return "\"set WKRCD4\" needs at least an argument" if ( @a < 2 );

   my $name = shift @a;
   my $attr = shift @a;
   my $arg = join("", @a);
   
   if(!defined($WKRCD4_sets{$attr})) {
      my @cList = keys %WKRCD4_sets;
      return "Unknown argument $attr, choose one of " . join(" ", @cList);
   }

   # get Hash pointer for the attribute requested from the global hash
   my $properties = $frameReadings{$WKRCD4_sets{$attr}};
   if(!$properties) {
      return "No Entry in frameReadings found for $attr";
   }

   # get details about the attribute requested from its hash
   my $addr  = $properties->{addr};
   my $bytes = $properties->{bytes};
   my $min   = $properties->{min};
   my $max   = $properties->{max};
   my $unp   = $properties->{unp};
   
    return "a numerical value between $min and $max is expected, got $arg instead"
        if($arg !~ m/^[\d.]+$/ || $arg < $min || $arg > $max);
   
   # convert string to value needed for command
   my $vp      = pack($unp, $arg);
   my @value = unpack ('C*', $vp);
   
   Log3 $name, 4, sprintf ("Write $attr: %02x bytes starting from %02x with %s (%s) packed with $unp", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp));
   my $cmd = pack('C*', WPCMD($hash, 'write', $addr, $bytes, @value));
   
   # set internal variables to track what is happending
   $hash->{LastRequestAdr} = $addr;
   $hash->{LastRequestLen} = $bytes;
   $hash->{LastRequest}     = gettimeofday();
   $hash->{SerialRequests}++;
   Log3 $name, 4, "Set -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd);
   DevIo_SimpleWrite( $hash, $cmd , 0 );
   
   return sprintf ("Wrote %02x bytes starting from %02x with %s (%s)", $bytes, $addr, unpack ('H*', $vp), unpack ($unp, $vp));
}



#########################################################################
# called from the global loop, when the select for hash->{FD} reports data
sub WKRCD4_Read($)
{
   my ($hash) = @_;
   my $name = $hash->{NAME};
   
   # read from serial device
   my $buf = DevIo_SimpleRead($hash);      
   return "" if ( !defined($buf) );

   # convert to hex string to make parsing with regex easier
   $hash->{buffer} .= unpack ('H*', $buf);   
   Log3 $name, 5, "Current buffer content: " . $hash->{buffer};

   # did we already get a full frame?
   if ($hash->{buffer} =~ "ff1002(.{4})(.*)1003(.{4})ff(.*)")
   {
      my $msg   = $1;
      my $frame = $msg . $2;
      my $crc   = $3;
      
      Log3 $name, 4, "Match msg: " .$msg . " " . $frame . " CRC " . $crc . " Rest " . $4;
      $hash->{buffer} = $4;

      # convert frame contents to byte array
      my @aframe = unpack ('C*', pack ('H*', $frame));
      
      # calculate CRC and compare with CRC from read
      my $crc2 = sprintf("%04x",CRC16(@aframe));
      if ($crc eq $crc2)
      {
         Log3 $name, 4, "CRC Ok.";
         $hash->{SerialGoodReads}++;
         
         # reply to read request ?
         if ($msg eq "0017") {
            my @data;
            for(my $i=0,my $offset=2;$offset<=$#aframe;$offset++,$i++)
            {
               # remove duplicate 0x10 (frames are encoded like this)
               if (($aframe[$offset]==16)&&($aframe[$offset+1]==16)) { $offset++; }
               $data[$i] = $aframe[$offset];
            }   
            Log3 $name, 4, "Parse with relative request start " . $hash->{LastRequestAdr} . " Len " . $hash->{LastRequestLen};
            # extract values from data
            parseReadings($hash, @data);         
         } elsif ($msg eq "0011") {
            # reply to write
         } else {
            Log3 $name, 3, "Unknown Msg type " . $msg . " in " . $hash->{buffer};
         }
      } else
      {
         Log3 $name, 3, "Bad CRC from WP: " . $crc . " berechnet: " . $crc2 . " Frame ". $frame;
         $hash->{SerialBadReads} ++;
      };
      @aframe = ();
   } else {
      Log3 $name, 5, "NoMatch: " . $hash->{buffer};
   };
   return "";
}

#
# copied from other FHEM modules
#########################################################################
sub WKRCD4_Ready($)
{
   my ($hash) = @_;

   return DevIo_OpenDev( $hash, 1, undef )
     if ( $hash->{STATE} eq "disconnected" );

   # This is relevant for windows/USB only
   my $po = $hash->{USBDev};
   my ( $BlockingFlags, $InBytes, $OutBytes, $ErrorFlags ) = $po->status;
   return ( $InBytes > 0 );
}

#
# send wakeup /at least my waterkotte WP doesn't respond otherwise
#########################################################################
sub WKRCD4_Wakeup($)
{
   my ($hash) = @_;
   my $name = $hash->{NAME};
   
   $hash->{SerialRequests}++;
   
   $hash->{LastRequestAdr} = 8;
   $hash->{LastRequestLen} = 4;
   $hash->{LastRequest}     = gettimeofday();
   
   my $cmd = "41540D100201150008000410037EA010020115003000041003FDC3100201150034000410037D90";
   DevIo_SimpleWrite( $hash, $cmd , 1 );
   
   Log3 $name, 5, "sent wakeup string: " . $cmd . " done.";
   return undef;
}

#
# request new data from WP
###################################
sub WKRCD4_GetUpdate($)
{
   my ($hash) = @_;
   my $name = $hash->{NAME};
   
   InternalTimer(gettimeofday()+$hash->{INTERVAL}, "WKRCD4_GetUpdate", $hash, 1);
   InternalTimer(gettimeofday()+$hash->{INTERVAL}/2, "WKRCD4_Wakeup", $hash, 1);

   $hash->{SerialRequests}++;
   
   my $cmd = pack('C*', WPCMD($hash, 'read', 0, 0x170));
   $hash->{LastRequestAdr} = 0;
   $hash->{LastRequestLen} = 0x170;
   $hash->{LastRequest}     = gettimeofday();
   DevIo_SimpleWrite( $hash, $cmd , 0 );
   
   Log3 $name, 5, "GetUpdate -> Call DevIo_SimpleWrite: " . unpack ('H*', $cmd);
   
   return 1;
}

#
# calculate CRC16 for communication with the WP
#####################################################################################################
sub CRC16
{
    my $CRC = 0;
    my $POLY  = 0x800500;

    for my $byte (@_, 0, 0) {
        $CRC |= $byte;
        for (0 .. 7) {
            $CRC <<= 1;
            if ($CRC & 0x1000000) { $CRC ^= $POLY; }
            $CRC &= 0xffffff;
        }
    }
    return $CRC >> 8;
}


#
# get Values out of data read
#####################################################################################################
sub parseReadings
{
    my ($hash, @data) = @_;
     my $name = $hash->{NAME};
 
   my $reqStart = $hash->{LastRequestAdr};
   my $reqLen    = $hash->{LastRequestLen};
   
   # get enough bytes?
    if (@data >= $reqLen)
    {
      readingsBeginUpdate($hash);
      # go through all possible readings from global hash
        while (my ($reading, $property) = each(%frameReadings))
        {
         my $addr  = $property->{addr};
         my $bytes = $property->{bytes};
         
         # is reading inside data we got?
            if (($addr >= $reqStart) &&
             ($addr + $bytes <= $reqStart + $reqLen))
            {
            my $Idx = $addr - $reqStart;
            # get relevant slice from data array
            my @slice = @data[$Idx .. $Idx + $bytes - 1];
            
            # convert according to rules in global hash or defaults
            my $pack   = ($property->{pack}) ? $property->{pack} : 'C*';
            my $unpack = ($property->{unp})  ? $property->{unp}  : 'H*';
            my $fmat   = ($property->{fmat}) ? $property->{fmat} : '%s';
            #my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice))) . " packed with $pack, unpacked with $unpack, (hex " . unpack ('H*', pack ('C*', @slice)) . ") format $fmat";
            my $value = sprintf ($fmat, unpack ($unpack, pack ($pack, @slice)));
            
            readingsBulkUpdate( $hash, $reading, $value );
            Log3 $name, 4, "parse set reading $reading to $value" if (@data <= 20);
            }
        }
      readingsEndUpdate( $hash, 1 );
   }
    else
    {
        Log3 $name, 3, "Data len smaller than requested ($reqLen) : " . unpack ('H*', pack ('C*', @data));
        return 0;
    }
}

1;


StefanStrobel

Anbei noch ein Screenshot mit Grafiken, die man aus den Daten so einer Wärmepumpe erzeugen kann

Dr. Boris Neubert

Hallo Stefan,

wenn Du das Modul bitte als Datei hier an Deinen Beitrag anhängst, packe ich es nach contrib.

Viele Grüße
Boris
Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

StefanStrobel

Anbei das Modul als Datei

Gruss und vielen Dank
    Stefan

Dr. Boris Neubert

Globaler Moderator, Developer, aktives Mitglied des FHEM e.V. (Marketing, Verwaltung)
Bitte keine unaufgeforderten privaten Nachrichten!

Mietzi67

Hallo Stefan,

sowas suche ich schon lange, vielen Dank! Hat auf Anhieb funktioniert.
Kannst Du bitte für Laien ein paar Codeschnipsel für die Plots zu Verfügung stellen?

Besten Dank,
Frank

StefanStrobel

Hallo Frank,

freut mich wenn das Modul auch bei Dir gut funktioniert.
Die neueste Version ist übrigens im contrib.
Die Plots kannst Du Dir am einfachsten selbst zusammenklicken. Das ist viel einfacher als es in manchen alten Posts scheint.
Du wählst einfach Dein Filelog der Wärmepumpe aus und clickst dann auf den Link "Create SVG plot"
Dann geht alles sehr elegant über den Plot-Editor (angehängtes Bild)

Gruss
   Stefan

Mietzi67

Hallo Stefan,

danke für den Tipp. Die WP loggt jetzt fleissig in FHEM-Plots.
Musste dabei so meine Erfahrungen mit Datenmengen sammeln. Mit komplettem Log erreicht man ca. 3MB in 24h, daraus zu plotten bringt die FB7390 ziemlich an die Grenze ihrer Rechenleistung :o . Also Logdateien splitten ist angesagt.

Was mir noch vorschwebt: ich möchte ein Übersichtsbild der WP mittels FLOORPLAN visualisieren (ähnlich wie in der Waterkotte Software).
Hast Du eine Anregung, wie man die Readings der Variablen dem FLOORPLAN übergeben kann? (möglichst einzeln auswählbar)

Viele Grüße,
Frank

StefanStrobel

Hallo Frank,

mit Floorplan habe ich leider auch noch nichts gemacht.
Wenn Du es hinbekommen hast, wäre es nett wenn Du es hier posten könntest :-)

Gruss
   Stefan

ak323

Hi Stefan,
ich will diesen Thread mal wieder hoch holen ...

Ich habe eine Waterkotte Ai1QE mit WWPR (Nachfolger vom CD4) und will diese über ModBus an FHEM anbinden.
Ich vermute Du nutzt auch ModBus RTU zur Kommunikation mit der Waterkotte ? Über RS485 oder RS232 ?

Zu Deinem Skrip habe ich eine Frage:
'Temp-Aussen'            => { addr => 0x0008, bytes => 0x0004, fmat => '%0.1f', unp => 'f<' },

"addr => 0x0008" ist die ModBus Adresse des Wertes welchen Du auslesen willst ? Korrekt ?
Beim WWPR sind die Adressen entsprechend anders aber auch 16 bit gem. Doku.

Ich habe eine Liste der Register des WWPR - theoretisch könnte ich Dein Script ja als Basis nehmen und nur die Register-Adressen anpassen ...  oder: hat das vielleicht schon mal jemand gemacht ?

Viele Grüße ak323
RaspberryPi 2 im 19" Rack mit 16x2 i2c LCD, FHEM, diverse HomeMatic, 1-Wire (8x DS18B20, 3x DS2408, 2x DS2413, 5x DS2401, DS2423 ATTiny) über DS9490R#, Waterkotte Ai1QE (WWPR) Wärmepumpe über Modbus, WH1080 über Signalduino, 433MHz Funksteckdosen, WiFi RGBWW via Tasmota, ...

PEPITO82

Hallo ak323,

ich habe auch eine Waterkotte Ai1QE mit WWPR plus Webinterface.
Zur Kommunikation benutze ich Modbus TCP Modul aus folgendem Faden: http://forum.fhem.de/index.php/topic,12655.0.html
Für mich war auch der Wiki-Artikel zum Dimplex Wärmepumpenmanager hilfreich: http://www.fhemwiki.de/wiki/Dimplex_W%C3%A4rmepumpenmanager

Ich kann damit alle Werte die ich sonst nur im Webinterface hatte abfragen.
Außerdem kann ich den Heizbetrieb bzw. Warmwasser an- und ausschalten.
Vor allem letzteres war mir wichtig, da der WWPR keine zwei voneinander getrennten Zeiträume zulässt.
Dies ist nun möglich.

Vielleicht helfen Dir die Infos ja ein wenig weiter.

Viele Grüße

Peter

Vielleicht helfen Dir ja diese Infos.

ak323

Danke Peter !

Das Webinterface für die Waterkotte ist nur so verdampt teuer !
Das Dimplex pcoWeb kostet 300€, Waterkotte soweit ich weiß mehr als 450€ netto ...

Ich wollte halt das Dimplex Modbus Interface LWPM410 nehmen ... gibts für 115€ ....

VG, ak323
RaspberryPi 2 im 19" Rack mit 16x2 i2c LCD, FHEM, diverse HomeMatic, 1-Wire (8x DS18B20, 3x DS2408, 2x DS2413, 5x DS2401, DS2423 ATTiny) über DS9490R#, Waterkotte Ai1QE (WWPR) Wärmepumpe über Modbus, WH1080 über Signalduino, 433MHz Funksteckdosen, WiFi RGBWW via Tasmota, ...

PEPITO82

Gibt es nicht vielleicht noch eine andere Variante, den Regler seriell auszulesen?

Habe meine Wärmepumpe gekauft als das neue Modell auf den Markt kam und das pcoweb als Dreingabe nachverhandelt.
Dieses wurde mir damals für 259 Euro netto angeboten.

StefanStrobel

Hallo,

Die CD4 Steuerung hatte leider noch kein Modbus.
Für Modbus habe ich aber im letzten Jahr ein neues Modul geschrieben, das sich recht leicht für die neueren Waterkotte Wärmepumpen verwenden lässt. Es funktioniert sowohl mit Modbus RTU über rs232 oder rs485 als auch mit Modbus TCP.
Siehe http://forum.fhem.de/index.php/topic,25315.0.html

Schau mal das Modul ModbusSET an. Es verwendet das generische Basismodul "Modbus" und definiert eigentlich nur die Register. Wenn Du die Daten für die neue Waterkotte  Wärmepumpe hast, kannst Du einfach ModbusSET als Vorlage nehmen. Alternativ könntest Du auch erst mal ein paar einzelne Werte mit dem Modul ModbusAttr definieren. Das basiert auf dem gleichen Basismodul. In der Commandref sind die Details beschrieben und im Wiki steht noch einiges mehr dazu:
http://www.fhemwiki.de/wiki/Modbus sowie http://www.fhemwiki.de/wiki/ModbusSET oder auch http://www.fhemwiki.de/wiki/ModbusAttr.
Ich persönlich würde ModbusSET nehmen und die Register anpassen. So sind auch die Module für verschiedene Stromzähler mit Modbus-Inteface entstanden.

Gruß
    Stefan

ak323

#14
Hallo Stefan.
Nach fast nem Jahr habe ich mir jetzt endlich eine Modbus Interface-Karte für meine Waterkotte Ai1QE gegönnt und eingebaut. WWPR natürlich...
Ich versuche jetzt die Modbus Kommunikation zum Auslesen der mir bekannten Register zum Laufen zu bekommen ...
Leider scheitere ich gerade ein wenig dran !

Meinen USB - RS485 Converter habe ich so eingebunden:
define ModbusRS485 Modbus /dev/ttyUSB0@19200,8,N,2
define Waterkotte ModbusAttr 1 30


Und meiner Waterkotte mit der ID1 ein Abfrageintervall von 30s gegeben ...

Jetzt komme ich aber nicht weiter die Register auszulesen. Bspw. ist die Außentemperatur im Register 1 abgelegt ..
Wie definier ich nun die Abfrage .. ?

Stehe auf dem Schlauch ... Danke für Deine Hilfe !

ak323
RaspberryPi 2 im 19" Rack mit 16x2 i2c LCD, FHEM, diverse HomeMatic, 1-Wire (8x DS18B20, 3x DS2408, 2x DS2413, 5x DS2401, DS2423 ATTiny) über DS9490R#, Waterkotte Ai1QE (WWPR) Wärmepumpe über Modbus, WH1080 über Signalduino, 433MHz Funksteckdosen, WiFi RGBWW via Tasmota, ...