Donnerstag, 29. Mai 2014

Neuster Code für die Heizungssteuerung

Hier kommt der neue Code für die Heizungssteuerung.
Es steuert zwei WW-Boiler, die je nach Temperatur mit einem Dreiwege-Ventil umgeswitcht werden.
Nun ist bei 40°C und mehr der Wechsel eingerichtet, damit das Wasser nicht zu lange stehen bleibt.


#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Ethernet.h>
#include <EthernetUdp.h> // für die NTP-Abfrage
#include <LCD.h>
#include <Keypad.h>

#include <Keypad_I2C.h>
#include <SPI.h>
#include <SD.h> // für die SD-Kartennutzung
#include <avr/wdt.h> // für den Watchdog vgl. (http://tushev.org/articles/arduino/item/46-arduino-and-watchdog-timer)
#include <Time.h>

// fuer das LCD
#define I2C_ADDR    0x27  // Define I2C Address where the PCF8574A is
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7

// LCD Sonderzeichen definieren
uint8_t boilerwand[8]  = {
 0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4};
uint8_t arrow_left[8] = {
 0x4,0x4,0xc,0x1f,0xc,0x4,0x4,0x4};
uint8_t arrow_right[8] = {
 0x4,0x4,0x6,0x1f,0x6,0x4,0x4,0x4};
uint8_t ecke_re[8] = {
 0x0,0x0,0x0,0x7,0x4,0x4,0x4,0x4};
uint8_t ecke_li[8] = {
 0x0,0x0,0x0,0x1c,0x4,0x4,0x4,0x4};
uint8_t linie[8] = {
 0x0,0x0,0x0,0x1f,0x0,0x0,0x0,0x0};

uint8_t arrow_up[8] = {
 0x4,0xe,0x1f,0x4,0x4,0x4,0x4};
uint8_t arrow_down[8] = {
 0x4,0x4,0x4,0x4,0x1f,0xa,0x4};

uint8_t clock[8] = {
 0x0,0xe,0x15,0x17,0x11,0xe,0x0};
uint8_t check[8] = {
 0x0,0x1,0x3,0x16,0x1c,0x8,0x0};
uint8_t retarrow[8] = {
 0x1,0x1,0x5,0x9,0x1f,0x8,0x4};
uint8_t bell[8]  = {
 0x4,0xe,0xe,0xe,0x1f,0x0,0x4};


// LCD Instanz
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);

// für die SD Karte
File SD_Datei;

// für NTP
unsigned int localPort = 8888;      // local port to listen for UDP packets
IPAddress timeServer(192, 168, 178, 1); // ntp nun über die eigene Fritzbox, die als Zeitserver eingestellt ist
// IPAddress timeServer(192, 53, 103, 108); // ntp1 ptb.de 192.53.103.108 Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
const int timeZone = 1; // Central European Time
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 
EthernetUDP Udp; // A UDP instance to let us send and receive packets over UDP
boolean zeitpaket_angefordert=false;
boolean zeitpaket_geliefert=false;
unsigned long epoch=0;

int stunde=0;
int minuten=0;
int sekunden=0;

// assign a MAC address for the ethernet controller.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // 0xED zum Schluss ist für die Produktivversion gedacht
byte gateway[] = { 192,168,178,1};
byte subnet[] = { 255, 255, 255, 0};
IPAddress ip(192,168,178,44);// assign an IP address for the controller: // die 47 am Ende für die Produktivversion!
EthernetClient client;// initialize the library instance:
char server[] = "api.cosm.com"; //IPAddress server(216,52,233,121);

// fuer COSM - Testversion
/*#define APIKEY         "XXXXXXXXXXXXXXXXX" // your cosm api key
#define FEEDID         XXXX // your feed ID - Arduino Testversion 2
#define USERAGENT      "Cosm Arduino Example (XXXX)" // user agent is the project name
*/

#define APIKEY         "XXXXXXXXXXXXXXXXXXX" // your cosm api key
#define FEEDID         XXXX // your feed ID-  Arduino Testversion 1
#define USERAGENT      "Heizduino (XXXX)" // user agent is the project name

unsigned long lastConnectionTime = 0;        // last time you connected to the server, in milliseconds
boolean lastConnected = false;      // state of the connection last time through the main loop
const int postingInterval = 10000;  //delay between updates to Pachube.com, in ms

/* Ende der Angaben für Pachube */


