Output timing is not consistent

I'm stumped, this sketch works almost exactly as it should except for on thing. Both Countdown Timers do what I want however the portion of the sketch that turns the Output off (for now an LED) can be anywhere from a quick blink to about 8 to 9 seconds on, even though I've set the output to stay on for 10 seconds. The portion of code I'm referring to is the ever so typical delay without using delay code.

unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= (Interval * 1000)) {
    (previousMillis = currentMillis);
    digitalWrite(Output, LOW);
  }

I've been working on this for days trying everything I can think of including using the integer seconds but nothing works as expected.

#include <RTClib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

RTC_DS3231 rtc;

//OLED   define
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels \
                          // Declaration for an SSD1306 display connected   to I2C (SDA, SCL pins) \
                          //Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

unsigned int T1Hours = 1;
unsigned int T1Minutes = 0;
unsigned int T1Seconds = 0;
unsigned int T1CurrentSeconds = 0;
unsigned int T1PreviousSeconds = 0;

unsigned int T2Hours = 0;
unsigned int T2Minutes = 0;
unsigned int T2Seconds = 15;
unsigned int T2CurrentSeconds = 0;
unsigned int T2PreviousSeconds = 0;

int Interval = 10;

unsigned long T1TotalSeconds = 0;
int T1LeftOverMinutes = 0;
int T1LeftOverSeconds = 0;
unsigned long T1CountDownSeconds = (T1Hours * 3600) + (T1Minutes * 60) + (T1Seconds);

unsigned long T2TotalSeconds = 0;
int T2LeftOverMinutes = 0;
int T2LeftOverSeconds = 0;
unsigned long T2CountDownSeconds = (T2Hours * 3600) + (T2Minutes * 60) + (T2Seconds);
unsigned long T2ResetDownSeconds = (T2Hours * 3600) + (T2Minutes * 60) + (T2Seconds);

int T1Count = 0;
int T2Count = 0;

const int Output = 4;

const int ButtonPin_1 = 2;
const int ButtonPin_2 = 3;
int ButtonState_1 = 0;
int ButtonState_2 = 0;

unsigned long previousMillis = 0;

