Problem bei Programmablauf

Hallo Community,

ich habe ein Problem bei meinem Programm und komme im Moment nicht weiter. Zuerst einmal die nötigen Daten.

Hardware:
-Elegoo Mega2560
-8-Fach-Relais
-RTC DS3231

Sollzustand: (Kurz und bündig)
-Der Code soll jeden Tag zur gewünschten Uhrzeit ein Relay für eine gewisse Zeit (in ms), Ein-und dann Ausschalten.

Problem im Detail:
Hierbei soll es auch möglich sein die gewünschten Startzeiten jederzeit am Tag zu ändern und aufzuspielen. Wenn ich das Programm aktuell auf den 2560 lade stellt sich folgendes Problem heraus.
*1-->Liegt die gewünschte Schaltzeit beim hochladen in der Zukunft, tut der Code was er soll. Also Relay um 15:15 Uhr An und nach 10000ms wieder ausschalten, und auf den nächsten Tag warten.
*2-->Liegt die gewünschte Schaltzeit beim hochladen in der Vergangenheit, Schaltet das Relay direkt nach dem Hochladen ,,unkontrolliert´´ ein und nach 10000ms wieder aus.

Wie kann ich erreichen, das das Relais nur zur gewünschten Uhrzeit schalte. Egal ob der Strom ausfällt oder nicht. Bzw. man den Code wie bei *2 hochlädt und das genannte Problem auftritt?

Ich habe ein paar Überlegungen/Tests ausprobiert, verwurstele mich jedoch oft.
Vielen Dank falls mir jemand einen Tipp geben kann.
Gruß

//Commands
//###################################################################
int StartStunde = 15  ;//[ℤ]=Ganze Zahlen
int StartMinute = 15  ;//[ℤ]
//###################################################################
//
const int LAUFZEIT = 10000; //[ms]
//
unsigned long ampelMillis;
unsigned long ampelIntervall;
//
enum ZUSTAENDE {AN, AUS};
byte zustand = AN;
//
//RTC
#include <DS3231.h>
DS3231  rtc(SDA, SCL);
//SDA an PIN 20Mega
//SCL an PIN 21Mega
Time  t;
//
//RELAY
const byte Relaypin = 22;
//
void setup() {
  Serial.begin(9600);

  //RTC
  rtc.begin();
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(MONDAY);     // Set Day-of-Week to WEDNESDAY
  //rtc.setTime(14, 28, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(6, 12, 2021);   // Set the date to January 1st, 2014

  //RELAY
  pinMode(Relaypin, OUTPUT);

}//setup ende

void loop() {

  t = rtc.getTime();

  //Startbedingung Uhrzeit
  if (t.hour >= StartStunde && t.min >= StartMinute) {
    // Relayschaltung
    if (millis() - ampelMillis >= ampelIntervall) {
      switch (zustand) {
        case AN:
          digitalWrite(Relaypin, HIGH);
          zustand = AUS;
          ampelMillis = millis();
          ampelIntervall = LAUFZEIT;
          break;
        case AUS:
          digitalWrite(Relaypin, LOW);
          ampelMillis = millis();
          break;
      }
    }
  }

  Serial.print("RTC: ");
  Serial.print(t.hour, DEC);
  Serial.print(":");
  Serial.print(t.min, DEC);
  Serial.print(":");
  Serial.print(t.sec, DEC);

  Serial.print(" LAUFZEIT: ");
  Serial.print(LAUFZEIT);

  Serial.println(" ");
}

Hallo,
überprüfe beim Einschalten die Schaltzeit und wenn diese in der Vergangenheit liegt dann den Schaltvorgang ignorieren.
Ich wünsche einen geschmeidigen Abend und viel Spass beim Programmieren in C++.

Haste dem ja auch gesagt.

Mach den aus.

Hallo,
Du kannst Die Uhrzeit doch auf == abfragen und damit eventuell einen Statusmerker , falls das nötig ist , glaube ich aber im Moment nicht, setzen. Wenn Du dazu noch die sec mit auswertest müsste es perfekt sein.

wenn die Uhrzeit == der Einschaltzeit ist dann startzeit=millis()
wenn millis()-startzeit >=1000 ausschalten

sowas in der Art

Heinz

Nutzt ihm nichts, wenn nach dem Start zwangsweise immer der PIN AN ist. :wink:

Hallo,
@my_xy_projekt
ich denke bei einem Neustart " upload" ist die Bedingung erfüllt , darum schaltete das ein. Mit einer = abfrage wäre das nicht der Fall .

Gruß Heinz

Die Bedingung ist immer irgendwann erfüllt.
Allein die Logik hinter folgender Konstellation sollte hinterfragt werden:

