Schaltzeiten Mit Taster Wechseln

Hallo Zusammen,

Ich stehe vor unten stehender Herausforderung.

Aber erst mal zur Hardware. Aufgebaut sind ein Arduino Mega 2560 R3, ein I2C LCD 2004 20x4, ein Relais an bin 8, eine RTC 3231 und ein Taster an Pin 4.

Es läuft JursEasyRTC Sketch mit LDC ausgabe.

Nun zu (meiner) Herausforderung:

Per Tastendruck möchte ich zwischen 3 gesetzten Schaltzeiten wechseln können und mir übers LCD den Modus anzeigen lassen. In etwa so:

// Modus 0 = Licht aus  
  if (lichtmodus == 0)
  {
    {8},  // Pin, Einschaltzeit, Ausschaltzeit (keine Zeiten)
    lcd.setCursor(0,1);
    lcd.println("Licht aus");    
  }
   
  // Modus 1 = Kurz  
  else if (lichtmodus == 1)
  {
    {8, 1800, 2000},  // Pin, Einschaltzeit, Ausschaltzeit (18:00 - 20:00)
    lcd.setCursor(0,1);
    lcd.println("Kurz");    
  }
   
  // Modus 2 = Lang 
  else if (lichtmodus == 2)
  {
    {8, 600, 2000},  // Pin, Einschaltzeit, Ausschaltzeit (06:00 - 20:00)
    lcd.setCursor(0,1);
    lcd.println("Lang");
  }

Verschiedene Ansätze und Googlen brachten mich nicht weiter. Darum ersuche ich um Euren Rat.
Ich bedanke mich schon mal für Eure Zeit.

Im Anhang der Zeitschaltsketch:

Zeitschaltuhr.ino (8.92 KB)

Wieso 2 Treads?
Bitte lauffähigen und fehlerfrei kompilierbaren Sketch.

So habe ich mir das vorgestellt.
er ist aber nicht fehlerfrei kompilierbar, da ich ihn mit der RTC nicht zusammen bringe.

// Konstanten
const int tasterPin = 4;     // Taster an Pin 4 angeschlossen
const int RelaisPin = 10;       // Relais an Pin 8 angeschlossen

 
// Variablen
int lichtmodus = 0;          // Variable für die verschiedenen festgelegten Farben
int tasterStatus = LOW;      // Variable zu speichern des Tasterstatus
 
 
void setup()
{
  pinMode(tasterPin, INPUT);      // Setzt den TasterPin als Eingang 
  pinMode(RelaisPin, OUTPUT);        // Setzt den RelaisPin als Ausgang
 } 
 
 
void loop()
{ 
  // Abfrage ob der Taster gedrückt ist
  tasterStatus = digitalRead(tasterPin);
   
  // Wenn Taster gedrückt ist...
  if (tasterStatus == HIGH)
  {
    lichtmodus++;     // Lichtmodus +1
    delay(300);       // 300ms warten
  }
   
  //+++++++++++++++ LEUCHTPROGRAMME +++++++++++++++++
   
  // Modus 0 = Licht aus  
  if (lichtmodus == 0)
  {
    {8},  // Pin, Einschaltzeit, Ausschaltzeit (keine Zeiten)
    lcd.setCursor(0,1);
    lcd.println("Licht aus");    
  }
   
  // Modus 1 = Kurz  
  else if (lichtmodus == 1)
  {
    {8, 1800, 2000}  // Pin, Einschaltzeit, Ausschaltzeit (18:00 - 20:00)
    lcd.setCursor(0,1);
    lcd.println("Kurz");    
  }
   
  // Modus 2 = Lang 
  else if (lichtmodus == 2)
  {
    {8, 600, 2000}  // Pin, Einschaltzeit, Ausschaltzeit (06:00 - 20:00)
    lcd.setCursor(0,1);
    lcd.println("Lang");
  }
 
 
  // Anzahl der Leutmodi auf 3 begrenzen. (0 bis 2)
  else
  {
    lichtmodus = 0;
  }
}

Oha - war gerade schon erstaunt, daß bei Post-Count 1 nur ein gesperrter Thread vorhanden ist :slight_smile:

@TO
Worin besteht nun Dein Problem?
Allgemein habe ich keine Lust, Deinen Sketch zu analysieren und Dir Deine Fehler vorzubeten.
Was mir aber auffällt: Wo Du wohnst, gibt's negative Uhrzeiten und so Kram - warum solltest Du sonst z.B. für Stunden (hier bei mir im Bereich 0...23) int, was Dir den Bereich -32768...32768 beschert?
Entweder verdammt kurze Stunden, oder verdammt lange Tage - wobei es bei Denen ja nicht anders aussieht.
Bitte korrekte(re) Datantypen benutzen.