void setup() {
  pinMode(ButtonPin_1, INPUT);
  pinMode(ButtonPin_2, INPUT);
  pinMode(Output, OUTPUT);

#define OLED_RESET -1  // Reset pin # (or -1 if sharing Arduino reset pin)

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, OLED_RESET)) {  // Address 0x3D for 128x64
    //Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }

  //  Serial.begin(9600);
  //while (!Serial)
  ;  // wait for serial
  delay(200);
  //Serial.println("DS3231RTC Read Test");
  //Serial.println("-------------------");

  display.display();  //Display logo
  delay(1000);
  display.clearDisplay();
  display.display();

  // digitalWrite(Output, HIGH);
  // delay(10000);
  //  digitalWrite(Output, LOW);
}
void loop() {

  ButtonState_1 = digitalRead(ButtonPin_1);
  ButtonState_2 = digitalRead(ButtonPin_2);

  if (ButtonState_1 == 0) {
    T1Count = 0;
    T2Count = 0;
  }
  if (ButtonState_2 == 0) {
    T1Count = 1;
    T2Count = 1;
  }

  display.clearDisplay();
  tmElements_t tm;
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(18, 1);

  if (RTC.read(tm)) {
    //Serial.print("Ok, Time = ");
    print2digits(tm.Hour);
    //    Serial.write(':');
    display.print(":");
    print2digits(tm.Minute);
    display.print(":");
    //    Serial.write(':');
    print2digits(tm.Second);
    //Serial.print(", Date (M/D/Y) = ");

    display.setCursor(18, 20);
    if (T1Hours < 10) { display.print(0); }
    //Serial.print(Hours);
    display.print(T1Hours);
    //    Serial.write(':');
    display.print(":");

    if (T1Minutes < 10) { display.print(0); }
    //Serial.print(Minutes);
    display.print(T1Minutes);
    //    Serial.write(':');
    display.print(":");

    if (T1Seconds < 10) { display.print(0); }
    //Serial.print(Seconds);
    display.print(T1Seconds);
    //Serial.println();

    display.setCursor(18, 40);
    if (T2Hours < 10) { display.print(0); }
    //Serial.print(T2Hours);
    display.print(T2Hours);
    //    Serial.write(':');
    display.print(":");

    if (T2Minutes < 10) { display.print(0); }
    //Serial.print(T2Minutes);
    display.print(T2Minutes);
    //    Serial.write(':');
    display.print(":");

    if (T2Seconds < 10) { display.print(0); }
    //Serial.print(T2Seconds);
    display.print(T2Seconds);
    //Serial.println();
    display.display();

  } else {
    if (RTC.chipPresent()) {
      //Serial.println("The DS3231 is stopped.  Please run the SetTime");
      //Serial.println("example to initialize the time and begin running.");
      //Serial.println();
    } else {
      //Serial.println("DS3231 read error!  Please check the circuitry.");
      //Serial.println();
    }
  }

  T1TotalSeconds = ((tm.Hour * 3600) + (tm.Minute * 60) + (tm.Second));

  T1CurrentSeconds = T1TotalSeconds;

  if (T1CurrentSeconds > T1PreviousSeconds) {
    T1PreviousSeconds = T1CurrentSeconds;
    T1CountDownSeconds = T1CountDownSeconds - T1Count;
    T1Hours = T1CountDownSeconds / 3600;  //next few lines are for displaying the timer count down
    T1LeftOverMinutes = T1CountDownSeconds - (T1Hours * 3600);
    T1Minutes = T1LeftOverMinutes / 60;
    T1LeftOverSeconds = T1LeftOverMinutes - (T1Minutes * 60);
    T1Seconds = T1LeftOverSeconds;  //up to here
  }

  if (T1CountDownSeconds == 0) {
    T1Count = 0;
  }

  T2TotalSeconds = ((tm.Hour * 3600) + (tm.Minute * 60) + (tm.Second));

  T2CurrentSeconds = T2TotalSeconds;

  if (T2CurrentSeconds > T2PreviousSeconds) {
    T2PreviousSeconds = T2CurrentSeconds;
    T2CountDownSeconds = T2CountDownSeconds - T2Count;  //next few lines are for displaying the timer2 count down
    T2Hours = T2CountDownSeconds / 3600;
    T2LeftOverMinutes = T2CountDownSeconds - (T2Hours * 3600);
    T2Minutes = T2LeftOverMinutes / 60;
    T2LeftOverSeconds = T2LeftOverMinutes - (T2Minutes * 60);
    T2Seconds = T2LeftOverSeconds;  //up to here
  }

  if (T2CountDownSeconds <= 0) {
    digitalWrite(Output, HIGH);
    T2CountDownSeconds = T2ResetDownSeconds;
    T2Count = 1;
  }

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= (Interval * 1000)) {
    (previousMillis = currentMillis);
    digitalWrite(Output, LOW);
  }
}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    //    Serial.write('0');
    display.print("0");
  }
  //Serial.print(number);
  display.print(number);
}

Any help would be much appreciated.

Thank

Just a thought after a quick read thru...

Change interval from int to unsigned long

  • First off:

  if (currentMillis - previousMillis >= (Interval * 1000)) {
    (previousMillis = currentMillis);
    digitalWrite(Output, LOW);
  }

  • Why the ( )

  if (currentMillis - previousMillis >= Interval * 1000ul) {
    previousMillis = currentMillis;
    digitalWrite(Output, LOW);
  }

  • When you turn on the output, did you want to reset previousMillis :thinking:

First off I want to thank all of you, the problem has been resolved even considering I didn't post the version with code tags. To Larry D, I missed those un needed brackets.

To van_der_decken, your suggestion solved it and I may even understand why. I am going to go over the code till I do.

You all have been great help to one of the worst coders on the planet.

Thanks

  • Well, if we think about this, we should reset the TIMER at the point the LED is turned ON.

  • Also, by default, multiplication is done as integer, tell compiler to use ul.
    Interval * 1000ul

