[Vorstellung] Einfache kabelgebundene Heizthermostat-Steuerung

Hallo zusammen,

nachdem ein Freund mit seinem out-of-the-box internetsteuerbarem Heizthermostat angibt, war es an der Zeit, meine Hausautomatisierung zu erweitern :wink:

Auch wenn ich das Gesamtkonstrukt noch nicht vorgestellt habe, stelle ich diesen Teil mal online; ich hab nämlich im Vorfeld wenig dazu im Netz gefunden.

Ursprünglich wollte ich ja fertige Funk-Thermostate steuern, aber nachdem ich zu faul war, Funkprotokolle zu analysieren und im Internet nicht richtig fündig wurde, habe ich die quick&dirty-Lösung verfolgt.

  • Billig-Thermostat von Conrad. Nicht, weil er gut wäre, er leise wäre, er schnell regeln würde, oder ich ihn besonders empfehlen könnte, sondern einfach weil die potentiellen 10€ Verlust beim ersten Basteln verkraftbar sind.

  • An die Taster-Pins rückseitig angelötete Kabel, die zum Arduino gehen.

Nachteil beim verwendeten Thermostat: ein wenig friemelig, da SMD.
Vorteil beim verwendeten Thermostat: komplett aufschraubbar, relativ geräumig, Schaltplan nachverfolgbar (nicht nur ein einziger ASIC als Siliziumklecks).

Nachdem wir 90% der Wohnung über nur eine einzelne riesige Heizung wärmen, und diese 4m vom zentralen Arduino entfernt ist, ist der Nachteil der kabelgebundenen Lösung für mich akzeptabel (wenn auch nicht absolut befriedigend).

In meinem Szenario ist die Heizung jetzt u.a. übers Ethernet-Shield steuerbar, Schaltzeiten und Verläufe werden dabei von einem externen Server vorgegeben; daher ist die Logik auf dem Arduino eher schmal.

Anbei findet Ihr das Test-Programm mit der (trivialen) Hardware-Beschaltung.

Viele Grüße, Holger

/*
 *            Arduino-Steuerung Heizungsthermostat HSA9001A
 *
 * Funktion:
 *            Steuerung eines Heizungsthermostates mittels emulierter Tastendrücke per Kabel
 *            Typ: "Heizkörperregler mit Timer HSA9001A" von Conrad
 *
 * Hardware:
 *           Die Taster "Up" und "Down" des Heizungsthermostates werden mit
 *           Optokopplern versehen und an zwei Pins des Arduino gehängt.
 *
 *
 *    Jeweils für hoch und runter:
 *                                         _____HCPL-817-000E
 *                           330 Ohm      |.    |
 *       PORT_HEAT_UP--------|=====|------|1   3|---------- Thermostat Taster Hoch 'Signal'
 *                                        |     | 
 *       Masse----------------------------|2   4|---------- Thermostat Taster Hoch 'Masse'
 *                                        |_____|
 *                                     
 *    Optional, wem "galvanische Trennung", "50 mA Imax am 3.3V-Pin" und "3.3V statt 3V" egal ist:
 *       3.3V ---------------------------------------------- Batteriefach Plus
 *       Masse---------------------------------------------- Batteriefach Masse
 *       (Bei mir tut das (noch), aber ohne jegliche Gewähr, dass der Arduino damit nicht bei euch in Flammen aufgeht!)
 */
 
//Hardware
  #define PORT_HEAT_UP          12  
  #define PORT_HEAT_DOWN        13  

//temperature memory
int           heatSetTemperature    = 0;     //Current temperature set on the module times 10 (e.g. 200 means 20.0°C)
int           heatWantedTemperature = 0;     //Requested temperature times 10

//recalibration
boolean       heatRecalibrationMode = 0;     //Current mode of operation. Used for the recalibration sequence
byte          heatRecalibrationCounter = 0;  //Recalibration mode 1: Number of already finished "Down" presses
/* Aus dem Handbuch:
   Betriebsart MANUELL (=Werkseinstellung) Symbol:
    In dieser Betriebsart regelt der Heizungsstellantrieb die Temperatur auf einen vom Anwender einstellbaren Temperatur-Schwellwert.
    Durch Betätigen der Taste „UP“ bzw. „DOWN“ kann der Temperatur-Schwellwert in 0,5 Schritten erhöht bzw. erniedrigt werden.
    Max. Einstellwert = 30°C, anschließend erscheint die Anzeige ON, dies bedeutet, das Ventil wird vollständig geöffnet.
    Min. Einstellwert = 8°C,  darunter erscheint die Anzeige OFF, dies bedeutet, das Ventil wird vollständig geschlossen.
*/
#define HEAT_DOWN_PRESSES_FOR_CALIBRATION 50 // Max 22 degree * 2 steps per degree + 2 time for turning off --> 46 should be enough

