Die Heizungssteuerung mit dem Arduino Mega 2560 befindet sich im produktiven Einsatz. Der Mega sendet die erfassten Daten zur grafischen Darstellung an Cosm. Es ist möglich, die Daten überall von der Ferne abzufragen.Diese Angaben werden auch lokal auf einem 4-zeiligen Display angezeigt (20x4). Eine manuelle Steuerung ist mit einem Keypad möglich (PCF8574). Sonst steuert der Mega über die Relaiskarte (Solid State Relais) Pumpen und Ventile vollautomatisch. Das System ist 24/7 sicher (Watchdog)
Montag, 29. Juli 2013
Aktueller Code für die Heizungssteuerung
#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include <Ethernet.h>
#include <LCD.h>
#include <Keypad.h>
#include <Keypad_I2C.h>
#include <SPI.h>
#include <EthernetUdp.h> // für die NTP-Abfrage
#include <SD.h> // für die SD-Kartennutzung
// 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
// 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, 53, 103, 108); // ntp1 ptb.de 192.53.103.108 Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
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 minute=0;
int sekunde=0;
// fuer COSM - Testversion
/*#define APIKEY "xxx" // your cosm api key
#define FEEDID 75484 // your feed ID - Arduino Testversion 2
#define USERAGENT "Cosm Arduino Example (75484)" // user agent is the project name
*/
#define APIKEY "XXX" // your cosm api key
#define FEEDID 90041 // your feed ID- Arduino Testversion 1
#define USERAGENT "Heizduino (90041)" // user agent is the project name
// 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'}
};
// 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 - für später
// Bit 3 - 3-Wege-Ventil für die Aufheizung: in kleinen oder 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- vs. 500l-Boiler
// Bit 7 -
// dementsprechend werden die Relais zugeordnet!
*/
byte relais_byte; // darüber werden die Methoden gesteuert
byte relais_vorschlag_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;
unsigned long time; // wird nach ca. X Tagen wieder auf Null gestezt
unsigned long time_z1=0; // Zwischenstand für temp_erfassung_ausgabe()
unsigned long time_z2=0; // Zwischenstände für regelmäßigen lcd_reset
unsigned long time_z3=0; // Zwischenstände für regelmäßigen Zeit-Ausgabe
unsigned long 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
unsigned int Heizung_3WV=0; // das hier ist das 3-Wege-Ventil
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=50; // 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=50; // Temperatur, die ein Boiler maximal erreichen darf, um keine Kalkablagerungen abzukriegen
const float max_temp_vorlauf=85; // ab hier schaltet die Zusatzpumpe
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
char key;
byte key2;
byte key_i;
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?
// 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);
// 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];
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 */
/* 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;
// 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);
// Keypad Instanz
Keypad_I2C kpd = Keypad_I2C(makeKeymap(keys),rowPins,colPins,ROWS,COLS,PCF8574_ADDR); // für das Keypad
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);
}
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
}
}
void zeit_lcd_ausgabe()
{
time=millis();
if (time>=time_z3)
{
time_d=time-time_z3;
}
else
{
time_d=4294967295-time_z3+time;
}
if (time_d>1000) // erst nach Ablauf einere Sek Zeit neu ausgeben
{
unixtime_umrechnen_aktualisieren();
if(zeitpaket_angefordert==false&&zeitpaket_geliefert==true)
{
lcd.setCursor(4, 1);
lcd.printByte(5);
}
else
{ lcd.setCursor(4, 1);
lcd.print(F(" "));
}
lcd.setCursor(5, 1);
lcd.print(stunde);
lcd.print(F(":"));
if ( minute < 10 )
{
// In the first 10 minutes of each hour, we'll want a leading '0'
lcd.print(F("0"));
}
lcd.print(minute);
lcd.print(F(":"));
if ( sekunde < 10 )
{
// In the first 10 seconds of each minute, we'll want a leading '0'
lcd.print(F("0"));
}
lcd.print(sekunde);
}
}
void lcd_ausgabe()
{
byte bitmask;
Serial.println(F("lcd_ausgabe!"));
//lcd.clear();
//ZEICHNUNG
for (int i=0;i<=3;i++)
{
lcd.setCursor(2, i);
lcd.write(uint8_t(0));
}
for (int i=0;i<=3;i++)
{
lcd.setCursor(15, 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
bitmask=1<<0; //Zusatzpumpe: "+"
if ((bitmask & relais_byte)>0)
{
lcd.setCursor(8, 3);
lcd.print(F("+"));
}
else
{
lcd.setCursor(8, 3);
lcd.print(F(" "));
}
bitmask=1<<4; //Umwälzung zwischen den beiden Boilern
// Umwälzen und Aufheizen findet niemals zusammen statt, daher diese Abfrage zuerst
if ((bitmask & relais_byte)>0)
{
lcd.setCursor(2, 2);
lcd.printByte(1);
for (int i=0;i<=11;i++) {lcd.printByte(6);}
lcd.printByte(2);
}
else
{
lcd.setCursor(2, 2);
lcd.write(uint8_t(0));
lcd.print(F(" "));
lcd.write(uint8_t(0));
}
bitmask=1<<1; //Aufheizung: "|"
if ((bitmask&relais_byte)>0)
{
bitmask=1<<3; //Aufheizung des kl. (0) o. gr. (1) Boilers
if ((bitmask&relais_byte)==0)
{
lcd.setCursor(2, 2);
lcd.write(uint8_t(0));
lcd.print(F(" "));
lcd.printByte(3);
for (int i=0;i<=4;i++)
lcd.printByte(6);
lcd.printByte(2);
}
else
{
lcd.setCursor(2, 2);
lcd.printByte(1);
for (int i=0;i<=5;i++)
{
lcd.printByte(6);
}
lcd.printByte(4);
lcd.print(F(" "));
lcd.write(uint8_t(0));
}
}
else
{
}
bitmask=1<<6; //Entnahme aus kleinen (1) bzw. großen Boiler (0)
if ((bitmask & relais_byte)>0)
{
lcd.setCursor(2, 0);
lcd.printByte(2);
lcd.print(F("WW"));
lcd.setCursor(13, 0);
lcd.print(F(" "));
lcd.write(uint8_t(0));
}
else
{
lcd.setCursor(2, 0);
lcd.write(uint8_t(0));
lcd.print(F(" "));
lcd.setCursor(13, 0);
lcd.print(F("WW"));
lcd.printByte(1);
}
// und zum Schluss das Relais der Auffüllung zeigen, falls es auf 300 zeigt und damit umsonst arbeitet
bitmask=1<<3; //Entnahme aus kleinen (1) bzw. großen Boiler (0)
if ((bitmask&relais_byte)>0)
{
lcd.setCursor(9, 2);
lcd.printByte(4);
}
if (arbeitsmodus == 0)
{
lcd.setCursor(7, 0);
lcd.print(F("Auto"));
}
else
{
lcd.setCursor(7, 0);
lcd.print(F("Man "));
}
}
void expanderWrite(byte data)
{ // Ansteuerung der Expander-Karte
Wire.beginTransmission(expander_relais);
Wire.write(data);
delay(100); // um evtl. Stromstösse o.ä. durch Relais ? abzuwarteb
Wire.endTransmission();
delay(300); // um evtl. Stromstösse o.ä. durch Relais ? abzuwarteb
// lcd_init(); // um das Kaulderwelsch zu beseitigen, nicht mehr nötig
}
void expanderWrite_o_delay(byte data)
{
Wire.beginTransmission(expander_relais);
Wire.write(data);
Wire.endTransmission();
}
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)
{
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;
}
void relais_byte_senden_o_delay (byte vb)
{
vb=~vb; // Das Relaisbyte muss vor dem Senden invertiert werden, da nur bei bit=low das Relais schaltet!!
expanderWrite_o_delay(vb); //... und rüberschieben zur Relaiskarte
}
void relais_byte_senden (byte vb)
{
vb=~vb; // Das Relaisbyte muss vor dem Senden invertiert werden, da nur bei bit=low das Relais schaltet!!
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()
{
time=millis();
if (time>=time_z1)
{
time_d=time-time_z1;
}
else
{
time_d=4294967295-time_z1;
time_d=time_d+time;
}
if ((time_d>10000)) // wurde vor mehr als x Sek gemessen? Dann führe erneute Messung durch!
{
get_temperature();
//temp_archivieren(k,TEMP_SENSOR[k]);
time_z1=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();
}
byte relais_bit_wechseln(byte vb, byte pos)
{
// Serial.println("relais_bit_wechseln!");
byte bitmask2=1<<pos;
vb=vb^bitmask2;
return vb;
}
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 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,"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,"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 = 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,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(arbeitsmodus,test,10)));
}
void lcd_reset()
{ // jede 600 Sek. = 10 Min
time=millis();
if (time>=time_z2)
{
time_d=time-time_z2;
}
else
{
time_d=4294967295-time_z2+time;
}
if (time_d>600000) // wurde vor mehr als XXX Sek gemessen? Dann führe erneute Messung durch!
{
lcd_init();
lcd.clear();
lcd_ausgabe();
zeit_lcd_ausgabe();
time_z2=millis();
}
}
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()
{
unsigned long epoch2 = epoch + (millis()-letzte_zeit) / 1000;
stunde=(epoch2 % 86400L) / 3600 + 2; // Anpassung GMT an BERLIN
minute=(epoch2 % 3600) / 60;
sekunde=(epoch2 % 60); //
}
void zeitabfrage_ausgabe()
{
// Quelle: Sketch-Beispiel für die Zeit
if((millis()-letzte_zeitanforderung>1000)&&(zeitpaket_angefordert==true))
{
zeitpaket_angefordert=false; // gleich den Schalter zurücksetzen
if ( Udp.parsePacket() )
{
letzte_zeit=millis();
zeitpaket_geliefert=true;
// We've received a packet, read the data from it
Udp.read(packetBuffer,NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, extract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print(F("Seconds since Jan 1 1900 = " ));
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print(F("Unix time = "));
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
epoch = secsSince1900 - seventyYears; // subtract seventy years
Serial.println(epoch);
unixtime_umrechnen_aktualisieren();
// print the hour, minute and second:
Serial.print(F("The UTC time is ")); // UTC is the time at Greenwich Meridian (GMT)
Serial.print(stunde,DEC); // print the hour (86400 equals secs per day)
Serial.print(F(":"));
if ( ((epoch % 3600) / 60) < 10 )
{
Serial.print(F("0"));// In the first 10 minutes of each hour, we'll want a leading '0'
}
Serial.print(minute,DEC); // print the minute (3600 equals secs per minute)
Serial.print(F(":"));
if ( (epoch % 60) < 10 )
{
Serial.print(F("0"));// In the first 10 seconds of each minute, we'll want a leading '0'
}
Serial.println(sekunde,DEC); // print the second
}
else
{
zeitpaket_geliefert=false;
}
}
if((zeitpaket_angefordert==false)&&(millis()-letzte_zeit>600000))
{
Serial.println(F("Anforderung der ZEIT"));
sendNTPpacket(timeServer); // send an NTP packet to a time server
letzte_zeitanforderung=millis();
zeitpaket_angefordert=true;
lcd.clear(); // regelmaessig den screen säubern
lcd_ausgabe();
zeit_lcd_ausgabe();
}
}
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(BOUD_RATE);
delay(500);
analogReference(INTERNAL1V1);
/* pinMode(8, INPUT);
pinMode(9, INPUT);
pinMode(10, INPUT);
pinMode(11, INPUT);
pinMode(12, INPUT);
pinMode(13, INPUT);
*/
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"));
// 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"));
time_z1=time_z2=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"));
Udp.begin(localPort);// für UDP
delay(500);
Serial.println(F("setup3 erfolgreich"));
//Ansteuerungs-Byte der Relaiskarte auf null setzen
relais_byte=0;
relais_byte_senden(byte(0)); // für ein Reset alles auf 0!
Serial.println(F("setup4 erfolgreich"));
lcd_init(); // initialize the lcd
lcd.clear();
lcd_ausgabe();
zeit_lcd_ausgabe();
temp_3_alt=analogRead(TEMP_SENSOR_PIN[(3)]); // eine einfache Ermittlung reicht hoffentlich beim Setup
Serial.println(F("Success with whole setup"));
/*
for (int i=0; i<=5; i++)
{ //TEMP_SENSOR[i]=0; // aktuelle Werte auf Null setzen
temp_pointer[i]=0; // Pointer setzen!
for (int j=0;j<=large_archiv_groesse;j++)
{
TEMP_SENSOR_L[i][j]=0; // auch alle Archivwerte auf 0 setzen
}
}
*/
/* // damit wird die clock speed verändert!
// TWBR = 152;
TWBR = 158;
TWSR |= _BV (TWPS0); // inkl. prescaler! macht eine Freq von 12,5 kHz
*/
/* für die spätere Einbindung der SD-Karte
pinMode(4, OUTPUT); // PIN 4 fuer Kommunikation mit SD-Karte
pinMode(10, OUTPUT); // PIN 10 fuert Kommunikation mit Ethernet
pinMode(53, OUTPUT); // Nur MEGA: Pin 53 fuer SD-Card
*/
}
void loop()
{
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==0)
{
// leeren Key und keine Temp-Veränderung da nicht neu gelesen, abfangen!!
}
else
{
if (key=='A') { arbeitsmodus=0; lcd_reset(); lcd_ausgabe(); }
if (key=='B') { arbeitsmodus=1; lcd_reset(); lcd_ausgabe(); }
if (key=='D') // RESET des LCD und der Relaiskarte
{
lcd_reset();
lcd_ausgabe(); // vorläufig; hier detailliertere Infos ausgeben
zeit_lcd_ausgabe();
relais_byte_senden(relais_byte);
lcd.setCursor(4,2);
lcd.print(F("mem"));
lcd.print(freeRam());
}
if (arbeitsmodus==1) // MANUELL
{
if (key=='0' || key=='1' || key=='3' || key=='4' || key=='6')
{
key2=key-48;
relais_vorschlag_byte=relais_bit_wechseln(relais_vorschlag_byte, key2);
}
else
{
if (key=='A' || key=='B' || key=='D' || key==0 )
{
// zulässige Eingaben ausschliessen
}
else
{
lcd.clear(); // Fehlermeldung("Falsche Taste")
lcd.setCursor(0, 2);
lcd.print(F("Nur [0,1,3,4,6,A,B]"));
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
{
if ((relais_byte&B00001000)==0) {Heizung_3WV=0;} else {Heizung_3WV=1;} // Heizung_3WV auf 1 bei 300l-, auf 0 bei 500l-Boiler
// Serial.print("Stellung des 3WV ist ermittelt: ");Serial.println(Heizung_3WV);
relais_vorschlag_byte=relais_byte; // sicherheitshalber
// Serial.print("gesetzt auf autopilot\n"); // gesetzt auf manuell
if (key=='D') // Reset des LCD und der Relaiskarte
{
lcd_reset();
lcd.clear();
lcd_ausgabe(); // vorläufig; hier detailliertere Infos ausgeben
zeit_lcd_ausgabe();
relais_byte_senden(relais_byte);
lcd.setCursor(4,2);
lcd.print(F("mem"));
lcd.print(freeRam());
}
// 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
if (TEMP_SENSOR[2]-TEMP_SENSOR[0]>0) // Temp des großen Boilers größer als die des Kleinen...
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,0); //... großen 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
}
if (TEMP_SENSOR[0]>45)
{
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,6,1); // ... kleinen Boiler als WW-Quelle auswählen
}
// Umwälzung der Boiler im Sommer
if ((relais_byte&B00010000)==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 ((relais_byte&B00010000)>0) // 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 ((relais_byte&B00000010)>0) // 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,3,0); // dazu das 3WV-Relais in den Ruhezustand schalten
// Serial.print("Sie haben # ausgewaehlt, Boiler wird geschlossen, das 3WV in Ruhezustand versetzt\n"); // Eine Meldung ausgeben
// temp_ausgabe_serial();
}
if (TEMP_SENSOR[(2-Heizung_3WV*2)]>max_B_temp) // falls die Temp des gerade befüllten 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
// 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]); // Eine Meldung ausgeben
// evtl. später eine entsprechende LCD-Meldung einbauen
// temp_ausgabe_serial();
}
if (TEMP_SENSOR[4]<(TEMP_SENSOR[2-2*Heizung_3WV]+2)) // Vorlauf um 2 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
// Serial.print("Aufheizung des Boilers wird gleich unterbrochen werden, da der Vorlauf inzwischen nicht mehr ausreicht!\n"); // Eine Meldung ausgeben
// evtl. später eine entsprechende LCD-Meldung einbauen
// temp_ausgabe_serial();
}
}
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,3,0); // auch das 3WV-Relais in den Ruhezustand = 500l-Richtung schalten
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,1); // ... die Befüllung des entsprechenden 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,3,1); // auch das 3WV-Relais in den Ruhezustand = 500l-Richtung schalten
relais_vorschlag_byte=relais_bit_setzen(relais_vorschlag_byte,1,1); // ... die Befüllung des entsprechenden 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
// Prüfung ob sich Daten geändert haben, wenn ja -> LCD neu zeichnen UND Byte setzen und schalten
if (relais_byte==relais_vorschlag_byte)
{
relais_byte_senden_o_delay(relais_byte); // ... und senden
// Serial.print("Keine Aenderng des Vorschlagsbytes eingetreten\n"); // gesetzt auf manuell, keine Änderung nötig
// temp_ausgabe_serial();
}
else // Relais_byte und Vorschlagsbyte sind ungleich
{
// Serial.print("Aenderng des Vorschlagsbytes eingetreten\n"); // gesetzt auf manuell
if (((relais_vorschlag_byte&B00000010)>0 && (relais_vorschlag_byte&B00010000))>0) // Kollision zwischen Auffüllen und Umwälzen abfangen, dann nichts machen!
{
relais_vorschlag_byte=relais_byte; // das manipulierte Vorschlag-Byte zurücksetzen auf den Ursprungswert
// Serial.print("Achtung: KOLLISION\n");
}
else
{ // wenn es aber keine Kollision gibt, Vorschlagsbyte als Relaisbyte übernehmen ...
// Serial.print("Keine KOLLISION\n");
relais_byte=relais_vorschlag_byte;
relais_byte_senden(relais_byte); // ... und senden
lcd_ausgabe();
}
} // Ende der Ausgabe
} // Ende der Verarbeitungsschleife, wenn key != 0
} // Ende der loop-Funktion
Abonnieren
Kommentare zum Post (Atom)
Keine Kommentare:
Kommentar veröffentlichen