// für das Keypad
#define ROWS 4
#define COLS 4 // sollte 4 sein!
#define PCF8574_ADDR 0x23 // Keypad Adresse
byte rowPins[4] = {0,1,2,3}; //connect to the row pinouts of the keypad
byte colPins[4] = {4,5,6,7}; //connect to the column pinouts of the keypad
char keys[4][4]=
{
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
char key;
byte key2;
byte key_i;

// Keypad Instanz
Keypad_I2C kpd = Keypad_I2C(makeKeymap(keys),rowPins,colPins,ROWS,COLS,PCF8574_ADDR); // für das Keypad

// I2C-Adressen der angeschlossenen Geräte
#define expander_keypad 0x23 //EXPANDER address, hier mit A0 und A1 auf HIGH, Adresse des Keypads
#define expander_relais 0x20 //EXPANDER address with 3 address pins grounded, Adresse der Relaiskarte

/*
// Der PCF8574 hat die Adresse 0x20
// einer ist für das Keyboard (Adresse: 0x20)
// einer ist für das Relaismodul (Adresse: 0x21, dazu A0 kurzschliessem; INT steht die Interrupts)

// Pins 0-5 sind an die Temp-Fühler (Erdungsfarbe) angeschlossen, braun an +, blau an -

// Update: Pin 0 und 1 sind beim Ethernet-Shield für die Nutzung von SD Card gedacht und liefern daher falsche Werte, daher Umlegung auf 8-13
// 8 = 300l-Boiler oben
// 9 = 300l-Boiler unten
// 10 = 500l-Boiler oben
// 11 = 500l-Boiler unten
// 12 = Vorlauf
// 13 = Rücklauf
*/

/*
// Relaisbyte 
// ACHTUNG: Relais mit +/- nicht über Arduino versorgen, sondern ext. Stromquelle anzapfen!
// Achtung: Das Relaisboard ist so konstruiert, dass die einzelnen Relais imer unter Strom sind, 
//          Wird das Kanal auf LOW geschaltet (GND), dann fließt Strom und das Relais schließt.
//          Folge: bei 11111110 schaltet das erste Relais, die anderen bleiben aus.
//
// Bit 0 - Zusatzpumpe für die Heizungsunterstützung, falls der Vorlauf z.B. über 95 Grad steigt
// Bit 1 - Pumpe UND Zwangsventil für die Aufheizung der Warmwasser-Boiler durch die Heizung ("befüllen")
// Bit 2 - 3-Wege-Ventil für die Aufheizung: in kleinen Boiler
// Bit 3 - 3-Wege-Ventil für die Aufheizung: in großen Boiler
// Bit 4 - Umwälzpumpe UND Ventil für die Umwälzung der Wärme vom 500l-Solarboiler in den kleinen 300l-Boiler ("umwälzen")
// Bit 5 - für später
// Bit 6 - 3-Wege-Ventil für das Brauchwasser: 300l-Boiler
// Bit 7 - 3-Wege-Ventil für das Brauchwasser: 500l-Boiler

// dementsprechend werden die Relais zugeordnet!
*/

byte relais_befehls_byte; // Dieses Byte ist das Ergebnis der Überprüfung des Relais-Bytes dar. Es ist um evtl. Konflikte bereinigt
// Es ist das Ergebnis der Schaltung des Relais und das Endergebnis 
byte relais_byte; // darüber werden die Methoden gesteuert, 
// dieses Byte wird nach einer Kontrolle der Konsistenz an den Relaiskontroller PCF8574 als relais_befehls_byte geschickt
// Im Konfliktfall wird DIESES um das Ergebnis der Kontrollprüfung korrigert
// Es ist der SOLL-Zustand des Relais, insofern ein Zwischenergebnis
byte relais_vorschlag_byte; // Dieses Byte ist das Ergebnis der Temperaturvergleiche, ein erstes SOLL_Byte

char test[4];
char tmp[10];
char tmpp[20];

boolean arbeitsmodus=0; // Setzen des Arbeitsmodus: entweder Manuell (=1) oder Autopilot (=0) 
boolean neue_temp_da=0;

const unsigned long max_millis=4294967295;
unsigned long time;  // wird nach ca. X Tagen wieder auf Null gestezt
unsigned long time_letzte_temp_messung=0; // Zwischenstand für temp_erfassung_ausgabe()
unsigned long time_letzte_lcd_ausgabe=0; // 
unsigned long time_letzter_lcd_reset=0; // Zwischenstände für regelmäßigen lcd_reset
unsigned long time_letzte_zeit_ausgabe=0; // Zwischenstände für regelmäßigen Zeit-Ausgabe

unsigned long time_letzte_zeitanforderung; // Zwischenstände, gebraucht für die Zeitabfrage
unsigned long letzte_zeit; // Zwischenstände, gebraucht für die Zeitabfrage
unsigned long time_d; // Zwischenstände, Arbeitsvariable

const unsigned long time_abstand_lcd_reset=600000; // 10 Minuten
const unsigned long time_abstand_lcd_ausgabe=60000; // 60 Sekunden
const unsigned long time_abstand_zeit_ausgabe=1000; // 1 Sekunde
const unsigned long time_abstand_temp_messung=10000; // 10 Sekunden
const unsigned long time_abstand_zeit_abfrage=600000; // 10 Minuten
const unsigned long time_abstand_3WV_schaltung=300000; // 5 Minuten
const unsigned long time_abstand_pumpen_schaltung=150000; // 150 Sekunden
const unsigned long time_vorgabe_3WV_abschaltung=30000; // 30 Sekunden
const unsigned long time_vorgabe_pumpen_abschaltung=3600000; // 1 Stunde
const unsigned long time_abstand_WW_boiler_wechsel=25200000; // 7 Stunden
// boolean relais3WV_switch=0;

unsigned long time_letzter_relaiswechsel[8]={
0,
0, 
0,
0,
(max_millis-time_abstand_pumpen_schaltung+30000),
0,
(max_millis-time_abstand_3WV_schaltung+30000),
(max_millis-time_abstand_3WV_schaltung+30000)
}; // Zeitpunkt der letzten Schaltung der sieben Relais, gleich auf Null initiieren

const unsigned int TEMP_SENSOR_PIN[6]={8, 9, 10, 11, 12, 13}; // Pinnummern zu Temp-Sensoren zuweisen, s.o.
// unsigned int temp_3_alt=0; // Zwischenspeicher für die untere Boilertemperatur für die Feststellung des Sommers

const int Anzahl_Temp_Messungen=25; // Zahl der pro Durchgang gemachten Messungen an den Fühlern, je mehr, desto besser
// const int large_archiv_groesse=10; // Zahl der gespeicherten alten Temp-Werte, kommt später!

float TEMP_SENSOR[6]={0,1,2,3,4,5};
int TEMP_SENSOR_Reading[6][Anzahl_Temp_Messungen];
//float TEMP_SENSOR_L[6][large_archiv_groesse];
// und gleich die sechs passenden Zeiger
// int temp_pointer[6]; , kommt päter

const float max_B_temp=60;              // Temperatur, die ein Boiler maximal erreichen darf, um keine Kalkablagerungen abzukriegen 60 Grad??
const float max_temp_vorlauf=85;        // ab hier schaltet die Zusatzpumpe 
const float hysteresis_3WV=6000;
const float zusatzpumpen_hysteresis=5;  // unterschreitet die temp die max_temp_vorlauf-Temp um diesen Wert, schaltet die Zusatzpumpe aus
const float sommer_temp_solarboiler=50; // angenommene Temp im gr. Boiler unten, wenn Sommer ist

const float SUPPLY_VOLTAGE = 1.1; // Voltage für die interne Vergleichsspannung, nur beim MEGA 1,1!
const unsigned int BOUD_RATE = 38400; // Übetragungsrate zum ser. Monitor, warum nicht 9600 ... oder mehr?

  
// Beschreibung der String-Lib: http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
// und http://www.robotc.net/wiki/ARDUINO_328_Functions_Strings

char temp_string[200];
char temp_a_string[200];

/* alt:
//The link:http://www.dfrobot.com/image/data/DFR0154/LiquidCrystal_I2Cv1-1.rar
//DFRobot.com
//Compatible with the Arduino IDE 1.0
//Library version:1.1

neu: 
https://bitbucket.org/fmalpartida/new-liquidcrystal/wiki/Home 
*/

#if defined(ARDUINO) && ARDUINO >= 100 // // keine Ahnung wofür
#define printByte(args)  write(args);
#else
#define printByte(args)  print(args,BYTE);
#endif;


void get_temperature() 
{
long vol=0;
for (int i=0; i<Anzahl_Temp_Messungen; i++)
{
for (int j=0; j<=5; j++)
{
TEMP_SENSOR_Reading[j][i]=analogRead(TEMP_SENSOR_PIN[(j)]);
}
    }

// nun die Mittelwert-Berechnung!
  for (int i=0; i<=5; i++)
{
vol=0;
for (int j=0; j<Anzahl_Temp_Messungen; j++)
{
vol=vol+TEMP_SENSOR_Reading[i][j];
}
TEMP_SENSOR[i]=SUPPLY_VOLTAGE*vol/10.24/Anzahl_Temp_Messungen;
if (TEMP_SENSOR[i]>=99) 
TEMP_SENSOR[i]=99; // Absicherung gegen unerwünschte und unrealistische Readings
if (TEMP_SENSOR[i]<=0) 
TEMP_SENSOR[i]=0; // Absicherung gegen unerwünschte und unrealistische Readings, für den Fall späterer Minustemperaturen
}
}


void zeit_lcd_ausgabe()
{
if (zeitabstand_bestimmen_zum(time_letzte_zeit_ausgabe)>time_abstand_zeit_ausgabe)  // erst nach Ablauf einere Sek Zeit neu ausgeben
{
unixtime_umrechnen_aktualisieren();

if(zeitpaket_angefordert==false&&zeitpaket_geliefert==true)
{
lcd.setCursor(4, 1); 
//lcd.print(F("*"));
lcd.printByte(5); 
}
else
{ lcd.setCursor(4, 1); 
//lcd.print(F("O")); 
lcd.print(F(" ")); 
}

lcd.setCursor(5, 1); 
lcd.print(stunde);
lcd.print(F(":"));

if ( minuten < 10 ) 
{
// In the first 10 minutes of each hour, we'll want a leading '0'
lcd.print(F("0"));
}
lcd.print(minuten);
lcd.print(F(":"));
 
if ( sekunden < 10 ) 
{
// In the first 10 seconds of each minute, we'll want a leading '0'
lcd.print(F("0"));
}
lcd.print(sekunden);
time_letzte_zeit_ausgabe=millis();
}
}


void lcd_ausgabe()
{
// if (zeitabstand_bestimmen_zum(time_letzte_lcd_ausgabe)>time_abstand_lcd_ausgabe)
{
Serial.println(F("lcd_ausgabe!"));
//lcd.clear(); 
//ZEICHNUNG Boiler
for (int i=0;i<=3;i++)
{
lcd.setCursor(2, i); 
//lcd.print(F("I"));
lcd.write(uint8_t(0));
}
for (int i=0;i<=3;i++)
{
lcd.setCursor(15, i); 
//lcd.print(F("I"));
lcd.write(uint8_t(0));
}
 // kleiner Boiler-Temperatur
lcd.setCursor(0, 0); 
lcd.print(TEMP_SENSOR[0],0);
lcd.setCursor(0, 3); 
lcd.print(TEMP_SENSOR[1],0);
// großer Boiler-Temperatur
lcd.setCursor(16, 0); 
lcd.print(TEMP_SENSOR[2],1);
lcd.setCursor(16, 3); 
lcd.print(TEMP_SENSOR[3],1);
// Vorlauf- und Rücklauf-Temperatur
lcd.setCursor(3, 3); 
lcd.print(F("v")); 
lcd.print(TEMP_SENSOR[4],1);
lcd.setCursor(10, 3); 
lcd.print(F("r")); 
lcd.print(TEMP_SENSOR[5],1);

// Relaisfunktionen: vgl. byte relais_byte
if (byte_bit_abfragen(relais_byte,0)==1) 
{
lcd.setCursor(8, 3); 
lcd.print(F("+"));
}     
else 
{
lcd.setCursor(8, 3); 
lcd.print(F(" "));
}

// Umwälzung zwischen den beiden Boilern
// Umwälzen und Aufheizen findet niemals zusammen statt, daher diese Abfrage zuerst
if (byte_bit_abfragen(relais_byte,4)==1) 
{
lcd.setCursor(2, 2); 
//lcd.print(F("<"));
lcd.printByte(1); 
for (int i=0;i<=11;i++) 
{
//lcd.print(F("-"));
lcd.printByte(6);
}
//lcd.print(F(">"));
lcd.printByte(2);
}
else 
{
lcd.setCursor(2, 2); 
//lcd.print(F("I"));
lcd.write(uint8_t(0));
lcd.print(F("            "));
//lcd.print(F("I"));
lcd.write(uint8_t(0));
}

// Aufheizung der Boiler
if (byte_bit_abfragen(relais_byte,1)==1) 
{
//Aufheizung des kl. (0) o. gr. (1) Boilers

if (byte_bit_abfragen(relais_byte,2)==1) // Aufheizung des kleinen Boilers
{
lcd.setCursor(2, 2); 
//lcd.print(F("<"));
lcd.printByte(1); 
for (int i=0;i<=5;i++) 
{
//lcd.print(F("-"));
lcd.printByte(6);
}
//lcd.print(F("+"));
lcd.printByte(4);
lcd.print(F("     "));
//lcd.print(F("I"));
lcd.write(uint8_t(0));
}
if (byte_bit_abfragen(relais_byte,3)==1) // Aufheizung des kleinen Boilers
{
lcd.setCursor(2, 2); 
//lcd.print(F("I"));
lcd.write(uint8_t(0));
lcd.print(F("      "));
//lcd.print(F("+"));
lcd.printByte(3); 
for (int i=0;i<=4;i++) 
{
//lcd.print(F("-"));
lcd.printByte(6);
}
//lcd.print(F(">"));
lcd.printByte(2);
}
}
else 
{
}

// WW-Entnahme aus den Boilern
if (byte_bit_abfragen(relais_byte,6)==1) // Entnahme aus dem kleinen Boiler 
{
lcd.setCursor(2, 0); 
//lcd.print(F(">"));
lcd.printByte(2); 
lcd.print(F("WW"));
lcd.setCursor(13, 0);
lcd.print(F("  "));
//lcd.print(F("I"));
lcd.write(uint8_t(0));
}
if (byte_bit_abfragen(relais_byte,7)==1) // Entnahme aus dem großen Boiler  
{
lcd.setCursor(2, 0); 
//lcd.print(F("I"));
lcd.write(uint8_t(0));
lcd.print(F("  "));
lcd.setCursor(13, 0);
lcd.print(F("WW"));
//lcd.print(F("<"));
lcd.printByte(1);
}

// und zum Schluss das Relais der Auffüllung zeigen, falls es auf 300 zeigt und damit umsonst arbeitet
//Entnahme aus kleinen (1) bzw. großen Boiler (0)

if (byte_bit_abfragen(relais_byte,3)==1)
{
lcd.setCursor(9, 2); 
//lcd.print(F(">"));
lcd.printByte(3); 


if (byte_bit_abfragen(relais_byte,2)==1)
{
lcd.setCursor(9, 2); 
//lcd.print(F("<"));
lcd.printByte(4); 


if (arbeitsmodus == 0) 
{
lcd.setCursor(7, 0); 
lcd.print(F("Auto")); 
}
 else
{
lcd.setCursor(7, 0); 
lcd.print(F("Man ")); 
}
time_letzte_lcd_ausgabe=millis();
}
}


void expanderWrite(byte data)
{ // Ansteuerung der Expander-Karte
Wire.beginTransmission(expander_relais);
Wire.write(data);
Wire.endTransmission();   
// lcd_init(); // um das Kaulderwelsch zu beseitigen, nicht mehr nötig
}


byte expanderRead(int i2caddr) 
{
int _data = -1; // Returnwert bei Fehlern ist "-1"!! // wäre abzufangen!
Wire.requestFrom(i2caddr, 1);
if (Wire.available()) 
{
_data = Wire.read();
}
return _data;
}


int byte_bit_abfragen(byte by, int stelle)
{
// by=beliebiges byte, z.B. das zu sendende oder vorgeschlagene byte
// stelle startet mit 0 und geht bis 7

int bitmaske = 1 << stelle;
if ((bitmaske&by)>0)
return 1;
else 
return 0;



byte relais_bit_setzen(byte rel_byte, unsigned int Position, boolean wert) 
{
unsigned int bitmaske = 1 << Position;
byte vb=0;

if (wert==0)
{
vb=((~bitmaske)&rel_byte);
}
else 
{
vb=(rel_byte|bitmaske);    
}
return vb;
}


byte relais_bit_wechseln(byte vb, byte pos)
{
//  Serial.println("relais_bit_wechseln!");
byte bitmask2=1<<pos;
vb=vb^bitmask2;
return vb;



unsigned long zeitabstand_bestimmen_zum(unsigned long zeit_var)
{
unsigned long time_diff=0;

time=millis();
if (time>=zeit_var)
{     
time_diff=time-zeit_var;
}
else
{
time_diff=4294967295-zeit_var+time;
}
return time_diff;
}


void relais_byte_senden (byte vb)
{
// vb=~vb; // Das Relaisbyte muss bei den blauen vor dem Senden invertiert werden, da nur bei bit=low das Relais schaltet! (nicht so bei den SDD)
expanderWrite(vb);  //... und rüberschieben zur Relaiskarte
}


void temp_archivieren(int fuehler, float temp)
{
/*Serial.println("temp_archivieren!");
  TEMP_SENSOR_L[fuehler][temp_pointer[fuehler]]=temp;
  if ((temp_pointer[fuehler]+1)==large_archiv_groesse)
  {
    temp_pointer[fuehler]=0;
  }
  else
  {
    temp_pointer[fuehler]=temp_pointer[fuehler]+1;
  }
*/
}


void temp_erfassung_ausgabe() 
{
if (zeitabstand_bestimmen_zum(time_letzte_temp_messung)>time_abstand_temp_messung)  // wurde vor mehr als x Sek gemessen? Dann führe erneute Messung durch!
{
get_temperature();
//temp_archivieren(k,TEMP_SENSOR[k]);
time_letzte_temp_messung=millis();
// lcd_ausgabe();
neue_temp_da=1;
daten_ablegen_versenden(); // zusammenstoepseln passiert in derdem unterprg.; Daten zu Cosm schaffen
}
else 
    {
neue_temp_da=0;
}
}


float mittelwert_L_berechnen(int fuehler) 
{
/*  Serial.println("mittelwert_berechnen!");
  float mittelwert=0;
  for (int a=0;a<large_archiv_groesse; a++)
  {
    mittelwert=mittelwert+TEMP_SENSOR_L[fuehler][a];
  }
  return (mittelwert/large_archiv_groesse);
*/
}


char keypad_ergebnis() // Abfrage des Keypads; evtl. unnötig, wird z.Z. nicht benutzt
{
char k = kpd.getKey(); 
if (k != 0) 
return k;
}


void temp_ausgabe_serial()
{
for (int i=0; i<=5; i++)
{
Serial.print(TEMP_SENSOR[i],1);
Serial.print(F("  ("));
// Serial.print(mittelwert_L_berechnen(i),2);
Serial.print(F(")"));
Serial.print(F("  -  "));
}

Serial.print(F("  ---  "));
Serial.write(itoa(relais_byte,test,10));
Serial.print(F("  ---  "));
Serial.write(itoa(relais_vorschlag_byte,test,10));
Serial.println();
}


boolean sommer_feststellen()
{
if (TEMP_SENSOR[3]>sommer_temp_solarboiler)
return 1;
if (TEMP_SENSOR[3]<45)
return 0;
}


void sendData() // zu COSM
{
// this method makes a HTTP connection to the server:
// if there's a successful connection:
    if (client.connect(server, 80)) 
{
Serial.println(F("connecting..."));
// send the HTTP PUT request:
client.print(F("PUT /v2/feeds/"));
client.print(FEEDID);
client.println(F(".csv HTTP/1.1"));
client.println(F("Host: api.pachube.com"));
client.print(F("X-PachubeApiKey: "));
client.println(APIKEY);
client.print(F("User-Agent: "));
client.println(USERAGENT);    

client.print(F("Content-Length: "));
client.println(strlen(temp_string), DEC);

// last pieces of the HTTP PUT request:
client.print(F("Content-Type: text/csv\n"));
client.println(F("Connection: close\n"));
// Serial.print(F("Laenge des Strings: ")); // was kommt bei der Laenge des Contents heraus??
// Serial.println(strlen(temp_string), DEC);

// here's the actual content of the PUT request:
Serial.println(temp_string);
client.println(temp_string);

Serial.print(F("Nun der SD-Archiv-String: "));
Serial.println(temp_a_string);
  
// note the time that the connection was made:
lastConnectionTime = millis();
}
else 
{
// if you couldn't make a connection:
Serial.println(F("connection failed"));
Serial.println();
Serial.println(F("disconnecting."));
client.stop();
}
}


void daten_ablegen_versenden()
{
// if there's incoming data from the net connection, send it out the serial port.  
// This is for debugging purposes only:
while (client.available())
{
char c = client.read();
Serial.print(c);
    } 

// if there's no net connection, but there was one last time then stop the client:
if (!client.connected() && lastConnected) 
{
Serial.println();
Serial.println(F("disconnecting.."));
client.stop();
}

// if you're not connected, and ten seconds have passed since
// your last connection, then connect again and send data:
// war: if (!client.connected() && (millis() - lastConnectionTime > postingInterval)) // soll ich das !client.connected() herausnehmen????
if (millis() - lastConnectionTime > postingInterval) // soll ich das !client.connected() herausnehmen????
{
cosm_daten_zusammenstoepseln();
sendData(); // temp_string wird verschickt
sd_karte_archivieren();
}

// store the state of the connection for next time through the loop:
lastConnected = client.connected();
}


void cosm_daten_zusammenstoepseln()
{
//  Serial.println("daten_zusammenstoepseln!");  

strcpy (temp_string,"sensor0");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[0],3,2,tmp)));
strcat(temp_string,"\n");

strcat (temp_string,"sensor1");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[1],3,2,tmp)));
strcat(temp_string,"\n");