Du liest die serielle Schnittstelle blockierend ein - das delay() hat Da Nichts verloren.
Du musst immer nur Das einlesen, was gerade greifbar ist und erst auswerten, wenn das Eingelesene vollständig ist.

Bei ersten Drüberfliegen wäre mir viel viel Code aufgefallen, Den ich irgendwie nicht gebraucht hätte - glaube ich zumindest.
Ein Problem sprang mich dabei nicht direkt an.

MfG

Edit
Was sollen die geschweiften Klammern bitte in dem Fetzen?
Den Mist hattest Du oben schon drin - es darf 'zur Not' auch etwas sein, was zumindest die Chance hat, kompiliert werden zu können!

Auch möchtest Du dringend auf die Flanke reagieren und nicht den Sketch mit delay() zur Standuhr machen.

da ich ihn mit der RTC nicht zusammen bringe.

Was genau klemmt?

Wenn Du sowohl Schaltzeiten wie die Aktuelle Zeit in Sekunden umrechnest dann ist der Vergleich einfacher.
Grüße Uwe

Der Code hat zu viele Zeichen darum muss ich ihn auf zwei mal einfügen.
Insgesamt könnte es so funktionieren. Das heißt, es funktioniert, wenn der Taster mit den Schaltzeiten nicht wäre. Beim einbauen des Tasters klemmt es...

#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define RTC_I2C_ADDRESS 0x68 // I2C Adresse der RTC ist 0x68 für DS1307 und DS3231
LiquidCrystal_I2C lcd(0x27,20,4); // I2C Adresse des 20x4 LCD

#define EIN HIGH  // Active-High
#define AUS LOW   // Active-High

// Konstanten
const int tasterPin = 4;     // Taster an Pin 4 angeschlossen
const int RelaisPin = 10;       // Relais an Pin 8 angeschlossen

// Variablen
int lichtmodus = 0;          // Variable für die verschiedenen festgelegten Farben
int tasterStatus = LOW;      // Variable zu speichern des Tasterstatus
 

char tm[15];  // für Zeit auf LCD
char wtag[4]; // für Wochentag auf LCD

/*****************Lampen-Schaltplan*******************/
struct schaltRelais_t{
  byte pin;
  int ein1; int aus1;
};

// Hier die Relais-Pins definieren mit Ein- und Ausschaltzeiten
schaltRelais_t schaltRelais[2]={
  {8, 600, 2100},  // Pin, Einschaltzeit, Ausschaltzeit (08:00 - 19:00)  //keine führenden Nullen!

};
/*****************END - Lampen-Schaltplan*************/


/*****************Beregnungs-Timer***************************/
struct timerRelais_t{
  byte pin;
  byte dauer;
  int timer1;
  int timer2;
};

timerRelais_t timerRelais={6, 25, 930, 1400}; // Timer an Pin-6 für 25 Sekunden um 09:30 und 14:00 //keine führenden Nullen!
/*****************END - Beregnungs-Timer*********************/


void relaisTimerNachZeit(int thishour, int thisminute)
{
  int thisTime= thishour*100+thisminute;
  if (thisTime==timerRelais.timer1 || thisTime==timerRelais.timer2)
  {
    Serial.println("Timer Start");
    digitalWrite(timerRelais.pin,EIN);
    lcd.clear();
    lcd.setCursor ( 2, 1 );
    lcd.print("TIMER AKTIV!");      //Ausgabe auf LCD vor delay()
    delay(timerRelais.dauer*1000L);
    lcd.clear();
    digitalWrite(timerRelais.pin,AUS);
    Serial.println("Timer Stopp");
  }
}


void relaisSchaltenNachZeit(int thishour, int thisminute)   
// Schaltet die Zeitschaltuhr ein und aus und setzt den Ausgang entsprechend
{
  boolean state;
  // Aus der aktuellen Zeit eine Schaltzeit bilden
  int thisTime= thishour*100+thisminute;
  // Alle Schaltzeiten durchgehen, falls eine davon EIN sagt, einschalten
  for (int i=0;i<sizeof(schaltRelais)/sizeof(schaltRelais_t);i++)
  {
    state=AUS;  // Amnahme: Es sei nichts geschaltet
    if (
       (thisTime>=schaltRelais[i].ein1 && thisTime<schaltRelais[i].aus1)
       )
    {   
        state=EIN;
    }
    if (digitalRead(schaltRelais[i].pin)!=state) // Falls geschaltet werden soll
    { // ein paar Debug-Ausgaben machen
      Serial.print("Relais ");
      Serial.print(i+1);  // Relais-Index zählt ab 0, einfach 1 dazuzählen
      Serial.print(": ");
      if (state==EIN) Serial.println("EIN"); else Serial.println("AUS");
    }
    digitalWrite(schaltRelais[i].pin, state); // Schaltzustand setzen
  }
} 

