Samstag, 17. Mai 2014

Neuer Code der Heizungssteuerung mit Fehlerkorrekturen, neuem Timeserver und Sommerzeitumstellung

Letztes Ärgernis, die angezeigte Zeit zwei Mal im Jahr umstellen zu müssen, ist nun behoben.
Ein paar kleinere Bugfixes sind auch dabei.
Der Time-Server ist nun meine FritzBox: 192.168.178.106

/*
/*
Bei der 2. Version des Arduino Ethernet Shields beachten:
"Yes, but when you reset the device before uploading, you can still upload the sketch WITH the shield attached, so it's quick and convenient."
aus
http://arduino.cc/forum/index.php/topic,67777.45.html#msg711073


Bei der 2. Version des Arduino Ethernet Shields beachten:
"Yes, but when you reset the device before uploading, you can still upload the sketch WITH the shield attached, so it's quick and convenient."
aus
http://arduino.cc/forum/index.php/topic,67777.45.html#msg711073
*/

#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);


#define APIKEY         "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // your cosm api key
#define FEEDID         xxxxx // your feed ID-  Arduino Testversion 1
#define USERAGENT      "Heizduino (xxxxxx)" // 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
// 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_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 (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
}

if (TEMP_SENSOR[0]>40)
{
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)
{
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);
}
}
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