strcat (temp_string,"sensor2");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[2],3,2,tmp)));
strcat(temp_string,"\n");

strcat (temp_string,"sensor3");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[3],3,2,tmp)));
strcat(temp_string,"\n");

strcat (temp_string,"sensor4");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[4],3,2,tmp)));
strcat(temp_string,"\n");

strcat (temp_string,"sensor5");    strcat(temp_string,",");
strcat(temp_string,(dtostrf(TEMP_SENSOR[5],3,2,tmp)));
strcat(temp_string,"\n");

 
strcat(temp_string,"relais0");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,0),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais1");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,1),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais2");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,2),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais3");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,3),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais4");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,4),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais6");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,6),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"relais7");    strcat(temp_string,",");
strcat(temp_string,(itoa(byte_bit_abfragen(relais_byte,7),test,10)));
strcat(temp_string,"\n");

strcat(temp_string,"manuell");    strcat(temp_string,",");
strcat(temp_string,(itoa(arbeitsmodus,test,10)));
strcat(temp_string,"\n");

// nun Archiv_string zusammenstoepseln
// unsigned long epoch2 = 1363644467; // ja, es geht :-)

unsigned long epoch2 = now() + (millis()-letzte_zeit) / 1000;
// war unsigned long epoch2 = epoch + (millis()-letzte_zeit) / 1000;