/*****************Display RTC auf LCD************************/
void updateDisplay(int &wochentag, int &stunden, int &minuten, int &sekunden)
{

/*****************Wochentag - Zahl zu Wort*******************/
switch (wochentag) {
    case 1:
      strcpy(wtag, "Mo");
      break;
    case 2:
      strcpy(wtag, "Di");
      break;
    case 3:
      strcpy(wtag, "Mi");
      break;
    case 4:
      strcpy(wtag, "Do");
      break;
    case 5:
      strcpy(wtag, "Fr");
      break;
    case 6:
      strcpy(wtag, "Sa");
      break;
    case 7:
      strcpy(wtag, "So");
      break;
  }
/*****************END - Wochentag - Zahl zu Wort*******************/
  
sprintf(tm, "%02d:%02d:%02d",stunden, minuten, sekunden);

lcd.setCursor ( 8, 0 ); // Anzeige Zeile 1 rechts
lcd.print(wtag);
lcd.print(", ");
lcd.print(tm);

}
/*****************END - Display RTC auf LCD************************/


int jahre,monate,tage,wochentag,stunden,minuten,sekunden;
// wochentag bleibt in diesem Test-Sketch berücksichtigt

void rtcReadTime(int &jahre, int &monate, int &tage, int &wochentag, int &stunden, int &minuten, int &sekunden)
// aktuelle Zeit aus RTC auslesen
{
// Reset the register pointer
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();
  Wire.requestFrom(RTC_I2C_ADDRESS, 7);
  // A few of these need masks because certain bits are control bits
  sekunden    = bcdToDec(Wire.read() & 0x7f);
  minuten     = bcdToDec(Wire.read());
  stunden     = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  wochentag   = bcdToDec(Wire.read());         // Wochentag berücksichtigt!
  tage        = bcdToDec(Wire.read());
  monate      = bcdToDec(Wire.read());
  jahre       = bcdToDec(Wire.read())+2000; 
}

void rtcWriteTime(int jahre, int monate, int tage, int wochentag, int stunden, int minuten, int sekunden)
// aktuelle Zeit in der RTC speichern
{
  Wire.beginTransmission(RTC_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(sekunden));    // 0 to bit 7 starts the clock
  Wire.write(decToBcd(minuten));
  Wire.write(decToBcd(stunden));      // If you want 12 hour am/pm you need to set bit 6 (also need to change readDateDs1307)
                                 
  Wire.write(decToBcd(wochentag)); // Wochentag berücksichtigt!
  Wire.write(decToBcd(tage));
  Wire.write(decToBcd(monate));
  Wire.write(decToBcd(jahre-2000));
  Wire.endTransmission(); 
}

byte decToBcd(byte val) // Hilfsfunktion zum Lesen/Schreiben der RTC
// Convert decimal number to binary coded decimal
// Hilfsfunktion für die Echtzeituhr
{
  return ( (val/10*16) + (val%10) );

Es muss sich keiner die Mühe machen mir alles vorzukauen. Das erwarte ich auch nicht. Ich brauche einen Denkanstoß, oder es fällt jemanden ein ähnlicher Sktech ein. Online habe ich nichts verwertbares gefunden.

}

byte bcdToDec(byte val)  // Hilfsfunktion zum Lesen/Schreiben der RTC
// Convert binary coded decimal to decimal number
// Hilfsfunktion für die Echtzeituhr
{
  return ( (val/16*10) + (val%16) );
}


int getIntFromString (char *stringWithInt, byte num)
// input: pointer to a char array
// returns an integer number from the string (positive numbers only!)
// num=1, returns 1st number from the string
// num=2, returns 2nd number from the string, and so on
{
  char *tail;
  while (num>0)
  {
    num--;
    // skip non-digits
    while ((!isdigit (*stringWithInt))&&(*stringWithInt!=0)) stringWithInt++;
    tail=stringWithInt;
    // find digits
    while ((isdigit(*tail))&&(*tail!=0)) tail++;
    if (num>0) stringWithInt=tail; // new search string is the string after that number
  } 
  return(strtol(stringWithInt, &tail, 10));
} 