Der Code schaltet genau einmal. Er kommt ja aus dem Zustand AUS niemehr raus.

Das wird so nichts. :wink:

Vielen Dank, die einfachen Lösungen sind meistens die Besten. Danke auch an alle anderen Member. Ich werde versuchen das ganze Umzusetzten.

Das ist schick - konnte ich gleich mal mitmachen...
Allerdings unbedingt in den Code für die DS-Lib reinschreiben, welche du verwendest.
Das wird sonst ein suchen....

Hier meine Ausgabe (Meine Uhr geht weit vor - ist aber beabsichtigt)

19:03:40.927 -> Start...
19:03:40.927 -> EinschaltZeit ist: 23:20 // Arduino eingeschaltet
19:04:24.656 -> Schalte ein              // Weckzeit erreicht!
19:04:24.656 -> StartZeit soll: 23:21    // Kontrollausgabe
19:04:24.656 -> StartZeit ist: 23:21
19:04:34.666 -> ausgeschaltet um: 23:21  // 10 Sekunden Laufzeit sind um

Mach was draus.

#include <DS3231.h>
DS3231  rtc(SDA, SCL);
Time  t;

const byte startZeit[] = {23, 21};
const int einschaltZeit = 10000; // Zeit in ms
bool isStart = false;

unsigned long ampelMillis;

const bool an = LOW, aus = HIGH;

const byte relayPin = 22;

byte secunde = 61;
void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  //RTC
  rtc.begin();
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(MONDAY);     // Set Day-of-Week to WEDNESDAY
  //rtc.setTime(14, 28, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(6, 12, 2021);   // Set the date to January 1st, 2014
  //RELAY
  digitalWrite(relayPin, aus);
  pinMode(relayPin, OUTPUT);
  t = rtc.getTime();
  Serial.print(F("EinschaltZeit ist: "));
  Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);
}

void loop()
{
  t = rtc.getTime();
  //Startbedingung Uhrzeit
  if (t.hour >= startZeit[0] &&
      t.min  >= startZeit[1])
  {
    if (!isStart)
    {
      Serial.println(F("Schalte ein"));
      Serial.print(F("StartZeit soll: "));
      Serial.print(startZeit[0]); Serial.print(':'); Serial.println(startZeit[1]);
      Serial.print(F("StartZeit ist: "));
      Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);
      digitalWrite(relayPin, an);
      isStart = true;
      ampelMillis = millis();
    }
  }
  else if (isStart) {isStart = false; Serial.println(F("StartMerker zurück gesetzt"));}
  if (millis() - ampelMillis >= einschaltZeit)
  {
    if (digitalRead(relayPin) == an)
    {
      digitalWrite(relayPin, aus);
      Serial.print(F("ausgeschaltet um: "));
      Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);
    }
  }
  /*
    Serial.print("RTC: ");
    Serial.print(t.hour, DEC);
    Serial.print(":");
    Serial.print(t.min, DEC);
    Serial.print(":");
    Serial.print(t.sec, DEC);
    Serial.println(" ");
  */
}

PS: Die Einschaltzeit ist die, wann der Arduino startet. Ich hab das nicht ganz so spät gemacht - und ja, ich bin auch lokal in einer anderen Zeitzone.

1 Like

Habe das Programm erweitert und eine Freigabefenster-Code hinzugefügt. Hierbei wird eine Relaisschaaltung während z.b. dem erneuten hochladen oder Stromlosschalten verhindert. Allerdings ist mir während der Testphase um ~Mitternacht folgendes im Seriellen Monitor aufgefallen.

RTC: 0:23:33  rtcTimeInMillis : 1412000 sTInMs : 1140000 freigabe : 0 
RTC: 0:23:33  rtcTimeInMillis : 1347464 sTInMs : 1140000 freigabe : 0 

Die Variable rtcTimeInMillis zeigt 4 Stellen vor dem Komma einen unglatten Wert an.
Die Schreibtischtestberechnung sollte jedoch für rtcTimeInMillis derart aussehen:

unsigned long rtcTimeInMillis =  ((t.hour * 3600000) + (t.min * 60000) + (t.sec * 1000)) 
=(23*60k)+(33*1000)
=1413000

= ((t.hour * 3600000) + (t.min * 60000) + (t.sec * 1000)) 
=(23*60k)+(34*1000)
=1414000  

--> Im Seriellen Monitor wird hierbei jedoch folgendes ausgegeben:
=1347464

-Reicht die kapazität der Variable nicht aus?
-Ich hörte einst von ~47 Tagen die man in millis abspeichern kann, somit muss der milliwert doch wesentlich höher sein?