strcpy(temp_a_string," "); strcat(temp_a_string,(ltoa(epoch2,tmpp,10)));

strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[0],3,2,tmp)));
strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[1],3,2,tmp)));
strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[2],3,2,tmp)));
strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[3],3,2,tmp)));
strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[4],3,2,tmp)));
strcat(temp_a_string,","); strcat(temp_a_string,(dtostrf(TEMP_SENSOR[5],3,2,tmp)));
 
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,0),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,1),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,2),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,3),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,4),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,6),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(byte_bit_abfragen(relais_byte,7),test,10)));
strcat(temp_a_string,","); strcat(temp_a_string,(itoa(arbeitsmodus,test,10)));
}


void lcd_neuausgabe()
{
lcd_reset();
lcd_ausgabe();  // vorläufig; hier detailliertere Infos ausgeben
zeit_lcd_ausgabe();
// relais_byte_senden(relais_byte);  
lcd.setCursor(3,2); 
lcd.print(F("M"));
lcd.print(freeRam());
lcd.print(F("T"));
lcd.print(int(millis()/3600000));lcd.print(F(":")); lcd.print(int((millis()%3600000)/60000));

}


void lcd_reset()
{ // jede 600 Sek. = 10 Min 
if (zeitabstand_bestimmen_zum(time_letzter_lcd_reset)>time_abstand_lcd_reset)  // wurde vor mehr als X Min. gemessen? Dann führe erneute Messung durch!
{
lcd_hardreset();
}
}