//delays
unsigned long heatLastPressMillis   = 0;     //millis, when button is currently pressed
unsigned long heatLastReleaseMillis = 0;     //millis for the delay after a button has been pressed and released

//Delays
#define MINIMAL_HEAT_BUTTON_PRESS_TIME   200 //minimal number of ms the heating system needs for a key press
#define MINIMAL_HEAT_BUTTON_RELEASE_TIME 200 //minimal number of ms the heating system needs between two key presses


template<class T> inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; }
//TEST PROGRAM
      void setup()
      {
        Serial.begin(9600);        // start serial port at 9600 bps:
        Serial << "Start heating thermostat example\n";
        Serial << "Press 0 to 9 for temperatures 20 to 29 °C\n";
        Serial << "Press r for recalibration test\n";
        setUpHeating();
      }
      void loop()
      {
        if (Serial.available() > 0) {
          byte inByte = Serial.read();
          if (inByte=='r')
          {
            Serial << "Recalibrate\n";
            heatRecalibrate();
          }
          else
          {
            byte temp = inByte - '0'; // --> 0=0, 1=1, ...
            int wanted = 200+temp*10; // --> 0=200, 1=210, ...
            Serial << "Setting " << (int)(wanted) << "C\n";
            heatSetTemp(wanted);    
          }
        }
        heatingManager();
        delay(1);
      }
//ENDE TEST

//The setup function for the thermostat
void setUpHeating()
{
  digitalWrite(PORT_HEAT_UP,   LOW);
  pinMode(     PORT_HEAT_UP,   OUTPUT);
  digitalWrite(PORT_HEAT_DOWN, LOW);
  pinMode(     PORT_HEAT_DOWN, OUTPUT);
  heatSetTemp(180); 
  heatRecalibrate();  
}

//Loop function (non-Blocking) for all heating functions
void heatingManager()
{
  if (heatLastPressMillis!=0 && (millis() - heatLastPressMillis < MINIMAL_HEAT_BUTTON_PRESS_TIME))
  { //Wait while pressing a button
    return;  
  }
  if (heatLastPressMillis!=0)
  { //Release button
    heatLastPressMillis=0;
    heatReleaseButtons(); //heatLastReleaseMillis will be set here
    return;
  }
  if (heatLastReleaseMillis!=0 && (millis() - heatLastReleaseMillis  <MINIMAL_HEAT_BUTTON_RELEASE_TIME))
  { //Wait after releasing a button
    return;  
  }
  if (heatLastReleaseMillis!=0)
  { //We waited enough after releasing ... Now we can continue ...
    heatLastReleaseMillis=0;
  }

  if (heatRecalibrationMode == 0) //normal mode, check for changet temperature requirements
  {
    if (heatWantedTemperature==0)
      return;
    if (heatSetTemperature < heatWantedTemperature)
      heatPressUp();
    if (heatSetTemperature > heatWantedTemperature)
      heatPressDown();
  } 
  else if (heatRecalibrationMode == 1) //recalibration mode phase 1 - turn off the thermostat
  {
    heatRecalibrationCounter ++;
    if (heatRecalibrationCounter < HEAT_DOWN_PRESSES_FOR_CALIBRATION) 
      heatPressDown();
    else
      heatRecalibrationMode = 2;
  }
  else if (heatRecalibrationMode == 2) //recalibration mode phase 2 - turn on again
  {
    heatPressUp();
    heatSetTemperature = 80;
    heatRecalibrationMode = 0;
  }
  return;
}

void heatSetTemp(int tempMal10)
{
  Serial << "T Set " << tempMal10 << "\n";
  heatWantedTemperature = tempMal10;
}

//At startup as well as after concurrent user input, heatSetTemperature might not reflect reality 
//Here we have to make sure that heatSetTemperature is correct
void heatRecalibrate()
{
/* The following (blocking code) is implemented nonblocking in the heatingManager() operation
  for (int i = 0; i<(2+22*2); i++)  heatPressDown();                        --> turn off  --> recalibrationMode = 1
  moveUp();                                                                 --> turn on   --> recalibrationMode = 2
  while (heatSetTemperature < heatWantedTemperature) heatPressUp();         --> Set again --> recalibrationMode = 0
  */
  Serial << "T Calib.\n";
  heatRecalibrationMode = 1;
  heatRecalibrationCounter =0;
}

void heatReleaseButtons()
{
  heatLastReleaseMillis = millis();
  digitalWrite(PORT_HEAT_UP, LOW);
  digitalWrite(PORT_HEAT_DOWN, LOW);
}

void heatPressUp()
{
  heatSetTemperature+=5;
  Serial << "T+\n";
  heatLastPressMillis=millis();
  digitalWrite(PORT_HEAT_UP, HIGH);
}

void heatPressDown()
{
  heatSetTemperature-=5;
  Serial << "T-\n";
  heatLastPressMillis=millis();
  digitalWrite(PORT_HEAT_DOWN, HIGH);
}