void setup()
{
  lcd.begin();
  lcd.backlight(); 
  Wire.begin();       // initialisiert die Wire-Library
  Serial.begin(9600); // Serielle Kommunikation starten
  Serial.println("set 31.10.2016 23:59:00 7 (Montag=1, Sonntag=7)\r\n");
  for (int i=0;i<sizeof(schaltRelais)/sizeof(schaltRelais_t);i++)
  {
    digitalWrite(schaltRelais[i].pin,AUS);
    pinMode(schaltRelais[i].pin,OUTPUT);
  } 
    digitalWrite(timerRelais.pin,AUS);
    pinMode(timerRelais.pin,OUTPUT);
  {
    pinMode(tasterPin, INPUT);      // Setzt den TasterPin als Eingang 
    pinMode(RelaisPin, OUTPUT);        // Setzt den RelaisPin als Ausgang
  }
}


void behandleSerielleBefehle()
{
  char linebuf[30];
  byte counter;
  if (Serial.available())
  {
    delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor
    memset(linebuf,0,sizeof(linebuf)); // Zeilenpuffer löschen
    counter=0; // Zähler auf Null
    while (Serial.available())
    {
      linebuf[counter]=Serial.read(); // Zeichen in den Zeilenpuffer einfügen
      if (counter<sizeof(linebuf)-1) counter++; // Zeichenzähler erhöhen
    }
    // Ab hier ist die Zeile eingelesen
    if (strstr(linebuf,"set")==linebuf) // Prüfe auf Befehl "set" zum Setzen der Zeit
    { // Alle übermittelten Zahlen im String auslesen
      tage=getIntFromString (linebuf,1);
      monate=getIntFromString (linebuf,2);
      jahre=getIntFromString (linebuf,3);
      stunden=getIntFromString (linebuf,4);
      minuten=getIntFromString (linebuf,5);
      sekunden=getIntFromString (linebuf,6);
      wochentag=getIntFromString (linebuf,7);
    }
    else
    {
      Serial.println("Befehl unbekannt.");
      return;
    }
    // Ausgelesene Werte einer groben Plausibilitätsprüfung unterziehen:
    if (wochentag<1 || wochentag>7 || jahre<2000 || monate<1 || monate>12 || tage<1 || tage>31 || (stunden+minuten)==0)
    {
      Serial.println(linebuf);
      Serial.println("\r\nFehlerhafte Zeitangabe im 'set' Befehl");
      Serial.println("\r\nBeispiel: ");
      Serial.println("set 31.10.2016 23:59:00 7 (Montag=1, Sonntag=7)\r\n");
      return;
    }
    rtcWriteTime(jahre, monate, tage, wochentag, stunden, minuten, sekunden);
    Serial.println("Zeit und Datum wurden auf neue Werte gesetzt.");
  }
}


void loop()
{
  char buffer[30];
  static unsigned long lastMillis;
  static int lastMinute;
  int stunden, minuten, sekunden, dummy;
  if (millis()-lastMillis>1000) // nur einmal pro Sekunde
  {
    lastMillis=millis();
    rtcReadTime(dummy, dummy, dummy, wochentag, stunden, minuten, sekunden);
    updateDisplay(wochentag, stunden, minuten, sekunden); // Funktion zur Anzeige auf LCD
    if (minuten!=lastMinute) // die aktuelle Minute hat gewechselt
    {
      lastMinute=minuten;
      snprintf(buffer,sizeof(buffer),"%02d:%02d Uhr",stunden,minuten);
      Serial.println(buffer);
      relaisSchaltenNachZeit(stunden,minuten);
      relaisTimerNachZeit(stunden,minuten);
    }
  }
  behandleSerielleBefehle();
}

Hallo,

Per Tastendruck möchte ich zwischen 3 gesetzten Schaltzeiten wechseln können und mir übers LCD den Modus anzeigen lassen.

Dafür musst du einen Taster entprellen und als nicht nachtriggerbaren Monoflop programmieren. Dann kannste pro Tastendruck um genau eins weiterzählen.

Das hier solltest du auch nochmal überdenken. Die Serielle fragt man permanent ohne delay im loop Umlauf ab. Wenn ein Zeichen vorhanden ist liest man es in einen Buffer ein. Wenn keins vorhanden ist ist auch gut. Wenn das Endezeichen erkannt wird schließt man den Buffer ab und verarbeitet den Inhalt. Von Serenifly u.a. gibts dazu gefühlt hunderte Beispiele im Forum. Okay, ist nicht immer einfach zu finden. Grundregel ist, die loop darf nicht künstlich blockiert werden.

void behandleSerielleBefehle()
{
  char linebuf[30];
  byte counter;
  if (Serial.available())
  {
    delay(100); // Warte auf das Eintreffen aller Zeichen vom seriellen Monitor
    memset(linebuf, 0, sizeof(linebuf)); // Zeilenpuffer löschen
    counter = 0; // Zähler auf Null
    while (Serial.available())
    {
      linebuf[counter] = Serial.read(); // Zeichen in den Zeilenpuffer einfügen
   ...