void lcd_hardreset()
{ // jede 600 Sek. = 10 Min 
lcd_init();
lcd.clear();
lcd_ausgabe();
zeit_lcd_ausgabe();
time_letzter_lcd_reset=millis();
}


void lcd_init()
{
lcd.begin(20, 4);
// lcd.init();                      // initialize the lcd 
// lcd.backlight();
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home ();                   // go home

lcd.createChar(0, boilerwand);
lcd.createChar(1, arrow_left);
lcd.createChar(2, arrow_right);
lcd.createChar(3, ecke_re);
lcd.createChar(4, ecke_li);
lcd.createChar(5, clock);

lcd.createChar(6, linie);
lcd.createChar(7, retarrow);
}


int freeRam() 
{
extern int __heap_start, *__brkval; 
int v; 
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


void sd_karte_archivieren()
{

digitalWrite(10, HIGH); // deaktiviere Ethernet
digitalWrite(4, LOW); // aktiviere SD-Card 
  
SD_Datei = SD.open("heizung.csv", FILE_WRITE); // hier ist der name der Datei auf der SD-Karte abgespeichert
if (SD_Datei)
{
Serial.print(F("Writing to FILE..."));
SD_Datei.println(temp_a_string);
// close the file:
SD_Datei.close();
Serial.println(F("done."));

else
{
// if the file didn't open, print an error:
Serial.println(F("error opening file on SD-card"));
}

digitalWrite(4, HIGH); // deaktiviere SD-Karte
digitalWrite(10, LOW); // aktiviere Ethernet

// vgl. http://macherzin.net/article27-Arduino-Kommunikation-Ethernet
// hier aber mit dem PIN 8, und nicht 4 - noch zu klären
}


unsigned long sendNTPpacket(IPAddress &address)
{
//send an NTP request to the time server at the given address 
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE); 
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011;   // LI, Version, Mode
packetBuffer[1] = 0;     // Stratum, or type of clock
packetBuffer[2] = 6;     // Polling Interval
packetBuffer[3] = 0xEC;  // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12]  = 49; 
packetBuffer[13]  = 0x4E;
packetBuffer[14]  = 49;
packetBuffer[15]  = 52;

// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:   
Udp.beginPacket(address, 123); //NTP requests are to port 123
Udp.write(packetBuffer,NTP_PACKET_SIZE);
Udp.endPacket(); 
Serial.println(F("NTP-Paket gesendet" ));
}


void unixtime_umrechnen_aktualisieren()
{
stunde=hour()+(int)summertime_EU(year(),month(),day(),hour(),1);
minuten=minute();
sekunden=second();
}


boolean summertime_EU(int year, byte month, byte day, byte hour, byte tzHours)
// Quelle: http://forum.arduino.cc/index.php?topic=172044.msg1278536#msg1278536
// European Daylight Savings Time calculation by "jurs" for German Arduino Forum
// input parameters: "normal time" for year, month, day, hour and tzHours (0=UTC, 1=MEZ)
// return value: returns true during Daylight Saving Time, false otherwise

  if (month<3 || month>10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
  if (month>3 && month<10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
  if (month==3 && (hour + 24 * day)>=(1 + tzHours + 24*(31 - (5 * year /4 + 4) % 7)) || month==10 && (hour + 24 * day)<(1 + tzHours + 24*(31 - (5 * year /4 + 1) % 7))) 
    return true; 
  else 
    return false;
}


void zeitabfrage_ausgabe()
{
// Quelle: Sketch-Beispiel für die Zeit
if((millis()-time_letzte_zeitanforderung>100)&&(zeitpaket_angefordert==true))
{
zeitpaket_angefordert=false; // gleich den Schalter zurücksetzen
letzte_zeit=millis();
setSyncProvider(getNtpTime);
zeitpaket_geliefert=true;
}

if((zeitpaket_angefordert==false)&&(millis()-letzte_zeit>time_abstand_zeit_abfrage))
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println(F("Transmit NTP Request"));
sendNTPpacket(timeServer);

time_letzte_zeitanforderung=millis();
zeitpaket_angefordert=true;
lcd.clear(); // regelmaessig den screen säubern 
lcd_ausgabe();
zeit_lcd_ausgabe();
}
}


time_t getNtpTime()
{
  uint32_t beginWait = millis();
  while (millis() - beginWait < 1500) {
    int size = Udp.parsePacket();
    if (size >= NTP_PACKET_SIZE) {
      Serial.println(F("Receive NTP Response"));
      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
      unsigned long secsSince1900;
      // convert four bytes starting at location 40 to a long integer
      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
      secsSince1900 |= (unsigned long)packetBuffer[43];
      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
    }
  }
  Serial.println(F("No NTP Response :-("));
  return 0; // return 0 if unable to get the time
}


void relais_vorschlagsbyte_kontrollieren()
{
// Konflikte klären
if (byte_bit_abfragen(relais_vorschlag_byte,1)==1 && byte_bit_abfragen(relais_vorschlag_byte,4)==1) // Kollision zwischen Auffüllen und Umwälzen abfangen, dann nichts machen!
{
relais_vorschlag_byte=relais_byte;           // das manipulierte Vorschlag-Byte auf den Ursprungswert zurücksetzen 
Serial.println(F("Achtung: KOLLISION 1")); 
}                                          

if (byte_bit_abfragen(relais_vorschlag_byte,6)==1 && byte_bit_abfragen(relais_vorschlag_byte,7)==1) // Kollision 
{
relais_vorschlag_byte=relais_byte;           // das manipulierte Vorschlag-Byte auf den Ursprungswert zurücksetzen 
Serial.println(F("Achtung: KOLLISION 2")); 
}                                          

if (byte_bit_abfragen(relais_vorschlag_byte,2)==1 && byte_bit_abfragen(relais_vorschlag_byte,3)==1) // Kollision 
{
relais_vorschlag_byte=relais_byte;           // das manipulierte Vorschlag-Byte auf den Ursprungswert zurücksetzen 
Serial.println(F("Achtung: KOLLISION 3"));  
}                                          

/*

// Übermäßige Laufzeit beenden

if (byte_bit_abfragen(relais_vorschlag_byte,0)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[0])>time_vorgabe_pumpen_abschaltung)
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,0,0);

Serial.println("Zusatz-Pumpe nach einer Stunde ausschalten!"); 
}

// Die Boileraufwärmung kann mehr als eine Stunde dauern
if (byte_bit_abfragen(relais_vorschlag_byte,1)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[1])>time_vorgabe_pumpen_abschaltung)
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);
Serial.println("Boileraufladung nach einer Stunde ausschalten!"); 
}

if (byte_bit_abfragen(relais_vorschlag_byte,4)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[4])>time_vorgabe_pumpen_abschaltung)
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,4,0);
Serial.println("Boilertausch nach einer Stunde ausschalten!"); 
}*/
}


void relaiswechsel_zeit_ablegen()
{
for (int i=0; i<8; i++)
{
if (byte_bit_abfragen(relais_byte,i)!=byte_bit_abfragen(relais_vorschlag_byte,i))
{
time_letzter_relaiswechsel[i]=millis();
// Serial.print("Relaiswechsel: ");Serial.println(i);
}
}
}


void relaiswechsel_umsetzungsbyte_kontrollieren()
{
byte altes_relais_befehls_byte=relais_befehls_byte;
relais_befehls_byte=relais_vorschlag_byte;

/* if (byte_bit_abfragen(relais_befehls_byte,6)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[6])>time_vorgabe_3WV_abschaltung)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,2,0);
}

// if (byte_bit_abfragen(altes_relais_befehls_byte,2)==0 && byte_bit_abfragen(relais_befehls_byte,2)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[2])<time_abstand_3WV_schaltung)
// {
// relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,2,0);
// }

if (byte_bit_abfragen(altes_relais_befehls_byte,3)==1 && byte_bit_abfragen(relais_befehls_byte,3)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[3])>time_vorgabe_3WV_abschaltung)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,3,0);
}

if (byte_bit_abfragen(altes_relais_befehls_byte,6)==1 && byte_bit_abfragen(relais_befehls_byte,6)==1 && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[6])>time_vorgabe_3WV_abschaltung)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,6,0);
}

if (byte_bit_abfragen(altes_relais_befehls_byte,7)==1 && byte_bit_abfragen(relais_befehls_byte,7)==1 && zeitabstand_bestimmen_zum([7])>time_vorgabe_3WV_abschaltung)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,7,0);
}

// relais_vorschlags_byte -> relais_befehls_byte

if    (byte_bit_abfragen(altes_relais_befehls_byte,6)==0 && byte_bit_abfragen(relais_befehls_byte,6)==1 
&& byte_bit_abfragen(altes_relais_befehls_byte,7)==1 && byte_bit_abfragen(relais_befehls_byte,7)==0)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,6,0);
// relais3WV_switch=1;
}

if    (byte_bit_abfragen(altes_relais_befehls_byte,6)==1 && byte_bit_abfragen(relais_befehls_byte,6)==0 
&& byte_bit_abfragen(altes_relais_befehls_byte,7)==0 && byte_bit_abfragen(relais_befehls_byte,7)==1)
{
relais_befehls_byte=relais_bit_setzen(relais_befehls_byte,7,0);
// relais3WV_switch=1;
}
*/


/* relais_byte_senden(relais_befehls_byte);
if (// relais3WV_switch==1) 
{
delay(1000);
// relais3WV_switch=0;
}
// Serial.print("Relaisvorschlag: "); Serial.println(relais_vorschlag_byte);
// Serial.print("Relaisstand: "); Serial.println(relais_byte);
*/
}


void setup()
{
// zum Abschalten der unbenutzten Pins
for (uint8_t pin=0; pin<69; ++pin) 
{
pinMode(pin, INPUT);
digitalWrite(pin, HIGH);
}
// für die Eingänge der Temperatursensoren

pinMode(62, INPUT);  
    pinMode(63, INPUT);  
    pinMode(64, INPUT); 
    pinMode(65, INPUT); 
    pinMode(66, INPUT); 
pinMode(67, INPUT); 

analogReference(INTERNAL1V1); // für die 1,1V Vergleichsspannung des Mega

// Open serial communications and wait for port to open:
Serial.begin(BOUD_RATE);
delay(500);

Serial.println(F("Setup SD beginnt 1"));
// disable w5100 SPI while setting up SD
pinMode(4,OUTPUT);
// pinMode(8,OUTPUT);
pinMode(10,OUTPUT);
pinMode(53, OUTPUT); // Nur MEGA: Pin 53 fuer SD-Card
Serial.println(F("Regulaeres Setup beginnt 2"));
digitalWrite(4, LOW); // aktiviere SD-Karte
digitalWrite(10,HIGH); // deaktiviere Ethernet

if(SD.begin(4) == 0) 
Serial.println(F("SD failed"));
else
Serial.println(F("SD ok"));

digitalWrite(4, HIGH); // deaktiviere SD-Karte - oder doch die 6?
digitalWrite(10, LOW); // aktiviere Ethernet
Serial.println(F("SD-Card Setup beendet"));


time_letzte_temp_messung=
time_letzter_lcd_reset=
time_letzte_zeitanforderung=
time_d=
time=
letzte_zeit=
millis(); // alle Zwischenstände beim Start auf praktisch Null setzen

 // -----------------------------------------

Wire.begin(); // für den Anschluss der Expander-Karte 
delay(500);
Serial.println(F("setup1 erfolgreich"));
 
kpd.begin();                          // Keypad initialisieren
delay(500);
Serial.println(F("setup2 erfolgreich"));

//Ansteuerungs-Byte der Relaiskarte auf null setzen
relais_byte=0;
relais_byte_senden(byte(0)); // für ein Reset alles auf 0!
relais_befehls_byte=0;

//time_d=millis();
//for (int i=0; i<8;i++) time_letzter_relaiswechsel[i]=time_d;
//Serial.println(F("setup3 erfolgreich"));

lcd_init();                      // initialize the lcd 
lcd.clear(); 
lcd_ausgabe();
zeit_lcd_ausgabe();
Serial.println(F("setup4 erfolgreich"));

// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) 
//   Ethernet.begin(mac, ip, gateway, subnet); // starte Ethernet! verbaucht weniger platz
// delay(1000); // warten auf Ethernet
{
delay(1500);
Serial.println(F("Failed to configure Ethernet using DHCP"));
// DHCP failed, so use a fixed IP address:
Ethernet.begin(mac, ip, gateway, subnet);
delay(1500);
}
Serial.println(F("Success to configure Ethernet using DHCP"));

Udp.begin(localPort);// für UDP
delay(250);
Serial.println(F("setup5 erfolgreich"));
Serial.println(F("Success with whole setup"));

// zum Schluss den Watchdog aktivieren! Totzeitraum max. 8 Sek zum Reset
wdt_enable(WDTO_8S); 




void loop()

wdt_reset(); // Reset des Watchdogs
lcd_reset(); // gegen die ewigen LCD-Probleme :-)
relais_vorschlag_byte=relais_byte;
temp_erfassung_ausgabe(); // wird nur jede X Sek wirklich durchgeführt

zeitabfrage_ausgabe();
zeit_lcd_ausgabe();

key=0;
key=kpd.getKey(); // neu, war: key=keypad_ergebnis();


if (key!=0 || neue_temp_da==1) 
{
if (key=='A')    { arbeitsmodus=0;     lcd_reset(); lcd_ausgabe();  }  
if (key=='B')    { arbeitsmodus=1;     lcd_reset(); lcd_ausgabe();  }
if (key=='C')    { lcd_hardreset(); }
if (key=='D')    { lcd_neuausgabe(); } // RESET des LCD und der Relaiskarte

if (arbeitsmodus==1)    // MANUELL
{
if (key=='0' || key=='1' || key=='2' || key=='3' || key=='4' || key=='6' || key=='7')
{
key2=key-48; // den ASCII-Key umrechnen
relais_vorschlag_byte=relais_bit_wechseln(relais_vorschlag_byte, key2);
}
else 
{
if (key=='A' || key=='B' || key=='C' || key=='D' || key==0 ) 
{
// zulässige Eingaben ausschliessen 
}
else
{
lcd.clear();                   //        Fehlermeldung("Falsche Taste")   
lcd.setCursor(0, 1); lcd.print(F("   NUR 0,1,2,  "));
lcd.setCursor(0, 2); lcd.print(F("3,4,6,7,A,B,C,D"));
delay(1000);       // obwohl es ein Delay ist, ist es gerade hier richtig
lcd.clear(); 
lcd_ausgabe();                 // aktuellen Zustand am LCD erneut anzeigen
zeit_lcd_ausgabe();
}
}
}

if (arbeitsmodus==0)    // AUTOPILOT
{
// Serial.print("gesetzt auf autopilot\n"); // gesetzt auf manuell
// Ausgabe des Displays
/*if (key=='D')    // Reset des LCD und der Relaiskarte
{
lcd_neuausgabe();
}*/

// Prüfung der Vorlauftemperatur, um evtl. die Zusatzpumpe anzuwerfen
                           
if (TEMP_SENSOR[4]>=max_temp_vorlauf)                               // falls Temp Vorlauf zu hoch, die Zusatzpumpe einschalten!
{
// Serial.print("Temp Vorlauf >= Temp B o !!");          
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,0,1);

else
{
// Serial.print("Temp Vorlauf <= Temp B o !!");          
if (TEMP_SENSOR[4]<=(max_temp_vorlauf-zusatzpumpen_hysteresis))          
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,0,0);
}
}

// Boilerauswahl Warmwasser

if (zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[6]) > time_abstand_3WV_schaltung 
   && zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[7]) > time_abstand_3WV_schaltung)
{

if (byte_bit_abfragen(relais_byte,6)==0 && byte_bit_abfragen(relais_byte,7)==0) // für den Start, wenn alle Bits auf 0 gesetzt sind
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,0);        // ... grossen Boiler als WW-Quelle auswählen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,1);        // ... grossen Boiler als WW-Quelle auswählen
}

