Ankündigung

Einklappen
Keine Ankündigung bisher.

Neues Wiregate-Plugin für PID-Heizungsregelung - Tester gesucht

Einklappen
Dieses Thema ist geschlossen.
X
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [wiregate] Neues Wiregate-Plugin für PID-Heizungsregelung - Tester gesucht

    Hallo zusammen,

    in Vorbereitung auf die Inbetriebnahme nächsten Winter (dann soll das Haus fertig werden), habe ich einen Multi-PID-Heizungsregler als Wiregate-Plugin entwickelt.

    Name: "Heizungsregler.pl"

    Features:

    * Beliebig viele Räume, in diesen beliebig viele Heizkreisläufe.

    * Sensoren (pro Heizkreislauf oder pro Raum): Vorlauf, Rücklauf, Estrich, Luft. Auch mehrere Sensoren in jeder Kategorie möglich, über die dann gemittelt wird.

    * Falls die Vorlauftemperatur über eine KNX-GA steuerbar ist, kann der Regler auch dieses tun.

    * Vorteil einer PID-Regelung gegenüber PI ist (grundsätzlich), dass der D-Anteil "vorausschauend" ist, und zwar um die sog. "Vorhaltezeit". Das sollte (in der Theorie) Überschwinger vermeiden und den Regler etwas aggressiver machen, außerdem hat es Vorteile bei sehr trägen Heizungsformen (PT2-Regelstrecke, z.B. Fussbodenheizung). Ansonsten ist es wie ein PI-Regler.

    * Die Parameter (Proportionalbereich, Vorhaltezeit, Nachstellzeit, Wind-up-Begrenzung im Integralanteil) sind pro Raum einzeln festlegbar

    * Stellt man in einem Raum die Solltemperatur auf den speziellen Wert von -1 Grad, so beginnt ein Optimierungszyklus: die Heizung dreht maximal auf (besser mit kaltem Zimmer starten) und analysiert die daraus folgende Temperaturkurve ("Sprungantwort") des Raumes. Daraus werden die "optimalen" obigen Parameter ermittelt (hier war einiges Experimentieren nötig, aber jetzt isses glaub ich ok). Am Ende des Optimierungszyklus springt die Solltemperatur auf die letzte gewählte "normale" Solltemperatur zurück, die optimierten Werte für diesen Raum werden für alle Zukunft in das Konfigurationsfile zurückgeschrieben (stehen also auch nach einem Reset genau so zur Verfügung), und der Regler wechselt in den Regelmodus.

    * Als echter "Multi"-Regler kann das Plugin natürlich in einem Raum regeln, in einem anderen optimieren und in einem dritten ausgeschaltet sein.


    Entwicklungsstand:

    * BETA-Stadium (oder ALPHA, wie man es sehen will).

    * Da ich erstens noch kein Haus habe und zweitens nicht für jeden Testlauf Gas verheizen und tagelang warten wollte, habe ich eine Simulationsumgebung programmiert - ein Perl-Skript, das dem Plugin die Umgebung des wiregated-Daemons vorgaukelt sowie eine simulierte Uhr im Zeitraffer.

    * Die Simulation umfasst auch einen einfachen "KNX-Bus" vor (also knx_write/knx_read sowie $plugin_subscribe funktionieren). Daher läuft der gleiche Code des Plugins in der Simulation wie auch auf dem Wiregate - nur dass in der Simulation auf einer schnellen Linux-Maschine drei Tage auf zwei Minuten verkürzt werden. (Übrigens läuft auch ChrisMs Multi-RTR.pl in der Simulation ohne jede Anpassung).

    * Zwischendurch werden Fenster geöffnet (und dies über KNX auf dem Bus gemeldet), dann wird ein Kamin angeschaltet und heizt zu, der User verändert die Solltemperatur (KNX-Busmeldung), usw.

    * Die physikalischen Eigenschaften des zu heizenden Zimmers (Wasser heizt Estrich, Estrich heizt Raum, Raum kühlt nach außen ab) werden durch Runge-Kutta-Schritte der entsprechenden (einfachen) Differentialgleichungen simuliert.

    * Selbstverständlich funktioniert die Heizungsregelung in der Simulation prima - sonst würde ich hier nichts posten. Auch sind Vorteile der PID- vs PI-Regelung klar erkennbar, und selbstverständlich auch Vorteile der automatischen Optimierung über Sprungantwortanalyse.

    * Grafische Darstellungen (RRDs) sind im Regler noch nicht "drin". Auch fehlt ein Feature, das verschiedene Heizkreisläufe im gleichen Raum so regelt, dass der Fussboden gleichmäßig warm ist (aktuell werden alle Ventile eines Raumes gleich angesteuert).

    Der nächste Schritt wäre das Ausprobieren in einem realen Haus. Nur ist die Heizungsperiode vorbei :-(

    Frage: ist jemand, der aktuell den Wiregate zur Heizungssteuerung einsetzt, möglichst viele Temperaturfühler hat (Vor/Rücklauf, Estrich...) und vielleicht sogar eine Wärmepumpe mit über KNX einstellbarer Vorlauftemp., und der um diese Jahreszeit noch heizt, bereit, einen Test des neuen Plugins unter Echtbedingungen zu machen? Als "Vorgeschmack" kopiere ich unten mal das Konfigurationsfile ein (nicht über "eibshort" wundern, das ist nur meine Art, nackte GA-Adressen zu vermeiden, ich nutze einfach die GA-Namen über ein spezielles Lookup-Hash).

    Viele Grüße,
    Fry

    Code:
    #!/usr/bin/perl
    #
    # Konfiguration ##########################################
    #
    # Raeume, Messfuehler und Heizkreislaeufe definieren
    # Jeder Raum hat einen eigenen Regler. Die Bezeichnungen der Raeume sind
    # beliebig, sie duerfen aber keinen Underscore ("_") enthalten.
    #
    # Ueber mehrere Sensoren eines Raumes inklusive aller seiner Heizkreislaeufe
    # wird gemittelt (egal wie oft eine GA in der Struktur auftaucht, sie wird 
    # nur einfach gewichtet). 
    
    # Falls kein Sensor im Raum definiert ist oder antwortet, wird der Haussensor 
    # genommen.
    #
    # Falls inflow und outflow weder im Raum noch zentral im Haus definiert
    # oder verfuegbar sind, wird der global definierte fixe Spread angenommen. 
    # (Spread = Vorlauf- minus Ruecklauftemperatur = inflow-outflow). Fehlt auch
    # dieser, so faellt die Regelung aus, sobald der Spread mit den vorhandenen
    # Werten nicht ermittelt werden kann.
    #
    # Eine komplette, minimale Konfiguration fuer zwei Zimmer sieht daher aus:
    #
    #%house=(
    #    cycle=>60,
    #    spread=>20,
    #    Zimmer1=>{control=>'1/2/4', sensor=>'1/2/3'},
    #    Zimmer2=>{control=>'1/2/14', sensor=>'1/2/13'},
    #    );
    #
    # Hierbei gibt es genau eine Wunschtemperatur und einen Messfuehler pro Raum.
    # Der Spread ist dabei fix mit 20K angenommen.
    
    %house=(
       
    # Taktung des Reglers, Anzahl Sekunden zwischen Regleraufrufen
    # Es macht durchaus Sinn, den Regler haeufiger laufen zu lassen als die 
    # Sensoren ausgelesen werden. Das sollte zu einem ruhigeren Regler fuehren.
        cycle => 60,
    
    # Anzahl der Datenpunkte fuer Trendberechnung
        mindata => 15, 
    
    # Falls die Heizung ueber eine einstellbare Vorlauftemperatur verfuegt, 
    # so kann hier eine GA fuer die Wunsch-Vorlauftemperatur festgelegt werden,
    # sowie eine maximale Vorlauftemperatur. Vorsicht! Bitte nur benutzen, wenn 
    # die tatsaechliche Vorlauftemperatur auch gemessen werden kann!
    
       inflow_control => '1/2/3', # GA zum Setzen der Soll-Vorlauftemperatur
       inflow_max => 40, # maximale Soll-Vorlauftemperatur
    
    # Der Spread, oder die Spreizung, ist die Differenz zwischen Vorlauftemperatur
    # und Ruecklauftemperatur. Falls entsprechende Messfuehler entweder nicht
    # konfiguriert oder nicht auslesbar sind, wird der hausuebergreifende Wert
    # genommen, der hier steht. Falls in einem solchen Fall auch hausueber-
    # greifend kein Wert konfiguriert ist steht, faellt die Heizung aus.
    # Faustregel: Thermentemperatur minus Raumtemperatur
    #   inflow => '1/2/3', # hausweite Vorlauftemp, falls nicht raumweise messbar
    #   outflow => '1/2/3', # hausweite Ruecklauftemp, falls nicht raumweise messbar
        spread => 20, # hausweite Spreizung, falls alle Stricke reissen
    
    # Die Gruppenadressen fuer 
    #    sensor (Ist-Temperatur im Raum), 
    #    window (Fensterkontakte, dpt-Typ 1, Wert 1 bedeutet offen)
    #    floor (Estrichtemperatur),
    #    inflow (Vorlauftemperatur) und 
    #    outflow (Ruecklauftemperatur) 
    # koennen jeweils auf Raum- und/oder Heizkreislaufebene definiert
    # sein und koennen einzelne GAs oder Listen von GAs sein. Wenn alle diese
    # Messfuehler fehlen, so werden die hausweiten Sensoren oben genommen. 
    # Falls die auch fehlen, wird der fixe Spread weiter oben genommen.
    # Wenn der auch noch fehlt, arbeitet der Regler nicht.
    
        WZ => {     
            control => $eibshort{TS_WZ}{ga},  # GA zum Setzen der Solltemperatur
    
            # Raumtemperaturfuehler (Liste oder nur einer)
    #       sensor => ['1/2/3','1/2/3','1/2/3'], 
            sensor => $eibshort{TL_WZ_Sofa}{ga},
            
            # Vorlauftemperatur, kann auch pro Kreislauf definiert sein
            inflow => $eibshort{TO_Zentralvorlauf}{ga}, 
    
            # Fensterkontaktabfrage
            window => [$eibshort{KF_WZ_West_L}{ga}, $eibshort{KF_WZ_West_R}{ga}], 
            
            # Heizkreislaeufe, aktuell werden alle gleich gesteuert 
            # (Ausgleich fuer gleiche Estrichtemp. noch nicht implementiert)
            circ => { 
                WZ_Nord => { 
                    actuator => $eibshort{VS_WZ_Mitte}{ga}, # Stellventil
                    floor => $eibshort{TE_WZ_Mitte}{ga}, # Estrichfuehler
    #               outflow => '1/2/3', # Ruecklauf
                }, 
    #           WZ_Mitte => { 
    #               actuator => '1/2/3', # Stellventil
    #               outflow => '1/2/3', # Ruecklauf
    #           }, 
    #           WZ_Sued => { 
    #               actuator => '1/2/3', # Stellventil
    #               outflow => '1/2/3', # Ruecklauf
    #           },  
            },
        },
        );

    #2
    Hi Fry,

    das klingt toll. Ich bin beeindruckt -nicht nur bzgl. des Reglers selbst, sondern auch über das Drumherum (Simulationsumgebung). Toll!

    Ich heize zwar noch -aber nur weil die Feuchtigkeit raus muss.
    Das bedeutet gleichzeitig aber: Das Haus ist gerade erst fertig -naja, die meisten würden sagen, dass es nicht fertig ist. Und das ist die Krux: Momentan hab ich absolut keine Zeit zum Testen. Aber Bis zum Winter ist ja nicht mehr so lang!

    Ein großes Danke jedenfalls!

    Gruß,
    Hendrik

    Kommentar


      #3
      Lieber Hendrik,
      danke für dein Feedback, es freut mich sehr und motiviert zu weiteren Beiträgen in die Community!

      Im nächsten Winter setze ich das Plugin dann schon selbst ein, und jeder (naja, so viele ich supporten kann/will) ist eingeladen es auszuprobieren. Bis dahin baue ich mein Haus (aktuell wird der Keller gemauert). Ich stelle das Plugin jetzt noch nicht ins SVN wegen des Entwicklungsstadiums, am Ende wird es aber dort landen (und natürlich GPLed sein).

      Am Rande: ich habe beim Experimentieren festgestellt, dass bei trägen Systemen (ist ja in der Simulation einstellbar) der D-Anteil sehr wichtig ist, um beim Aufheizen vorausschauend und rechtzeitig die Heizung runterzuregeln, bevor sie überschießt (das kann ein PI-Regler prinzipbedingt nicht tun). Daher kommt wohl auch die Aussage, die man in der Literatur findet, dass ein PI-Regler für PT2-Regelstrecken suboptimal ist. Wichtig ist die korrekte Einstellung der Vorhaltezeit - die ermittelt das Plugin durch Analyse der Sprungantwort ganz von selbst. (Die Optimierungsroutine ist alleine ca. 50% des gesamten Codes).

      Die Skalierung der Ventilstellung mit unterschiedlicher Spreizung (Differenz Vor-/Rücklauftemp) ist hingegen relativ straightforward, aber auch wesentlich. Der Regler ist dabei durchaus in der Lage, die Vorlauftemperatur auf unter 30 Grad zu drücken. Für Wärmepumpenbesitzer mit KNX-Ansteuerung ziemlich wichtig, weil die Wirkzahl einer Wärmepumpe reziprok mit der Temperaturdifferenz der Pumpstrecke skaliert (folgt direkt aus dem 2. Hauptsatz der Thermodynamik)!

      Wo ich gerade aus dem Nähkästchen plaudere, noch ein Kommentar für alle Leser: man liest immer wieder "Fussbodenheizungen sind träge, deshalb lohnt sich eine Regelung gar nicht, einfach die Heizung fix bzw. einfach nach der Aussentemperatur einstellen, alles andere ist Quark". DAS IST EIN MÄRCHEN - ein Märchen mit dem wahren Kern, dass viele existierende Regler (das sind fast immer reine PIs, seltener Zweipunktregler, fast nie PIDs) nicht gut mit den trägen Regelstrecken zurecht kommen, und damit sie beim Heizen nicht total überschießen, gedrosselt eingestellt werden, ergo sehr langsam ansprechen. Die Antwort ist ein PID-Regler!

      Schönen Abend!
      Fry

      PS. Falls jemand in anderem Kontext nochmal Bedarf an einer Plugin-Simulation "im Zeitraffer" hat - für die Heizungsregelung war das unverzichtbar - würde ich gerne davon hören und helfen.

      Kommentar


        #4
        Hallo Fry,

        ich würde das Plugins sehr gerne testen.

        Konfiguration: Wohnzimmer mit 3 Estrichtemperaturfühlern, Parkett damit Temperaturbegrenzung für den Parkett, TS3 als Raumtemperaturfühler.

        Mein Problem: Durch Sonneneinstrahlung, geht die Raumtemperatur durch die Decke (hohe Transiente)

        Meine derzeitige Lösung: 2 Stunden vor der (voraussichtlichen) Sonneeinstrahlung, die Solltemperatur senken ;(

        Das ganze wird durch einen MTD RTR geregelt (PI).

        Ich glaube man könnte das durch einen D - Anteil im Regler besser machen.

        Also Konfig neu: WG - Plugin mit % Vorgabe an MTD - Heizungsaktor.

        vG
        Wolfgang

        Kommentar


          #5
          Hallo Wolfgang,
          eine Temperaturbegrenzung fürs Parkett ist im Plugin noch nicht implementiert. Bei welcher Temperatur liegt diese Begrenzung denn?
          Grüße,
          Fry

          PS. Ohne Modifikation des Plugins ließe sich die Begrenzung aber dadurch umsetzen, dass man die Vorlauftemperatur auf genau diesen maximalen Wert stellt. Mit Nachteilen für die Zimmer, in denen kein Parkett liegt - die werden dann auch langsamer geheizt.
          Was für eine Art Heizung hast du eigentlich?

          Kommentar


            #6
            Hallo Fry

            Ich hätte auf jeden Fall Interesse für die nächste Heizperiode Dein Script einzusetzen, da FBH. Wiregate vorhanden, Fühler noch nicht angeschlossen, ich werde aber bloss Luftfühler pro Raum haben und Vorlauf-/Rücklauf direkt bei der Heizung.

            Gruss
            Christian

            Kommentar


              #7
              Werds auch mal einbauen aber momentan ist nicht mehr soviel mit heizen
              VL *könnte* ich dynamisch ändern, hat sich mit den bisherigen Ansätzen aber nicht wirklich bewährt, höchstens ein leichtes anheben in der Übergangszeit rein aus Komfort-Gründen..

              Da spielen halt wahnsinnig viele Faktoren mit, die "Universal-lösung" gibts da vermutlich nicht.

              Makki
              EIB/KNX & WireGate & HS3, Russound,mpd,vdr,DM8000, DALI, DMX
              -> Bitte KEINE PNs!

              Kommentar


                #8
                Zitat von makki Beitrag anzeigen
                VL *könnte* ich dynamisch ändern, hat sich mit den bisherigen Ansätzen aber nicht wirklich bewährt, höchstens ein leichtes anheben in der Übergangszeit rein aus Komfort-Gründen..
                Was für eine Art Heizung (Gas, WP...) hast du und welcher Hersteller? Ich bin nämlich festgelegt auf Wolf Gastherme. Die Wölfe machen zwar super Qualität und gewinnen lauter Preise mit ihren Produkten, haben aber leider keine Patentlösung in Bezug auf KNX im Programm. Da würde es mich freuen, wenn jemand eine Lösung in Bezug auf Vorlauftemp.-regelung hätte.

                (Am Rande: aus physikalischen Gründen bewirkt die Absenkung der VL-Temperatur bei modernen Brennwertthermen keine so gewaltige Energieersparnis. Ein wenig aber schon, und die würde ich gerne mitnehmen. Bei Wärmepumpen ist das anders, da macht es _richtig viel_ aus.)

                Grüße, Fry

                Kommentar


                  #9
                  Zitat von Hennessy Beitrag anzeigen
                  Ich hätte auf jeden Fall Interesse für die nächste Heizperiode Dein Script einzusetzen, da FBH. Wiregate vorhanden, Fühler noch nicht angeschlossen, ich werde aber bloss Luftfühler pro Raum haben und Vorlauf-/Rücklauf direkt bei der Heizung.
                  Ok, prima, dann also bis zum Winter. Ich hatte ja nur die vage Hoffnung, dass irgendwo in den Bergen (bei den sieben Zwergen) jetzt noch jemand ordentlich heizt. Den hätte ich dann als Betatester "rekrutiert".

                  VG,
                  Fry

                  Kommentar


                    #10
                    Hallo Fry,

                    Zitat von Fry Beitrag anzeigen
                    Hallo Wolfgang,
                    eine Temperaturbegrenzung fürs Parkett ist im Plugin noch nicht implementiert. Bei welcher Temperatur liegt diese Begrenzung denn?
                    der Wert liegt bei 27°C Oberlächentemperatur also umra 28.5 °C Estrichtemperatur. Das Ganze ist eine Standard Warmwasserheizung (Gas - Therme) mit Heizkörpern im Rest des Hauses und einer Konstant-Temperatur - Regelung z.zT auf 40°C eingestellt für die FBH im Wohnzimmer.

                    Das Wohnzimmer hat insgesammt 6 FB - Heizkreise, von denen jeweils 2 Kreise auf einem Ausgang des Heizungsaktors liegen.

                    Also 1 Sollwert, 1 Istwert Raumtemperatur, 3 x PWM - Kreise ansteuern, 3 x Estrichtemperatur messen, Fensterkontakt auswerten.


                    Im Moment heize ich auch noch, ich wohne in Bayerns Sibirien!

                    ... Und so ein paar rudimentäre Kenntnisse sind aus der Regelungstechnik Vorlesung auch noch da (ist halt schon 20 Jahre her )

                    vG
                    Wolfgang

                    Kommentar


                      #11
                      Zitat von Fry Beitrag anzeigen
                      Was für eine Art Heizung (Gas, WP...) hast du und welcher Hersteller?
                      idm WP ("alt", 2008) , Grundwasser; Wie schon angemerkt ist das eine ganz andere Nummer als eine Gastherme, hier spielt jedes ° VL ein dicke Rolle wo man sich bei ner Gastherme evtl. gut ein Ei drauf pellen kann..
                      Daher habe ich eher darauf optimiert möglichst geringen VL und möglichst grosse Ventilöffnung in der Heizperiode in relevanten Räumen zu haben..

                      Makki
                      EIB/KNX & WireGate & HS3, Russound,mpd,vdr,DM8000, DALI, DMX
                      -> Bitte KEINE PNs!

                      Kommentar


                        #12
                        Also... hier das Plugin (ca. 750 Zeilen, hoffe das Forum macht das mit...)

                        "OHNE GEWEHR" :-)

                        und

                        "VORSICHT BETA".

                        Plugin in /etc/wiregate/plugin/generic/Heizungsregler.pl:

                        Code:
                        ##################
                        # Heizungsregler #
                        ##################
                        # Wiregate-Plugin
                        # (c) 2012 Fry under the GNU Public License
                        
                        # $plugin_info{$plugname.'_cycle'}=0; return "deaktiviert";
                        
                        use POSIX qw(floor);
                        use Math::Round qw(nearest);
                        
                        # Hilfs-Hash eibshort initialisieren
                        my %eibshort;
                        for my $ga (keys %eibgaconf)
                        {
                            next unless defined $eibgaconf{$ga}{'name'};
                            next unless $eibgaconf{$ga}{'name'}=~/^(\S+)/;
                            my $short=$1;
                            $eibshort{$short}=$eibgaconf{$ga};
                            $eibshort{$short}{ga}=$ga;
                            $eibshort{$ga}=$short;
                        }
                        
                        # Aufrufgrund ermitteln
                        my $event=undef;
                        if (!$plugin_initflag) 
                        { $event='restart'; } # Restart des daemons / Reboot
                        #elsif ((stat('/etc/wiregate/plugin/generic/' . $plugname))[9] > time()-2) 
                        # ab PL30:
                        elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'})
                        { $event='modified'; } # Plugin modifiziert
                        elsif (%msg) { $event='bus'; } # Bustraffic
                        #elsif ($fh) { $event='socket'; } # Netzwerktraffic
                        else { $event='cycle'; } # Zyklus
                        
                        # Konfigurationsfile einlesen
                        my $conf=$plugname; $conf=~s/\.pl$/.conf/;
                        $conf="/etc/wiregate/plugin/generic/conf.d/$conf";
                        my %house=();
                        my $err=read_from_house_config();
                        return $err if $err;
                        
                        # Initialisierung
                        my %dyn=();
                        my $retval='';
                        my $t=time();
                        
                        if($event=~/restart|modified/)
                        {
                            # Alle Controller-GAs abonnieren, Reglerstati initialisieren
                            for my $k (grep /^$plugname\_/, keys %plugin_info)
                            {
                                delete $plugin_info{$k};
                            }
                        
                            for my $r (grep ref($house{$_}), keys %house)
                            {
                                $plugin_subscribe{$house{$r}{control}}{$plugname}=1;
                                RESET($r);
                            }
                        
                            $plugin_info{$plugname.'_cycle'}=$house{cycle}; 
                        
                            store_to_plugin_info(\%dyn);
                        
                            $event='cycle';
                        }
                        
                        %dyn=recall_from_plugin_info();
                        
                        # Zyklischer Aufruf - Regelung
                        if($event=~/cycle/)
                        { 
                            my $Vreq=undef;
                            for my $r (grep ref($house{$_}), keys %house)
                            {
                                if($dyn{$r}{mode} eq 'ON')
                                {
                                    # PID-Regler
                                    my ($T,$T0,$U,$Vr)=PID($r); 
                                    $retval.=sprintf "$r\->%.1f(%.1f)%d%% ", $T?$T:"T?", $T0, 100*$U;
                                    $Vreq=$Vr if defined $Vr && (!defined $Vreq || $Vr>$Vreq);
                                }
                                elsif($dyn{$r}{mode} eq 'OPTIMIZE')
                                {
                                    # Optimierung der PID-Parameter durch Ermittlung der Sprungantwort
                                    $retval.="$r\->".OPTIMIZE($r); 
                                    $Vreq=$house{inflow_max} if defined $house{inflow_max};
                                }
                                elsif($dyn{$r}{mode} eq 'OFF')
                                {
                                    $retval.="$r\->OFF ";     
                                }
                            }
                            
                            if(defined $Vreq)
                            {
                                $Vreq=$house{inflow_max} if defined $house{inflow_max} && $Vreq>$house{inflow_max};
                                knx_write($house{inflow_control},$Vreq,9.001) if defined $house{inflow_control};
                                $retval.=sprintf "Vreq=%d", $Vreq;
                            }
                        
                            $retval=~s/\s*$//; # Space am Ende entfernen
                        }
                        elsif($event=~/bus/)
                        {
                            return if $msg{apci} eq 'A_GroupValue_Response';
                        
                            # Aufruf durch GA - neue Wunschtemperatur
                            my $ga=$msg{dst};
                        
                            # erstmal den betroffenen Raum finden
                            my @rms=(grep ref($house{$_}) && $house{$_}{control} eq $ga, keys %house);
                            my $r=shift @rms;
                        
                            # $r ist undef falls obige Schleife fertig durchlaufen wurde
                            if(defined $r)
                            {
                                my $T0=0;
                                $T0 = $msg{value} if defined $msg{value};
                                my $mode=$dyn{$r}{mode};
                        
                                # Jemand moechte einen Sollwert wissen
                                if($msg{apci} eq 'A_GroupValue_Read')
                                {
                                    $T0=$dyn{$r}{T0};
                                    $T0=$dyn{$r}{T0old} if $dyn{mode} eq 'OPTIMIZE';
                                    knx_write($ga,$T0,9.001); 
                                    return;
                                }
                                
                                # spezielle Temperaturwerte sind 0=>OFF und -1=>OPTIMIZE
                                if($T0==0)
                                {
                                    RESET($r); 
                                    writeactuators($r,0); 
                                    $dyn{$r}{mode}='OFF';
                                    $retval.="$r\->OFF";
                                }
                                elsif($T0==-1)
                                {
                                    return if $dyn{$r}{mode} eq 'OPTIMIZE'; # Entprellen
                        
                                    # Initialisierung der Optimierungsfunktion
                                    $dyn{$r}{mode}='OPTIMIZE';
                                    $dyn{$r}{T0old}=$dyn{$r}{T0};
                                    writeactuators($r,0); 
                                    my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
                        
                                    $retval.=sprintf "$r\->OPT", $T;
                                }
                                else # neue Wunschtemperatur
                                {
                                    return if $dyn{$r}{T0} == $T0; # Entprellen
                                    
                                    RESET($r) if $mode eq 'OPTIMIZE'; # Optimierung unterbrochen
                                    $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
                                    $dyn{$r}{T0}=$T0;
                                    my ($T,$T0,$U,$Vr)=PID($r); 
                                    $retval.=sprintf "$r\->%.1f(%.1f)%d%%", $T, $T0, 100*$U;
                                }
                            }
                            else  
                            {
                                # GA-Abonnement loeschen
                                delete $plugin_subscribe{$ga}{$plugname};
                            }
                        }
                        
                        # Speichere Statusvariablen aller Regler
                        store_to_plugin_info(\%dyn);
                        
                        return $retval eq '' ? undef : $retval;
                        
                        
                        ########## Datenpersistenz - Speichern und Einlesen ###############
                        
                        sub store_to_plugin_info
                        {
                            my $dyn=shift;
                        
                            # Alle Laufzeitvariablen im Hash %{$dyn} 
                            # in das (flache) Hash plugin_info schreiben
                            for my $r (keys %{$dyn})
                            {
                                for my $k (grep /^$plugname\_$r\_(temps|times|Uvals)_/, keys %plugin_info)
                                {
                                    delete $plugin_info{$k};
                                }
                        
                                for my $v (keys %{$dyn->{$r}})
                                {
                                    next if $v=~/^(temps|times|Uvals)$/; # Arrays
                                    $plugin_info{$plugname.'_'.$r.'_'.$v}=$dyn->{$r}{$v};
                                }
                        
                                for my $array (qw(temps times Uvals))
                                {
                                    my $arr=$dyn->{$r}{$array};
                                    next unless $arr;
                                    for my $i (0..$#{$arr})
                                    {
                                        $plugin_info{$plugname.'_'.$r.'_'.$array.'_'.$i}=$arr->[$i];
                                    }
                                }
                            }
                        }
                        
                        sub recall_from_plugin_info
                        {
                            my %dyn=();
                        
                            for my $k (grep /^$plugname\_/, keys %plugin_info)
                            {
                                next unless $k=~/^$plugname\_([^_]+)\_(\S+)$/; 
                                my $r=$1; my $v=$2; 
                        
                                unless($v=~/^(temps|times|Uvals)_([0-9]+)$/)
                                {
                                    $dyn{$r}{$v}=$plugin_info{$k};
                                }
                                else
                                {
                                    my $array=$1; my $i=$2;
                                    $dyn{$r}{$array}[$i]=$plugin_info{$k};
                                }
                            }
                        
                            return %dyn;
                        }
                        
                        sub read_from_house_config
                        {
                            open CONFIG, "<$conf" || return "no config found";
                            my @lines = <CONFIG>;
                            close CONFIG;
                            eval("@lines");
                            return "config error" if $@;
                        }
                        
                        sub store_to_house_config
                        {
                            my $r=shift; # der betreffende Raum im Haus
                        
                            open CONFIG, ">>$conf";
                            print CONFIG "\$house{$r}{pid}={";
                            for my $k (sort keys %{$house{$r}{pid}})
                            {
                                print CONFIG sprintf "$k=>%.2f, ", $house{$r}{pid}{$k} unless $k eq 'date';
                                print CONFIG "$k=>'$house{$r}{pid}{date}'," if $k eq 'date';
                            }
                            print CONFIG "};\n";
                            close CONFIG;
                        }
                        
                        ########## Kommunikation mit Sensoren und Aktoren ###############
                        
                        sub readsensors
                        {
                            my $r=shift; # interessierender Raum
                            my @substructures=();
                        
                            push @substructures, values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
                            push @substructures, $house{$r};
                        
                            my %T=();
                            my %R=();
                        
                            for my $type (qw(sensor inflow floor outflow window))
                            {
                                my $dpt=$type eq 'window' ? 1 : 9;
                        
                                # Alle Sensoren eines Typs im gesamten Raum einlesen
                                for my $ss (@substructures)
                                {
                                    if(defined $ss->{$type})
                                    {
                                        my $sensorlist=(ref $ss->{$type})?$ss->{$type}:[$ss->{$type}];
                                        for my $s (@{$sensorlist})
                                        {
                                            unless(defined $T{$type}{$s})
                                            {
                                                $T{$type}{$s}=knx_read($s,$house{cycle},$dpt);
                                                delete $T{$type}{$s} unless defined $T{$type}{$s};
                                            }
                                        }
                                    }
                                }
                        
                                # Ueber alle Sensoren mitteln, dabei wird jeder Sensor genau einmal
                                # beruecksichtigt, auch wenn er in der Konfiguration mehrfach steht
                                my $n=0;
                                for my $k (keys %{$T{$type}})
                                {   
                                    if(defined $T{$type}{$k}) 
                                    {
                                        if($type eq 'window')
                                        {
                                            $R{$type}=1 if int($T{$type}{$k})==1;
                                        }
                                        else
                                        {
                                            $R{$type}+=$T{$type}{$k};
                                            $n++;
                                        }
                                    }
                                }
                                $R{$type}/=$n if defined $R{$type} && $type ne 'window';
                            }
                        
                            # Falls Fensterkontakte nicht lesbar -> Fenster als geschlossen annehmen
                            $R{window}=0 unless defined $R{window};
                        
                            # outflow (Ruecklauf) und floor (Estrich) nehmen wir als gleich an,
                            # falls nicht beide Werte vorhanden sind. Das sollte immer noch besser
                            # sein als der globale Hauswert, um danach den Spread zu berechnen.
                            unless(defined $R{outflow} && defined $R{floor})
                            {
                                $R{outflow}=$R{floor} if defined $R{floor};
                                $R{floor}=$R{outflow} if defined $R{outflow};
                            }
                        
                            # Falls Vor- oder Ruecklauf nicht fuer den Raum definiert,
                            # nehmen wir die Hauswerte - falls diese verfuegbar sind
                            for my $type (qw(inflow outflow))
                            {
                                if(!defined $R{$type} && defined $house{$type})
                                {
                                    $R{$type} = knx_read($house{$type},$house{cycle},9);
                                    delete $R{$type} unless $R{$type};
                                }
                            }
                        
                            # Jetzt Spread (Spreizung) berechnen, falls alle Daten verfuegbar
                            if(defined $R{inflow} && defined $R{outflow})
                            {
                               $R{spread}=$R{inflow}-$R{outflow};
                            }
                        
                            # und wenn alle Stricke reissen, bleibt der vorkonfigurierte Wert
                            $R{spread}=$house{spread} unless defined $R{spread};
                        
                            return @R{qw(sensor inflow floor outflow spread window)};
                        }
                        
                        sub writeactuators
                        {
                            my $r=shift; # Raum mit Substruktur
                            my $U=shift; # Ventileinstellung
                        
                        #    plugin_log($plugname, "Trying to write $r, $U");
                            
                            my @substructures=values %{$house{$r}->{circ}} if defined $house{$r}->{circ};
                            push @substructures, $house{$r};
                        
                            for my $ss (@substructures)
                            {
                                if(defined $ss->{actuator})
                                {
                                    unless(ref $ss->{actuator})
                                    {
                                        knx_write($ss->{actuator},100*$U,5.001); # DPT NOCH UNKLAR ########
                                    }
                                    else
                                    {
                                        for my $s (@{$ss->{actuator}})
                                        {
                                            knx_write($s,100*$U,5.001);
                                        }
                                    }
                                }
                            }
                        #    plugin_log($plugname, "Done trying to write $r, $U");
                        }
                        
                        ########## PID-Regler #####################
                        
                        sub RESET
                        {
                            my $r=shift; # zu regelnder Raum im Haus
                            
                            $dyn{$r} = {
                                mode=>'OFF', T0=>20, Told=>0, told=>$t, IS=>0, DF=>0, 
                                temps=>[], times=>[], Uvals=>[], U=>0
                                };
                        }
                        
                        sub PID
                        {
                            my $r=shift; # zu regelnder Raum im Haus
                            my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
                        
                            # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
                            my ($mode,$T0,$Told,$told,$IS,$DF,$temps,$times,$Uvals,$U) 
                                = @{$dyn{$r}}{qw(mode T0 Told told IS DF temps times Uvals U)};
                         
                            return ($T,$T0,$U,0) unless $T && $spread; 
                        
                            # Regelparameter einlesen
                            my ($Tv,$Tn,$lim,$prop,$refspread)=(5,30,1,1,10); # Defaults
                        
                            ($Tv,$Tn,$lim,$prop,$refspread)
                                =@{$house{$r}{pid}}{qw(Tv Tn lim prop refspread)}
                                if defined $house{$r}{pid};
                        
                            $Tv*=60; $Tn*=60; # in Sekunden umrechnen
                        
                            # Anzahl Datenpunkte fuer Steigungsberechnung
                            my $S1=12; $S1=$house{mindata} if defined $house{mindata};
                         
                            # Anzahl Datenpunkte fuer Ermittlung neuer Vorhaltetemperatur 
                            my $S2=$S1;
                        
                            push @{$temps},$T; shift @{$temps} while @{$temps}>$S1;
                            push @{$times},$t; shift @{$times} while @{$times}>$S1;
                        
                            if($window)
                            {
                                $U=0; # Heizung aus falls Fenster offen 
                                push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;        
                        
                                $dyn{$r} = {
                                    mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF, 
                                    temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
                                };
                                
                                writeactuators($r,$U); 
                                return ($T,$T0,$U,0); 
                            }
                        
                            # Skalierung fuer aktuellen Spread
                            my $coeff = $refspread/($spread*$prop);
                            
                            # Proportionalteil (P)
                            my $P = $T0 - $T;
                            
                            # Integralteil (I)
                            $IS += $P * ($t - $told) / $Tn;
                            
                            # kein negativer I-Anteil bei reiner Heizung (nur fuer Klimaanlage erforderlich)
                            $IS=0 if $IS<0; 
                        
                            # Begrenzung des I-Anteils zur Vermeidung von Ueberschwingern ("wind-up")
                            $IS=+$lim/$coeff if $IS>+$lim/$coeff;
                            
                            # Differentialteil (D) - gemittelt wegen moeglichem Sensorrauschen
                            $S1=scalar(@{$times});
                            if($S1>=2)
                            {
                                my ($SX,$SX2,$SY,$SXY)=(0,0,0,0);
                                for my $i (0..$S1-1)
                                {
                                    my $time=$times->[$i]-$times->[0];
                                    $SX+=$time;
                                    $SX2+=$time*$time;
                                    $SY+=$temps->[$i];
                                    $SXY+=$time*$temps->[$i];
                                }
                                $DF = - $Tv * ($S1*$SXY - $SX*$SY)/($S1*$SX2 - $SX*$SX);
                            }
                        # Fuer den Fall S1==2 fuehrt die obige Regression zum gleichen Ergebnis wie:
                        #    $DF = - $Tv * ($T - $Told) / ($t - $told);
                           
                            # und alles zusammen, skaliert mit der aktuellen Spreizung
                            $U = ($P + $IS + $DF) * $coeff;
                            
                            # Stellwert begrenzen auf 0-1
                            $U=1 if $U>1; 
                            $U=0 if $U<0;
                            push @{$Uvals}, $U; shift @{$Uvals} while @{$Uvals}>$S2;    
                        
                            # Wunsch-Vorlauftemperatur ermitteln
                            my $Vr=$V;
                            if(defined $Vr)
                            {
                                $Vr=$T0+3 if $Vr<$T0+3;
                                my $Uavg=0; $Uavg+=$_ foreach (@{$Uvals}); $Uavg/=scalar(@{$Uvals});
                        
                                $Vr+=1 if $Uavg>0.9;
                                $Vr-=1 if $Uavg<0.6 && $V>$T0+6 && $spread>6;
                                $Vr-=1 if $Uavg<0.75 && $V>$T0+5 && $spread>5;
                                $Vr-=1 if $Uavg<0.7 && $V>$T0+4 && $spread>4;
                                $Vr-=1 if $Uavg<0.6 && $V>$T0+3 && $spread>3;
                            }
                        
                            # Variablen zurueckschreiben
                            $dyn{$r} = {
                                mode=>$mode, T0=>$T0, Told=>$T, told=>$t, IS=>$IS, DF=>$DF, 
                                temps=>$temps, times=>$times, Uvals=>$Uvals, U=>$U
                            };
                            
                            # Ventil einstellen 
                            writeactuators($r,$U); 
                            
                            # Ist, Soll, Stellwert, Spread, Wunsch-Vorlauftemp.
                            return ($T,$T0,$U,$Vr); 
                        }
                        
                        ########## Optimierungsroutine #####################
                        
                        sub OPTIMIZE
                        {
                            my $r=shift;
                            my ($T,$V,$E,$R,$spread,$window)=readsensors($r);
                        
                            # Ohne Temperaturmessung und Spread keine Regelung -> aufgeben
                            return "(OPT) " unless defined $T && defined $spread; 
                        
                            # Praktische Abkuerzungen fuer Statusvariablen
                            my ($mode,$phase,$T0old) = @{$dyn{$r}}{qw(mode phase T0old)};
                        
                            # Falls Fenster offen  -> Abbruch, Heizung aus und Regler resetten
                            if($window)
                            {
                                if($phase ne 'COOL')
                                {
                                    RESET($r);
                                    $dyn{$r}{mode}='ON'; 
                                    $dyn{$r}{T0}=$T0old;
                                    return "FAILED:WINDOW ";
                                }
                                else
                                {
                                    # Tn, Tv, prop und refspread wurden am Ende der HEAT-Periode bereits berechnet
                                    # Wir nutzen die "cooling"-Periode sowieso nicht.
                                    # Also Parameter ins Konfig-File schreiben.
                                    my ($Tn, $Tv, $prop, $refspread) = @{$dyn{$r}}{qw(Tn Tv prop refspread)};
                                    my $date=`/bin/date +"%F %X"`; chomp $date;
                                    my $lim=0.5; 
                                    $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
                                    store_to_house_config($r);
                                }
                            }
                        
                            # Warte bis Therme voll aufgeheizt
                            # das Aufheizen der Therme geschieht in der Hauptschleife
                            $phase='WAIT' unless defined $phase;
                        
                            if($phase eq 'WAIT')
                            {
                                if(defined $V && defined $house{inflow_max} && $V<$house{inflow_max}-3)
                                {
                                    writeactuators($r,0); # noch nicht heizen
                                    return "WAIT(V=$V) "; 
                                }
                                
                                # Falls Heizung noch nicht voll an, jetzt starten
                                writeactuators($r,1); # maximal heizen
                        
                                # Temperaturaufzeichnung beginnen
                                $dyn{$r} = {
                                    mode=>$mode, phase=>'HEAT', 
                                    T0old=>$T0old, told=>0, optstart=>$t, 
                                    maxpos=>0, maxslope=>0, 
                                    sumspread=>$spread, temps=>[0], times=>[$T]
                                };
                                
                                return sprintf("%.1f(HEAT)%.1f ",$T,$spread);
                            }
                        
                            my ($optstart, $sumspread, $told, $temps, $times) 
                                = @{$dyn{$r}}{qw(optstart sumspread told temps times)};
                        
                            my $tp=$t-$optstart;
                        
                            # falls aus irgendeinem Grund zu frueh aufgerufen, tu nichts
                            return sprintf("%.1f(", $T).'SKP'.sprintf(")%.1f ",$spread) 
                                if $tp-$told<$house{cycle}/2;
                        
                            # Temperaturkurve aufzeichnen
                            push @{$times}, $tp; 
                            push @{$temps}, $T; 
                            $sumspread+=$spread;
                        
                            # Anzahl Datenpunkte fuer Steigungsberechnung. Hier verdoppelt, weil wir
                            # mehr Praezision brauchen.
                            my $S1=25; $S1=2*$house{mindata} if defined $house{mindata}; 
                            
                            if(scalar(@{$temps})<=$S1)
                            {
                                $dyn{$r} = {
                                    mode=>$mode, phase=>$phase, 
                                    T0old=>$T0old, told=>$tp, optstart=>$optstart, 
                                    maxpos=>0, maxslope=>0, 
                                    sumspread=>$sumspread, temps=>$temps, times=>$times
                                };
                        
                                return sprintf("%.1f(", $T).'OPT'.sprintf(")%.1f ",$spread);
                            }
                        
                            # Steigung der Temperaturkurve durch Regression bestimmen
                            my ($SX,$SY,$SY2,$SXY)=(0,0,0,0);
                            
                            for my $i (-$S1..-1)
                            {
                                $SX+=$temps->[$i];
                                $SY+=$times->[$i];
                                $SY2+=$times->[$i]*$times->[$i];
                                $SXY+=$times->[$i]*$temps->[$i];
                            }
                            
                            my $slope = ($S1*$SXY - $SX*$SY)/($S1*$SY2 - $SY*$SY);
                            
                            if($phase eq 'HEAT')
                            {
                                my ($maxpos, $maxslope) = @{$dyn{$r}}{qw(maxpos maxslope)};
                                
                                if($slope<=0 || $maxslope<=0 || $slope>=0.7*$maxslope)
                                {
                                    my $retval='';
                                    
                                    if($slope>$maxslope)
                                    {
                                        $maxslope = $slope; 
                                        $maxpos = nearest(1,$#{$temps}-$S1/2);
                                        $retval=sprintf "%.2fKph",$slope*60*60;
                                    }
                                    elsif($slope>0)
                                    {
                                        $retval=sprintf "%.2fKph=%d%%", $slope*60*60, 100*$slope/$maxslope;
                                    }
                                    else
                                    {
                                        $retval=sprintf "%.2fKph",$slope*60*60;
                                    }
                                    
                                    # Statusvariablen zurueckschreiben
                                    $dyn{$r} = {
                                        mode=>$mode, phase=>'HEAT', 
                                        T0old=>$T0old, told=>$tp, optstart=>$optstart, 
                                        maxpos=>$maxpos, maxslope=>$maxslope, 
                                        sumspread=>$sumspread, temps=>$temps, times=>$times
                                    };
                                    
                                    return sprintf("%.1f(", $T).$retval.sprintf(")%.1f ",$spread);
                                }
                        
                                # Erwaermung deutlich verlangsamt -> Optimierung berechnen
                                # Abschaetzung des finalen Plateauniveaus durch Annahme 
                                # exponentieller Thermalisierung    
                                
                                # Position maximaler Steigung
                                my $pos1 = nearest(1,$maxpos-$S1/2);
                                my $t1 = $times->[$maxpos];
                                
                                # Endpunkt
                                my $t3 = $times->[nearest(1,-1-$S1/2)];
                                
                                # Punkt in der Mitte zwischen max. Steigung und Endpunkt
                                my $pos2 = undef;
                                for my $p ($maxpos..$#{$times})
                                {
                                    if($times->[$p]>=($t1+$t3)/2) { $pos2=$p; last; }
                                }
                                unless(defined $pos2)
                                {
                                    RESET($r);
                                    $dyn{$r}{mode}='ON'; # ansonsten uebrige Werte behalten
                                    $dyn{$r}{T0}=$T0old;
                                    return "FAILED:POS2";
                                } 
                                $pos2 = nearest(1,$pos2-$S1/2); 
                                
                                # Temperaturen an den Punkten t=0, maxtime, (maxtime+t)/2, t
                                # gemittelt ueber S1 Werte
                                my ($X0,$X1,$X2,$X3)=(0,0,0,0);
                                for my $i (0..($S1-1))
                                {
                                    $X0+=$temps->[$i];
                                    $X1+=$temps->[$i+$pos1];
                                    $X2+=$temps->[$i+$pos2];
                                    $X3+=$temps->[-$i-1];
                                }
                                $X0/=$S1; $X1/=$S1; $X2/=$S1; $X3/=$S1;  
                        
                                # Berechnung des Plateauwertes bei exponentieller Thermalisierung
                                my $Xplateau=($X1*$X3 - $X2*$X2)/($X1 - 2*$X2 + $X3);
                        
                                # Analyse der Sprungantwort
                                my $refspread = $sumspread/scalar(@{$times});
                                my $DX = $Xplateau - $X0; 
                                my $Ks = $DX/$refspread; 
                                my $Tu = $t1 - 2*($tp-$told) - ($X1-$X0)/$maxslope; 
                                my $Tg = $DX/$maxslope;
                                
                                # Optimierung der PID-Parameter nach Chien/Hrones/Reswick
                                # (siehe zB Wikipedia). Wir nehmen aber etwas andere Koeffizienten, 
                                # das fuehrt zu ruhigerem Regelverhalten...
                                
                                # Proportionalbereich prop=1/Kp, kleineres prop ist aggressiver
                                my $prop = $maxslope*$Tu/(0.3*$refspread); 
                                
                                # Nachstellzeit des Integralteils, kleiner ist aggressiver
                                my $Tn = $Tg/60; 
                                
                                # Vorhaltezeit des Differentialteils, groesser ist aggressiver
                                my $Tv = $Tu/60; 
                        
                                # alle drei Parameter muessen positiv sein, sonst Fehler
                                unless($prop>=0 && $Tn>=0 && $Tv>=0)
                                {
                                    RESET($r);
                                    $dyn{$r}{mode}='ON';
                                    $dyn{$r}{T0}=$T0old;
                                    $dyn{$r}{Told}=$T;
                                    
                                    return "FAILED:NEG";
                                }
                        
                                # Statusvariablen zurueckschreiben
                                $dyn{$r} = {
                                    mode=>$mode, phase=>'COOL', 
                                    T0old=>$T0old, told=>$tp, optstart=>$optstart, 
                                    Tn=>$Tn, Tv=>$Tv, prop=>$prop, refspread=>$refspread, tcool=>$t3,
                                    sumspread=>$sumspread, temps=>$temps, times=>$times
                                };
                                
                                # Abkuehlung einleiten
                                writeactuators($r, 0);
                                
                                return sprintf("%.1f(COOL) ",$T);
                            }
                            
                            if($phase eq 'COOL' && $slope>0)
                            {
                                return sprintf("%.1f(%.2fKph) ",$T,$slope*60*60);
                            }
                        
                            # Abspeichern der optimierten Parameter im Konfigurationsfile
                            # aus der Laenge der "cooling"-Periode bis zum Maximum koennte man noch was berechnen, 
                            # aber wir setzen $lim hier als Konstante
                            my ($Tn, $Tv, $prop, $refspread, $tcool)
                                = @{$dyn{$r}}{qw(Tn Tv prop refspread tcool)};
                            my $date=`/bin/date +"%F %X"`; chomp $date;
                            my $lim=0.5; 
                            $house{$r}{pid}={Tv=>$Tv, Tn=>$Tn, lim=>$lim, prop=>$prop, refspread=>$refspread, date=>$date};
                            store_to_house_config($r);
                        
                            # Regelung starten
                            RESET($r);
                            $dyn{$r}{mode}='ON';
                            $dyn{$r}{T0}=$T0old;
                            $dyn{$r}{Told}=$T;
                        
                            # Info an den User
                            return sprintf "t=%dh:%02dmin Tv=%.1fmin Tn=%dmin lim=%.1f prop=%.1f spread=%.1f ", $tp/3600,($tp/60)%60,$Tv,$Tn,$lim,$prop,$refspread;
                        }
                        und das Beispiel-Konfigurationsfile war ja schon weiter oben gepostet, das kommt in /etc/wiregate/plugin/conf.d/Heizungsregler.conf.

                        Have fun!
                        Fry

                        Kommentar


                          #13
                          Damit du dir nichts zerschießt:

                          Ich sehe mir gerne dein angepasstes Konfig an, bevor du es scharf schaltest.

                          Teste das Plugin am besten erstmal in einem Raum mit Fliesen oder aber mit Vorlauftemp. 30°C, dann kann nichts passieren.

                          Bitte lass einmal die Optimierungsroutine laufen (Raum abkühlen lassen, dann Solltemperatur auf -1 Grad stellen, abwarten, kein Fenster öffnen) und poste bitte den relevanten Output auf /var/log/wiregate_plugin.log hier. Ich bin gespannt, welche Werte die Optimierungsroutine in einer Echtsituation ermittelt (und wie gut die Simulation war).

                          VG, Fry

                          Kommentar


                            #14
                            Zitat von makki Beitrag anzeigen
                            idm WP ("alt", 2008) , Grundwasser; Wie schon angemerkt ist das eine ganz andere Nummer als eine Gastherme, hier spielt jedes ° VL ein dicke Rolle wo man sich bei ner Gastherme evtl. gut ein Ei drauf pellen kann..
                            Daher habe ich eher darauf optimiert möglichst geringen VL und möglichst grosse Ventilöffnung in der Heizperiode in relevanten Räumen zu haben..
                            Das passt, und das ist auch genau die richtige Strategie.

                            Obwohl ich keine WP haben werde (für einen Physiker eigentlich blamabel, aber die Sondenbohrung wäre auf meinem Grundstück ein Riesenaufwand, ich sag nur Gutachten und Genehmigung), habe ich im Plugin eine Optimierung der Vorlauftemperatur vorgesehen. Einfacher Algorithmus, vom Prinzip her so: wenn alle Ventile auf <70% stehen, dann wird die Vorlauftemperatur schrittweise gesenkt und gleichzeitig die Reglerparameter angepasst, bis die Ventile auf Öffnung >90% kommen.

                            Ich plane dann eine WP in 20 Jahren, wenn die Pumpen besser, die Genehmigungen einfacher und das Gas teurer sein werden - und meine Therme mal den Geist aufgeben wird (glaub ich aber nicht bei Wolf) :-)

                            VG, Fry

                            Kommentar


                              #15
                              Zitat von Fry Beitrag anzeigen
                              Obwohl ich keine WP haben werde (für einen Physiker eigentlich blamabel, aber ...
                              Naja, ich finde das ist nicht blamabel sondern eine ganz einfache, wirtschaftliche Frage.. Ich habe das Glück in (AFAIR) 7m fliessendes GW zu haben, wenn ich dafür 50m tiefer oder durch drei Instanzen müsste, hätte ich vermutlich auch ne Gastherme mit Grüssen an Gerhard und Vladimir

                              Ich plane dann eine WP in 20 Jahren, wenn die Pumpen besser, die Genehmigungen einfacher und das Gas teurer sein werden
                              Wenns beruhigt, ich glaube Strom/Gaspreis bei einer COP von 3+ wird sich nicht viel nehmen, sicher ist nur das es beides nicht günstiger wird.. Politisch ist beides Banane, der Gaspreis ist aus unerfindlichen Gründen ans Öl gebunden und ich subventioniere mit dem Strom nutzlose 3% Solarpanels während real dann jetzt halt statt Atom- Kohlekraftwerke neu gebaut werden
                              Das wird noch sehr lustig werden, vielleicht beneide ich dich in 5-10J auch, das das Gas aus dem Rohr kommt, während der Strom für WP streng rationiert wird..

                              -> und sich daher intelligente Regelungen immer mehr durchsetzen werden, weils so oder so immer mehr Geld kostet! Das ist das gute daran, daher Danke für dieses Plugin!
                              Ich bin mir sicher, das in 5J die WP nicht mehr als Verbraucher fungieren darf, der halt mal einschaltet, wenn man grad dazu lustig ist..

                              Makki
                              EIB/KNX & WireGate & HS3, Russound,mpd,vdr,DM8000, DALI, DMX
                              -> Bitte KEINE PNs!

                              Kommentar

                              Lädt...
                              X