Anhang: Code:

//RTC
#include <DS3231.h>
DS3231  rtc(SDA, SCL);
Time  t;
//RTC Belegung
//SDA --> Pin 20
//SCL --> Pin 21

const byte startZeit[] = {18, 14};//[Stunde, Minute]
const int einschaltZeit = 5000; // [ms]

bool isStart = false;
unsigned long ampelMillis;
const bool an = LOW, aus = HIGH;
const byte relayPin = 2;
byte secunde = 61;

unsigned long startTimeInMillis = ( startZeit[0] * 3600000) + (startZeit[1] * 60000);//Legt Startzeitpunkt als Milliwert fest
unsigned long rtcTimeInMillis;
bool freigabe = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println(F("Start..."));
  //RTC
  rtc.begin();
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(MONDAY);     // Set Day-of-Week to WEDNESDAY
  //rtc.setTime(14, 28, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(6, 12, 2021);   // Set the date to January 1st, 2014
  //RELAY
  digitalWrite(relayPin, aus);
  pinMode(relayPin, OUTPUT);
  t = rtc.getTime();
  Serial.print(F("EinschaltZeit ist: "));
  Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);
}

void loop() {

  //Freigabefenster öffnen
  rtcTimeInMillis = ((t.hour * 3600000) + (t.min * 60000) + (t.sec * 1000));//Überlauf bei sekunde 33 ???

  if (startTimeInMillis == rtcTimeInMillis ) {//Zwischen Startzeit und Laufzeitende wird freigabe für Relaisschaltung erteilt
    freigabe = 1;
  }
  if (rtcTimeInMillis >= (startTimeInMillis + einschaltZeit )) {
    freigabe = 0;
  }

  t = rtc.getTime();
  //Startbedingung Uhrzeit
  if (t.hour >= startZeit[0] &&
      t.min  >= startZeit[1])
  {
    if (freigabe == 1) {
      if (!isStart)
      {
        Serial.println(F("Schalte ein"));
        Serial.print(F("StartZeit soll: "));
        Serial.print(startZeit[0]); Serial.print(':'); Serial.println(startZeit[1]);
        Serial.print(F("StartZeit ist: "));
        Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);

        digitalWrite(relayPin, an);

        isStart = true;
        ampelMillis = millis();
      }
    }
    else if (isStart) {
      isStart = false;
      Serial.println(F("StartMerker zurück gesetzt"));
    }
    if (millis() - ampelMillis >= einschaltZeit)
    {
      if (digitalRead(relayPin) == an)
      {
        //
        digitalWrite(relayPin, aus);
        //
        Serial.print(F("ausgeschaltet um: "));
        Serial.print(t.hour); Serial.print(':'); Serial.println(t.min);
      }
    }
  }

  Serial.print("RTC: ");
  Serial.print(t.hour, DEC);
  Serial.print(":");
  Serial.print(t.min, DEC);
  Serial.print(":");
  Serial.print(t.sec, DEC);

  Serial.print(" ");

  Serial.print(" rtcTInMs : ");
  Serial.print(rtcTimeInMillis);

  Serial.print(" startTInMs : ");
  Serial.print(startTimeInMillis);

  Serial.print(" freigabe : ");
  Serial.print(freigabe);

  Serial.print(" V1.1 ");
  Serial.println(" ");
}

Menno!
Du weisst doch wie man Code hier einstellt. Und Du siehst doch nach der Veröffentlichung spätestens, das der Inhalt nicht mit deinem Wunsch übereinstimmt.

Mach das bitte richtig.
Und nein, es reicht nicht.

so sollte es passen. Danke

Der maximale rtcTimeInMillis Wert sollte somit um 23:59:59, bei 86 399 000 liegen.
Unsigned long variablen können doch 2 147 483 647 was deutlich größer ist.

Verstehe momentan nicht wieso ich die RTC Zeit so nicht in millisekunden umrechnen kann?

Kannst Du schon.
Dann las Dich mal überraschen:

23 Stunden = 23*60 minuten            // 1380
Dazu addiert: 58 Minuten = 23*60+58   // 1438
Macht in Sekunden: 
(23*60+58)*60                         // 86280

Code für den Seriellen Monitor:

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  Serial.println((23*60+58)*60);
}

void loop()
{}

Mach mal.

OK, habe die Problemstellung isoliert und herausgefunden wo der Fehler liegt.
Kann mir jedoch nicht genau erklären was an der Berechnung von "rtcTimeInMillis" und "zzz" den fehler hervorruft. Mathematisch sind beide Berechnungen konsistent.