if (TEMP_SENSOR[0]>40)
{
if (zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[6]) > time_abstand_WW_boiler_wechsel)
{
if(byte_bit_abfragen(relais_byte,6)==1) // Prüfung, ob WW gerade vom kleiner Boiler kommt
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,0);        // ... grossen Boiler als WW-Quelle auswählen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,1);        // ... grossen Boiler als WW-Quelle auswählen
}
else
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,1);        // ... kleinen Boiler als WW-Quelle auswählen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,0);        // ... kleinen Boiler als WW-Quelle auswählen
}
}
else
{
// braucht man nicht, da keine Veränderung des Vorschlagbytes
// relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,byte_bit_abfragen(relais_byte,6));        // auf bisherige Werte setzen
// relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,byte_bit_abfragen(relais_byte,7));        // auf bisherige Werte setzen
}
}
else
{
if (TEMP_SENSOR[2]>TEMP_SENSOR[0])   // Großer Boiler wärmer als der kleine ...
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,0);       //... kleinen  Boiler als WW-Quelle abwählen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,1);       //... großen Boiler als WW-Quelle auswählen
}
else // ... nein, der kleine Boiler ist wärmer
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,1);       // ... kleinen Boiler als WW-Quelle auswählen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,7,0);       // ... kleinen Boiler als WW-Quelle auswählen
}

}

}