1 Like
  • Highly suggest you use the following format with millis() based TIMERs like yours.
#define ENABLED      true
#define DISABLED     false

. . .

bool timerFlag   =  DISABLED;

. . .

  if (T2CountDownSeconds <= 0) 
    {
    digitalWrite(Output, HIGH);

    //enable the LED OFF TIMER
    timerFlag = ENABLED;
    previousMillis = millis(); // <----- Perhaps add this?

    T2CountDownSeconds = T2ResetDownSeconds;
    T2Count = 1;
  }

. . .

  //IF ENABLED, is it time to turn OFF the LED
  if (timerFlag == ENABLED && currentMillis - previousMillis >= Interval * 1000ul)
    {
    //we are now finished with the OFF TIMER
    timerFlag = DISABLED;
    digitalWrite(Output, LOW);

  }

This is my latest code and for the most part I'm getting what I want however the two countdown timers will randomly stop and sometimes cannot be restarted unless the board is reset. This happens sometimes every second time that I start the countdown times. I've looked at this code dozens of times but I have no idea what to change to fix this.

#include <Wire.h>
#include <RTClib.h>
#include <DS1307RTC.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

RTC_DS3231 rtc;

//OLED   define
#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels \
                          // Declaration for an SSD1306 display connected   to I2C (SDA, SCL pins) \
                          //Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


unsigned long T1Hours = 10;  //Overall total run time, Tumbler runs for this duration
unsigned long T1Minutes = 0;
unsigned long T1Seconds = 0;

unsigned long T2Hours = 0;  //interval between each watering
unsigned long T2Minutes = 1;
unsigned long T2Seconds = 0;

unsigned long WateringTime = 5;  //Time that water runs after each interval in Seconds

unsigned long T1CurrentSeconds = 0;  //convert overall total runtime to an integer
unsigned long T1PreviousSeconds = 0;
unsigned long T1CountDownSeconds = (T1Hours * 3600ul) + (T1Minutes * 60ul) + (T1Seconds * 1ul);
unsigned long T1ResetTimerSeconds = (T1Hours * 3600ul) + (T1Minutes * 60ul) + (T1Seconds * 1ul);

unsigned long T2CurrentSeconds = 0;  //convert interval between each watering to an integer
unsigned long T2PreviousSeconds = 0;
unsigned long T2CountDownSeconds = (T2Hours * 3600ul) + (T2Minutes * 60ul) + (T2Seconds * 1ul);
unsigned long T2ResetTimerSeconds = (T2Hours * 3600ul) + (T2Minutes * 60ul) + (T2Seconds * 1ul);

unsigned long SetNewWateringTime = 0;
unsigned long ChangeDuration = WateringTime;

unsigned long T1LeftOverMinutes = 0;
unsigned long T1LeftOverSeconds = 0;

unsigned long T2LeftOverMinutes = 0;
unsigned long T2LeftOverSeconds = 0;

int T1Count = 0;
int T2Count = 0;

const int TumblerOnOff = 10;
const int WaterOnOff = 11;

const int ButtonPin_1 = 2;
const int ButtonPin_2 = 3;
const int ButtonPin_3 = 4;
int ButtonState_1 = 0;
int ButtonState_2 = 0;
int ButtonState_3 = 0;

unsigned long T1previousMillis = 0;
unsigned long T2previousMillis = 0;

#define ENABLED true
#define DISABLED false
bool T1timerFlag = DISABLED;
bool T2timerFlag = DISABLED;

void setup() {
  pinMode(ButtonPin_1, INPUT);
  pinMode(ButtonPin_2, INPUT);
  pinMode(ButtonPin_3, INPUT);
  pinMode(WaterOnOff, OUTPUT);
  pinMode(TumblerOnOff, OUTPUT);

#define OLED_RESET -1  // Reset pin # (or -1 if sharing Arduino reset pin)

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C, OLED_RESET)) {  // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for (;;)
      ;
  }

  Serial.begin(9600);
  while (!Serial)
    ;  //wait for serial
  delay(200);
  Serial.println("DS3231RTC Read Test");
  Serial.println("-------------------");

  display.display();  //Display logo
  delay(1000);
  display.clearDisplay();
  display.display();
}