Wenn ich die Formel:

  unsigned long  rtcTimeInMillis = ((((t.hour * 60 + t.min) * 60) + t.sec) * 1000); //zeit auf millisekunde

in den TR eingebe bekomme ich bei 0:0:33 Uhr den korrekten Wert 33000 heraus.

Seriell wird jedoch:

RTC: 0:0:32 xxx: 0 yyy: 32 zzz: 32000 rtcTimeInMillis: 32000 
RTC: 0:0:32 xxx: 0 yyy: 32 zzz: 32000 rtcTimeInMillis: 32000 
RTC: 0:0:32 xxx: 0 yyy: 32 zzz: 32000 rtcTimeInMillis: 32000 
RTC: 0:0:33 xxx: 0 yyy: 33 zzz: 33000 rtcTimeInMillis: 4294934760 
RTC: 0:0:33 xxx: 0 yyy: 33 zzz: 33000 rtcTimeInMillis: 4294934760 
RTC: 0:0:33 xxx: 0 yyy: 33 zzz: 33000 rtcTimeInMillis: 4294934760 

4294934760 ausgespuckt.

Oder stehe ich auf dem Schlauch? Solange der Code funktioniert =)

CODE Komplett:

//RTC
#include <DS3231.h>
DS3231  rtc(SDA, SCL);
Time  t;
//RTC Belegung
//SDA --> Pin 20
//SCL --> Pin 21

void setup() {
  Serial.begin(9600);

  //RTC
  rtc.begin();
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(MONDAY);     // Set Day-of-Week to WEDNESDAY
  rtc.setTime(23, 58, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(6, 12, 2021);   // Set the date to January 1st, 2014

}

void loop() {
  t = rtc.getTime();
  unsigned long xxx = (t.hour * 60 + t.min) * 60; //zeit in sekunden
  unsigned long yyy = xxx + t.sec;  //zeit in sekunden +RTC sekunden
  unsigned long zzz = yyy * 1000; //umrechnen auf millisekunden

  //rtcTimeInMillis = ((t.hour * 3600000) + (t.min * 60000) + (t.sec * 1000));//Überlauf bei sekunde 33 ???
  //rtcTimeInMillis = (((t.hour * 60 + t.min) * 60) + t.sec); //zeit auf sekunde
  //unsigned long  rtcTimeInMillis = ((((t.hour * 60 + t.min) * 60) + t.sec) * 1000); //zeit auf millisekunde

  Serial.print("RTC: ");
  Serial.print(t.hour, DEC);
  Serial.print(":");
  Serial.print(t.min, DEC);
  Serial.print(":");
  Serial.print(t.sec, DEC);

  Serial.print(" xxx: ");
  Serial.print(xxx);

  Serial.print(" yyy: ");
  Serial.print(yyy);

  Serial.print(" zzz: ");
  Serial.print(zzz);

  Serial.print(" rtcTimeInMillis: ");
  Serial.print(rtcTimeInMillis);

  Serial.println(" ");
}

Soweit erstmal vielen Dank für den Support!

Nein.
Nochmal. Hast Du den Code von mir oben ausgeführt?
Was ist deine Ausgabe?
Und hast Du dazu eine Erklärung?
Ich bleib gerne noch bei - Das bekommen wir in 3 Post/replys nachhaltig hin...

Habe es getestet, und momentan keine Erklärung dazu.
Ich vermute irgend einen Syntaxfehler ??? Ich weiß es nicht genau.
Der Wert ist nicht korrekt. Im TR bekomme ich bei gleicher Klammersetzung 86280 raus.
Der Monitorwert jedoch 20744.
Die Rechnung sollte Mathematisch korrekt sein. Bitte klär mich auf =)

//BESCHREIBUNG: Code liefert die RTC Zeit in Millisekunden. Jedoch nur auf 1000er Stellen genau! Und wird nur 1/s aktualisiert.

//RTC
#include <DS3231.h>
DS3231  rtc(SDA, SCL);
Time  t;
//RTC Belegung
//SDA --> Pin 20
//SCL --> Pin 21

void setup() {
  Serial.begin(9600);

  //RTC
  rtc.begin();
  // The following lines can be uncommented to set the date and time
  //rtc.setDOW(MONDAY);     // Set Day-of-Week to WEDNESDAY
  rtc.setTime(23, 58, 0);     // Set the time to 12:00:00 (24hr format)
  //rtc.setDate(6, 12, 2021);   // Set the date to January 1st, 2014

  Serial.println(F("Start..."));
  Serial.println((23 * 60 + 58) * 60);

  Serial.println(23 * 60) ;
  Serial.println((23 * 60) + 58) ;
  Serial.println(((23 * 60) + 58) * 60) ;
}