// Umwälzung der Boiler im Sommer

if (zeitabstand_bestimmen_zum(time_letzter_relaiswechsel[4]) > time_abstand_pumpen_schaltung)
{
if (byte_bit_abfragen(relais_byte,4)==0) // falls Boileraustausch nicht stattfindet (==0) 
{
if ((sommer_feststellen()==1) &&   //Festlegung, wann es zur Umwälzung kommen soll: Falls Sommer und ...
((TEMP_SENSOR[2]-TEMP_SENSOR[1])>10) && //Temp.Differenz zwischen großen und kleinen Boiler >10 Grad und ...
(TEMP_SENSOR[2]>max_B_temp) && //Temp-Gr-Boiler > max. zulässige Temp und ...
(TEMP_SENSOR[0]<=60))               //Temp-Kl-Boiler <= max. zulässige Temp und ...
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);    // Befüllung stoppen (falls sowoeso nicht gestoppt!)
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,4,1);    // Umwälzung zw den Boilern beginnen          
}
}

if (byte_bit_abfragen(relais_byte,4)==1) // falls Boileraustausch stattfindet (==0) 
{
if ((sommer_feststellen()==0) ||
((TEMP_SENSOR[2]-TEMP_SENSOR[1])<5) || // falls Temp-Differenz zw. Gr-Boiler und Kl-Boiler unter 5 Grad fällt oder...
(TEMP_SENSOR[2]<=50)) // || // falls Temp Gr-Boiler unter 45 Grad fällt oder ...
//TEMP_SENSOR[0]>max_B_temp) // falls Temp Kl-Boiler größer als max zulässige Boiler-Temp
// Festlegung, wann die Umwälzung aufhören soll! Sommer_feststellen wohl nicht erforderlich :-)
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,4,0);    // Umwälzung zw den Boilern stoppen          
}
}
}

