Can't find the reason my light timer doesn't work

Hey guys!
I've got a long program for a gardening system running on an Arduino Nano and it's supposed to turn on and off the grow lights at set times. It calculates the time it should turn on and off the grow lights depending on what I am growing and the length of the plant (which affects how strong light the plant gets). However when it needs more time than 20 hours per day, it is still supposed to cap that time to 20 hours to ensure the plants get at least 4 hours of night each day, but for some reason my grow lights runs 24/7 still.
I really can't seem to figure it out and have been at this for an eternity.
Can perhaps someone here see if there's something obvious I've missed?
Code below, the light control function is called LightTimer. Thanks!

void LightTimer(int current_hour, int current_minute){

  float halftime = on_hours/2;

  // Function for emulating daylight using RTC time function and LED lights
  if (current_minute != last_lightminute) {
    if (current_hour < (13-halftime) || current_hour > (12+halftime)) { // Eg. halftime = 8 gives current_hour < 5 OR current_hour > 20
      if (current_hour > 21 || current_hour < 7) {
        digitalWrite(mushlight_pin, LOW);
      }
      else {
        digitalWrite(mushlight_pin, HIGH);
      }
      digitalWrite(growlight_pin, LOW);
      digitalWrite(fan_pin, LOW);
    }
    else if (current_hour > (12-halftime) && current_hour < (13+halftime)) { // Eg. halftime = 8 gives current_hour > 4 AND current_hour < 21
      if (mode == 1) {
        digitalWrite(mushlight_pin, HIGH);
        digitalWrite(growlight_pin, LOW);
        digitalWrite(fan_pin, LOW);
      }
      else if (mode == 2) {
        digitalWrite(mushlight_pin, HIGH);
        digitalWrite(growlight_pin, HIGH);
        digitalWrite(fan_pin, HIGH);
      }
    }
    last_lightminute = current_minute;
  }
}

void nap(int msec){

  // Pause function
  unsigned long currentMillis;
  unsigned long previousMillis = millis(); // Setting starting time

  while (true) {
    currentMillis = millis();
    if (currentMillis - previousMillis >= msec) {
    break;
    }
  }
}

Hello billskullman

Use a logic analyzer to see what happens.

Insert Serial.println()´s at points of interrest.

Have a nice day and enjoy coding in C++.

    // Calculating daily hour need for grow lights
    on_hours = (277.8 * DLI_need / PPFD_measure);
    PPFD_measure = 0;

Print on_hours to see the values are correct.

  while (true)
  {
    currentMillis = millis();
    if (currentMillis - previousMillis >= msec)
    {
      break;
    }
  }

Blocking code with a millis based TIMER ?
:scream:


Time to study how a State Machine can be used in your sketches.
:wink:

Ok so found it haha.. I was overwriting the on_hours after setting it to a max 20. Silly me :slight_smile:

What did you mean by this? :

Is this a bad solution?

I did this because I want to port the system to an ESP32 when it is finally delivered here, and use OTA updates but then I can't use delay() as I understand.

1 Like
  • A millis based TIMER is meant to replace delay( ) as a non-blocking replacement.

  • A short look at your code suggests it would benefit from a State Machine structure.

Ok thanks, I'll have a look at state machine structure :slight_smile:

There are many examples on this WEB site that will help with understanding a State Machine.

Here is just one example that should help in your journey.

this code is a complicated replacement for delay() which has the samweblocking nature as a delay()

The microcontroller is forced to stay inside your while-loop.
On an ESP32 this is even more blocking than a delay() executed on an ESP32

in this condition

    if (currentMillis - previousMillis >= msec)

On an ESP32
if msec has a value greater that a few seconds the watchdog-timer will bite you

blocking code is not limited to the function delay()
of course you can code blocking code without delay()
your code has to be non-blocking.

It's supposed to be like this on the ESP32:


void nap(int msec){

  // Pause function while using OTA
  unsigned long currentMillis;
  unsigned long previousMillis = millis(); // Setting starting time

  do {
    // OTA 
    currentMillis = millis();
    ArduinoOTA.handle();
    }
  while (currentMillis - previousMillis >= msec);
}

Is that still bad, or is there a better way to do this? Maybe the state-machine way as LarryD suggested?

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