void loop() {
  t = rtc.getTime();
  unsigned long xxx = (t.hour * 60 + t.min) * 60; //zeit in sekunden
  unsigned long yyy = xxx + t.sec;  //zeit in sekunden +RTC sekunden
  unsigned long zzz = yyy * 1000; //umrechnen auf millisekunden

  //rtcTimeInMillis = ((t.hour * 3600000) + (t.min * 60000) + (t.sec * 1000));//Überlauf bei sekunde 33 ???
  //rtcTimeInMillis = (((t.hour * 60 + t.min) * 60) + t.sec); //zeit auf sekunde
  unsigned long  rtcTimeInMillis = ((((t.hour * 60 + t.min) * 60) + t.sec) * 1000); //Zeit auf millisekunde

/*
  Serial.print("RTC: ");
  Serial.print(t.hour, DEC);
  Serial.print(":");
  Serial.print(t.min, DEC);
  Serial.print(":");
  Serial.print(t.sec, DEC);

  Serial.print(" xxx: ");
  Serial.print(xxx);

  Serial.print(" yyy: ");
  Serial.print(yyy);

  Serial.print(" zzz: ");
  Serial.print(zzz);

  Serial.print(" rtcTimeInMillis: ");
  Serial.print(rtcTimeInMillis);
*/

  Serial.print("   ((23 * 60) + 58) * 60= ") ;
  Serial.print(((23 * 60) + 58) * 60) ;

  Serial.println(" ");
}

AUSGABE:

((23 * 60) + 58) * 60= 20744 

Dann helf ich mal.
ALSO: Wenn Du rechnest und nicht angibst, mit was Du rechnen willst, wird mit INT gerechnet.
Damit kommt es zum Überlauf.
Kannste nachrechen: 86280-20744=65536 -> genau das, was in ein unsigned int passt :slight_smile:

Wenn Du sicher gehen willst, auch mit größeren Ergebnissen rumzukommen, musste das angeben.
Mein Code von oben mit dem Zwang, das jetzt Unsigned long gerechnet wird:

void setup()
{
  Serial.begin(115200);
  Serial.println(F("Start..."));
  Serial.println((23*60+58)*60UL);
}

void loop()
{}

Auf beiden Seiten des = bei Dir musst Du mit der selben Größe rechnen:

  unsigned long  rtcTimeInMillis = ((((t.hour * 60 + t.min) * 60) + t.sec) * 1000); //zeit auf millisekunde

dann musst rechts auch festlegen, das das Ergebnis unsigned long sein kann.

Das sagt Dir der Compiler aber auch:

/tmp/arduino_modified_sketch_818170/sketch_jan02f.ino: In function 'void setup()':
/tmp/arduino_modified_sketch_818170/sketch_jan02f.ino:5:28: warning: integer overflow in expression [-Woverflow]
   Serial.println((23*60+58)*60);
                  ~~~~~~~~~~^~~
Compiling libraries...

Ich hoffe jetzt wirds klarer :wink:

Links

1 Like

Achsooo ist das. Ich habe immer angenommen der Datentyp der beschriebenen Variable gibt sowiso vor wie gerechnet werden muss.
Somit macht das absolut sinn. Scheinbar ist es auch egal wo im Term man "UL" angiebt.
Jetzt wird mir einiges klar. Hatte schon bei ähnlichen Codes Probleme aus dieser Richtung.
Jetzt wäre das wohl erledigt. Vielen Dank nochmal für die Hilfe!!!

Jetzt weis ich auch das es eine Fehlerauslese in der Arduino IDE gibt.

CODE:

  Serial.print("   ((23 * 60) + 58) * 60UL= ") ;
  Serial.print(((23 * 60) + 58) * 60UL) ;

  Serial.print("   ((23UL * 60) + 58) * 60= ") ;
  Serial.print(((23UL * 60) + 58) * 60) ;

  Serial.print("   ((23 * 60) + 58UL) * 60= ") ;
  Serial.print(((23 * 60) + 58UL) * 60) ;

AUSGABE:

((23 * 60) + 58) * 60UL= 86280   
((23 * 60) + 58) * 60UL= 86280   
((23 * 60) + 58) * 60UL= 86280

:slight_smile:

Ja, das ist manchmal sehr erhellend, mal durchzuscrollen, was der Compiler alles so ausgibt - wenn es den unter DATEI - Voreinstellungen auch richtig und vollständig eingerichtet ist. Auch wenn man erstmal nichts fehlerhaftes sieht, sind manche Warnungen nicht umsonst.
Na dann,..

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.