// Aufheizung der Boiler im Winter

if(byte_bit_abfragen(relais_byte,1)==1) // Prüfung, ob der Boiler gerade aufgeladen wird
{
// Serial.print("Die Aufheizung des Boilers laeuft.\n");                                                           // Eine Meldung ausgeben
if (key=='#')  // wenn Heizung manuell als Ziel ausgewählt wird
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);      // Auffüllen des Boilers beenden, Relais schließen
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,2,0);      // dazu das Aufladungsrelais in den Ruhezustand schalten
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,3,0);      // dazu das Aufladungsrelais in den Ruhezustand schalten
}

if (byte_bit_abfragen(relais_byte,2)==1 && TEMP_SENSOR[0]>max_B_temp) // falls die Temp des gerade befüllten kleinen Boilers oben größer als erlaubte Temp ...
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);      // ... die Befüllung des Boilers beenden
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,2,0);      // auch das 3WV-Relais in den Ruhezustand schalten
// Serial.print("Aufheizung des Boilers wird gleich unterbrochen werden, da die Temperatur des Boilers inzwischen groesser als erlaubt ist!\n");Serial.print(TEMP_SENSOR[(2-Heizung_3WV*2),2]);
// evtl. später eine entsprechende LCD-Meldung einbauen
// temp_ausgabe_serial();
}

if (byte_bit_abfragen(relais_byte,3)==1 && TEMP_SENSOR[2]>max_B_temp) // falls die Temp des gerade befüllten großen Boilers oben größer als erlaubte Temp ...
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);      // ... die Befüllung des Boilers beenden
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,3,0);      // auch das 3WV-Relais in den Ruhezustand schalten
}

if (byte_bit_abfragen(relais_byte,2)==1 && TEMP_SENSOR[4]<(TEMP_SENSOR[0]+3))    // Vorlauf um 3 Grad kleiner als Temperatur des geschalteten Boilers oben?
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);      // ... die Befüllung des Boilers beenden
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,2,0);      // auch das 3WV-Relais in den Ruhezustand schalten
}

if (byte_bit_abfragen(relais_byte,3)==1 && TEMP_SENSOR[4]<(TEMP_SENSOR[2]+3))    // Vorlauf um 3 Grad kleiner als Temperatur des geschalteten Boilers oben?
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,0);      // ... die Befüllung des Boilers beenden
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,3,0);      // auch das 3WV-Relais in den Ruhezustand schalten
}
}
else // falls der Boiler gerade nicht aufgeladen wird
{
// Serial.print("Die Aufheizung des Boilers findet nicht statt\n");   // Eine Meldung ausgeben        
if (key=='#')  // wenn Heizung als Ziel ausgewählt wird
{
// Serial.print("Sie haben # ausgewaehlt, Heizung ist jedoch bereits als Ziel ausgewaehlt\n");       // Eine Meldung ausgeben
// temp_ausgabe_serial();
}
if (key=='*')  // wenn Boiler als Ziel ausgewählt wird
{
if ((TEMP_SENSOR[2]<=max_B_temp)&&((TEMP_SENSOR[2]+2)<TEMP_SENSOR[4])) // 500l-Boiler-Temp kleiner als max.Temp und kleiner als Vorlauf-Temp
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,2,0); // das 3WV in 300l-Boiler-Richtung aus!
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,3,1); // auch das 3WV-Relais in 500l-Richtung schalten
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,1); // ... die Befüllung des Boilers beginnen                     
// Serial.print("Sie haben * ausgewaehlt, max. Temp des gr. Boilers ist noch nicht erreicht und der Vorlauf ist fuer den gr. Boiler warm genug, also wird der gr. Boiler aufgeladen\n");                       // Eine Meldung ausgeben
// temp_ausgabe_serial();
}
else // falls Boiler befüllt werden soll und Gr-Boiler warm genug ODER der Vorlauf reicht zum Erwärmen des großen Boilers nicht aus
{
// Serial.print("Sie haben * ausgewaehlt, max. Temp des gr. Boilers ist bereits erreicht oder der Vorlauf ist fuer den gr. Boiler nicht warm genug (evtl. aber fuer den 300l-Boiler)\n");                       // Eine Meldung ausgeben
if ((TEMP_SENSOR[0]<=max_B_temp)&&((TEMP_SENSOR[0]+2)<TEMP_SENSOR[4]))        // 300l-Boiler-Temp kleiner als max.Temp und kleiner als Vorlauf-Temp
{
// Serial.print("Sie haben * ausgewaehlt, max. Temp des kl. Boilers ist noch nicht erreicht und der Vorlauf ist fuer den kl. Boiler warm genug, also wird der kl. Boiler aufgeladen\n");                       // Eine Meldung ausgeben
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,2,1);      // das Relais zum 300l-Boiler schalten 
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,3,0);      // das Relais in 500l-Richtung ausschalten
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,1);      // ... die Befüllung des (kleinen) Boilers beginnen
// temp_ausgabe_serial();
}
else
{
// Serial.print("Sie haben * ausgewaehlt, max. Temp auch des kl. Boilers ist bereits erreicht oder der Vorlauf ist fuer den kl. Boiler nicht warm genug, also wird auch er nicht aufgeladen\n");                       // Eine Meldung ausgeben
// temp_ausgabe_serial();
}
}
}
}
}  // Hier endet der Anweisungsblock für den Arbeitsmodus = 0 (autopilot)

}                // Ende der Verarbeitungsschleife, wenn key != 0

relais_vorschlagsbyte_kontrollieren();

if (relais_byte!=relais_vorschlag_byte)
{
relaiswechsel_zeit_ablegen();
relais_byte=relais_vorschlag_byte;
lcd_ausgabe();
}

if (neue_temp_da==1) 
{
lcd_ausgabe();
}

relais_byte_senden(relais_byte);

}                // Ende der loop-Funktion

Keine Kommentare:

Kommentar veröffentlichen