void loop() {

  ButtonState_1 = digitalRead(ButtonPin_1);
  ButtonState_2 = digitalRead(ButtonPin_2);
  ButtonState_3 = digitalRead(ButtonPin_3);

  if ((ButtonState_1 == 0) && (T1Count == 0) && (T2Count == 0)) {  // Start both timers

    T1previousMillis = millis();
    T2previousMillis = millis();
    T1timerFlag = ENABLED;
    T2timerFlag = ENABLED;
    SetNewWateringTime = ChangeDuration;

    digitalWrite(WaterOnOff, HIGH);  // Turn on Water, try to figure out how to cut this time in half

    T1Count = 1;  // Start T1 CountDown Timer
    T2Count = 1;  // Start T2 CountDown Timer
  }

  unsigned long T1currentMillis = millis();

  if (T1timerFlag == ENABLED && T1currentMillis - T1previousMillis >= (WateringTime + SetNewWateringTime) * 1000ul) {
    T1previousMillis = T1currentMillis;
    //we are now finished with the OFF TIMER
    T1timerFlag = DISABLED;
    SetNewWateringTime = ChangeDuration;

    digitalWrite(WaterOnOff, LOW);
  }

  if (ButtonState_2 == 0) {  // Pause both timers

    T1Count = 0;  // Pause T1
    T2Count = 0;  // Pause T2
    T1timerFlag = DISABLED;
    T2timerFlag = DISABLED;

    ChangeDuration = WateringTime;

    digitalWrite(WaterOnOff, LOW);
  }

  if ((ButtonState_3 == 0) && (T1Count == 0)) {  //Reset countdown timers, only when both timers are paused

    T1CurrentSeconds = 0;  // Reset T1
    T1PreviousSeconds = 0;
    T1CountDownSeconds = T1ResetTimerSeconds;

    T2CurrentSeconds = 0;  // Reset T2
    T2PreviousSeconds = 0;
    T2CountDownSeconds = T2ResetTimerSeconds;
    
    T1timerFlag = DISABLED;
    T2timerFlag = DISABLED;

    ChangeDuration = WateringTime;

    digitalWrite(WaterOnOff, LOW);
  }

  if (T1Count == 1) {
    digitalWrite(TumblerOnOff, HIGH);

  } else {
    if (T1Count == 0) {
      digitalWrite(TumblerOnOff, LOW);
    }
  }

  display.clearDisplay();
  tmElements_t tm;
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(18, 1);

  if (RTC.read(tm)) {
    Serial.print("Ok, Time = ");
    print2digits(tm.Hour);
    Serial.write(':');
    display.print(":");
    print2digits(tm.Minute);
    display.print(":");
    Serial.write(':');
    print2digits(tm.Second);
    Serial.print(", Date (M/D/Y) = ");

    display.setCursor(18, 20);
    if (T1Hours < 10) { display.print(0); }
    Serial.print(T1Hours);
    display.print(T1Hours);
    Serial.write(':');
    display.print(":");

    if (T1Minutes < 10) { display.print(0); }
    Serial.print(T1Minutes);
    display.print(T1Minutes);
    Serial.write(':');
    display.print(":");

    if (T1Seconds < 10) { display.print(0); }
    Serial.print(T1Seconds);
    display.print(T1Seconds);
    Serial.println();

    display.setCursor(18, 40);
    if (T2Hours < 10) { display.print(0); }
    Serial.print(T2Hours);
    display.print(T2Hours);
    Serial.write(':');
    display.print(":");

    if (T2Minutes < 10) { display.print(0); }
    Serial.print(T2Minutes);
    display.print(T2Minutes);
    Serial.write(':');
    display.print(":");

    if (T2Seconds < 10) { display.print(0); }
    Serial.print(T2Seconds);
    display.print(T2Seconds);
    Serial.println();
    display.display();

  } else {
    if (RTC.chipPresent()) {
      Serial.println("The DS3231 is stopped.  Please run the SetTime");
      Serial.println("example to initialize the time and begin running.");
      Serial.println();
    } else {
      Serial.println("DS3231 read error!  Please check the circuitry.");
      Serial.println();
    }
  }

  T1CurrentSeconds = ((tm.Hour * 3600ul) + (tm.Minute * 60ul) + (tm.Second * 1ul));

  if (T1CurrentSeconds > T1PreviousSeconds) {  // Code for T1 countdown timer,
    T1PreviousSeconds = T1CurrentSeconds;
    T1CountDownSeconds = T1CountDownSeconds - T1Count;
    T1Hours = T1CountDownSeconds / 3600ul;  //next few lines are for displaying the T1 count down
    T1LeftOverMinutes = T1CountDownSeconds - (T1Hours * 3600ul);
    T1Minutes = T1LeftOverMinutes / 60ul;
    T1LeftOverSeconds = T1LeftOverMinutes - (T1Minutes * 60ul);
    T1Seconds = T1LeftOverSeconds * 1ul;  //up to here
  }

  T2CurrentSeconds = ((tm.Hour * 3600ul) + (tm.Minute * 60ul) + (tm.Second * 1ul));

  if (T2CurrentSeconds > T2PreviousSeconds) {  // Code for T2 countdown timer
    T2PreviousSeconds = T2CurrentSeconds;
    T2CountDownSeconds = T2CountDownSeconds - T2Count;  //next few lines are for displaying the T2 count down
    T2Hours = T2CountDownSeconds / 3600ul;
    T2LeftOverMinutes = T2CountDownSeconds - (T2Hours * 3600ul);
    T2Minutes = T2LeftOverMinutes / 60ul;
    T2LeftOverSeconds = T2LeftOverMinutes - (T2Minutes * 60ul);
    T2Seconds = T2LeftOverSeconds;  //up to here
  }

  {
    if (T2CountDownSeconds <= 0) {
      digitalWrite(WaterOnOff, HIGH);
      T2timerFlag = ENABLED;
      T2previousMillis = millis();

      T2CountDownSeconds = T2ResetTimerSeconds;
      T2Count = 1;
    }
  }

  unsigned long T2currentMillis = millis();

  if (T2timerFlag == ENABLED && T2currentMillis - T2previousMillis >= (WateringTime + SetNewWateringTime) * 1000ul) {
    T2previousMillis = T2currentMillis;
    //we are now finished with the OFF TIMER
    T2timerFlag = DISABLED;
    SetNewWateringTime = 0;
    digitalWrite(WaterOnOff, LOW);

    ChangeDuration = 0;
  }

  if (T1CountDownSeconds == 0) {  // When T1 hits 0 both timers are paused and reset to there starting times
    T1Count = 0;                  // Stop T1 countdown timer
    T2Count = 0;                  // Stop T2 countdown timer

    T1timerFlag = DISABLED;
    T2timerFlag = DISABLED;

    T1CurrentSeconds = 0;  // Reset T1 to it's original time
    T1PreviousSeconds = 0;
    T1CountDownSeconds = T1ResetTimerSeconds;

    T2CurrentSeconds = 0;  // Reset T2 to it's original time
    T2PreviousSeconds = 0;
    T2CountDownSeconds = T2ResetTimerSeconds;

    digitalWrite(TumblerOnOff, LOW);
    digitalWrite(WaterOnOff, LOW);

    ChangeDuration = WateringTime;
  }

}

void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
    display.print("0");
  }
  Serial.print(number);
  display.print(number);
}
  • Hummm, you have an RTC connected but you are using millis( ) TIMERs, why ? :thinking:

  • BTW for documentation, don’t use HIGH, LOW, 1 or 0.

# define waterON    HIGH
# define waterOFF   LOW

# define tumblerON  HIGH
# define tumblerOFF LOW

Example:
digitalWrite(TumblerOnOff, tumblerOFF);

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