Fun with Arduino 14 Day Night Cycle with millis(), no delay()

Time_for_ActionNow that we know how to get rid of the delay(0 and use millis() in stead (see previous video 13) we can finalize our Automatic Day Night Light Cycle unit to have direct on screen feedback of cycle time adjustment by the user and to have the cycle stop, and the lights turn off, immediately when the switch is set to ‘off’.

Our unit has quite nice specifications:

  1. Configurable timing, via keyboard or via analog input with on screen display
  2. An option to randomize the times to give it some ‘livelyness’
  3. On screen display of the on/off, day/night state and the cycles times

Let’s first modify the code we had in video 12 which uses the potentiometer analog input to change the cycle time.

Read  on below the video …

In the declarations we add light_status as byte and time_for_action as unsigned long.

In loop() we check if millis() > time_for_action and if it is, we invert the light_status, and check if it is day or night to print the proper text. Then we add cycle_time to millis() to calculate the next time_for_action.

This way the loop() is always running and we will be able to see changes we make to the cycle_time with the potentiometer printed on screen immediately. When we switch the toggle switch off, time_for_action and light_status are immediately set to 0 and we will have an immediate stop.

#define SWITCH_PIN 2 // input for toggle switch
#define POTM_PIN A0  // potmeter analog input 
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time
byte light_status;   // 0=off, 1=on
byte switch_new, switch_old;
unsigned int cycle_time, cycle_time_old;
unsigned int day_time, night_time;
unsigned long time_for_action;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  cycle_time = map(analogRead(POTM_PIN), 0, 1024, 1, 31) * 10;
  cycle_time_old = cycle_time;
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To change the cycle time, rotate the knob");
  Serial.println();
}

void loop() {
  
// read potentiometer and print to screen, only if changed
  cycle_time = map(analogRead(POTM_PIN), 0, 1024, 1, 31) * 10;
  if(cycle_time != cycle_time_old) {
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" seconds");
    cycle_time_old = cycle_time;
    delay(100);
  }
  
// print cycle on/off status to screen, only if changed
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }

  if (switch_new == LOW) { // day/night cycle started
    digitalWrite(LED_PIN, HIGH);
    if (millis() > time_for_action) {
      light_status = !light_status;
      if (light_status == 1) {
        night_time = cycle_time + random(T_MIN, T_MAX);
        time_for_action = millis() + night_time * 1000UL;
        Serial.print("Night time = ");
        Serial.print(night_time);
        Serial.println(" seconds");        
      }
      else {
        day_time = cycle_time + random(T_MIN, T_MAX);
        time_for_action = millis() + day_time * 1000UL;
        Serial.print("Day time   = ");
        Serial.print(day_time);
        Serial.println(" seconds");
      }
    } 
  }

  else { // cycle stopped
    digitalWrite(LED_PIN, LOW);
    light_status = 0;
    time_for_action = 0;
  }
  digitalWrite(LIGHTS_PIN, light_status);
}

Yes … this works nice. And the code is still easy to read and understand. Let’s also change the code we used for the keyboard input to complete our mision:

#define SWITCH_PIN 2 // input for toggle switch
#define LIGHTS_PIN 8 // output to FET or relay module
#define LED_PIN A5   // output to LED on switch panel
#define T_MIN 1      // minimum random time to add to cycle_time
#define T_MAX 5      // maximum random time to add to cycle_time
byte light_status;   // 0=off, 1=on
byte switch_new, switch_old;
unsigned int cycle_time = 1; // [s] 180 = 3 minutes
unsigned int day_time, night_time;
unsigned long time_for_action;

void setup() {
  pinMode(SWITCH_PIN, INPUT_PULLUP);
  pinMode(LIGHTS_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  Serial.println();
  Serial.println("Day/Night cycle system is ready");
  Serial.println();
  Serial.print("Cycle time: ");
  Serial.print(cycle_time);
  Serial.println(" seconds");
  Serial.println();
  Serial.println("To enter a new cycle time, type");
  Serial.println("in the field above and press Enter");
  Serial.println();
}

void loop() {
// Read serial input and change cycle_time
  if (Serial.available() > 0) {
    cycle_time = Serial.parseInt();
    Serial.println();
    Serial.print("Cycle time changed to: ");
    Serial.print(cycle_time);
    Serial.println(" seconds");
    Serial.println();
  }
// Check if toggle switch changed state and print if so
  switch_new = digitalRead(SWITCH_PIN);
  if (switch_new != switch_old) {
    if (switch_new == LOW) Serial.println("Cycle started");
    else Serial.println("Cycle stopped");
    switch_old = switch_new;
  }

// Change lights in day/night cycle
  if (switch_new == LOW) { // cycle started
    digitalWrite(LED_PIN, HIGH);
    if (millis() > time_for_action) {
      light_status = !light_status;
      if (light_status == 1) {
        night_time = cycle_time + random(T_MIN, T_MAX);
        time_for_action = millis() + night_time * 1000UL;
        Serial.print("Night time = ");
        Serial.print(night_time);
        Serial.println(" seconds");        
      }
      else {
        day_time = cycle_time + random(T_MIN, T_MAX);
        time_for_action = millis() + day_time * 1000UL;
        Serial.print("Day time   = ");
        Serial.print(day_time);
        Serial.println(" seconds");
      }
    } 
  }

  else { // cycle stopped
    digitalWrite(LED_PIN, LOW);
    time_for_action = 0;
    light_status = 0;
  }
  digitalWrite(LIGHTS_PIN, light_status);
}

Yes, that works fine too. Mission accomplished … our Day Night Cycle Light Switching Module is finished!

We’ll be using this if (millis() > time_for_action) {…} construction more often, with many applications there’s a need for timers and this way we can keep the Arduino active and have it attend to other tasks in the mean time.

There is one little issue … millis() and time_for_action are unsigned long variables … they are not infinite. After 49 days millis() will overflow and restart at 0. We will probably have switched off our Arduino long before that, but if you have your Arduino permanently powered on, then this code can be used in stead:

if (millis() - previous_time_for_action > INTERVAL) {
  previous_time_for_action = millis();
  // your code for when it is time for action goes here
}

Even after an overflow of millis() the outcome of the subtraction is still correct, so this code does not have the issue that after 49 days the lighting will have one wrong cycle.

We’ve seen the analogRead() instruction at work, with the potentiometer input. It probably won’t come as a surprise that there also exists a counterpart: analogWrite(). In the next video we are gooing to make a LED dimmer. We will use this analogWrite() connected to a power FET module … this enables us to control the brightness of LED (strip) lighting.

— 0 —

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s