Millis function to keep LED on

I am trying to get my garden lights to turn on for varying durations after sunset.
I am using a LDR to fetch the light status however I would like to youse the millis() function to keep the LEDs on for a certain period while still being able to dim the circuit etc.

I feel like I am pretty close and have looked at blink without delay sketches and watched a bunch of videos but still can't quite crack it.

any help much appreciated

Ps. be gentle. I have been tinkering with arduino for 2 weeks :slight_smile:



// Sets the Constants

int ldr = A0;         //Set LDR PIN
int ldr_value = 0;
int GARDENLIGHTS = 5; //The PWM pin the LED is attached to
int PATHLIGHTS = 6;   //The PWM pin the LED is attached to
int STATUS_LED_G = 7; //The Green Indicator  LED of the outdoor Circuit 
int STATUS_LED_R = 8; //The Red Indicator  LED of the outdoor Circuit 

unsigned long previousMillis = 0; //Will store the time that the lights were last turned on

static unsigned long two_hours =  72000000;       // interval at which to Keep lights on for 2h (milliseconds)
const long four_hours = 14400000;  // interval at which to Keep lights on for 4h (milliseconds)
const long six_hours = 21600000;   // interval at which to Keep lights on for 6h (milliseconds)

void setup() {
  
Serial.begin(9600);   // initialize serial communication at 9600 bits per second:

// declares named LED pins to be an outputs://
 pinMode(GARDENLIGHTS, OUTPUT);
 pinMode(PATHLIGHTS, OUTPUT);
 pinMode(STATUS_LED_G, OUTPUT);
 pinMode(STATUS_LED_R, OUTPUT);
 
}

void loop() {
 unsigned long currentMillis = millis();
 ldr_value=analogRead(ldr);   //Reads the Value of LDR(light).
 
  //Runs this code if the value of the LDR is less than #
  
    if(ldr_value<500){
   
   if (currentMillis - previousMillis < two_hours) 
  
   
   digitalWrite(STATUS_LED_G,HIGH);   //Makes the Green Status LED glow in Dark.
   digitalWrite(STATUS_LED_R,LOW);    //Makes the Red Status LED turn off in the dark
   
   
   
    
    /*digitalWrite(STATUS_LED_G,LOW);   //Makes the Green Status LED glow in Dark.
    digitalWrite(STATUS_LED_R,HIGH);    //Makes the Red Status LED turn off in the dark*/
    int analogValue = analogRead(A1);                   //Reads the input on analog pin A1 (value between 0 and 1023)
    int brightness = map(analogValue, 0, 1023, 0, 255); //Scales it to brightness (value between 0 and 255)
  
    analogWrite(GARDENLIGHTS, brightness);              // sets the brightness of OUTDOOR LED 1 that connects to  pin 5 through PWM
    analogWrite(PATHLIGHTS, brightness);              // sets the brightness of OUTDOOR LED 1 that connects to  pin 5 through PWM

    

   //print out the value of the garden
   Serial.print("Analog: ");
   Serial.print(analogValue);
   Serial.print(", Brightness: ");
   Serial.println(brightness);
   
    previousMillis = currentMillis;
  }
  
  else //Runs this code if the value of the LDR is less than #
  {
    digitalWrite(STATUS_LED_G,LOW);   //Turns the LED OFF in Light.
    digitalWrite(STATUS_LED_R,HIGH);  //Turns the LED ON in Light.
    digitalWrite(GARDENLIGHTS,LOW);   //Turns the LED OFF in Light.

    Serial.println("LDR: ");  //Prints the value of LDR to Serial Monitor.
    Serial.println(ldr_value);    //As a value.
  }
  
}

since there are no braces associated with the condition, the only line it affects is the one immediately following it and i don't see previousMillis set before this.

presumably you have 3 modes of operation. one the ldr recognizes nightfall, you want the code to operate the LEDs for 2 hours allowing, i'm guessing a potentiometer on pin A1 to control brightness (the comment should say read pot, not A1)

but you also need to only recognize nightfall once, not continually recognize it and reset your timer. so you need to have a day mode and a night mode and only check for nightfall in day-mode and when nightfall occurs enter a timed-mode where the lights are on and when the timer expires change to night-mode

does this make sense?

consider

const byte LdrPin       = A1;
const byte PotPin       = A0;
const byte RedLedPin    = 13;
const byte GrnLedPin    = 12;
const byte GardenLedPin = 10;

enum { Day, Timed, Night };
int mode = Day;

#define Hours(x)    (x*3600*1000)
const unsigned long Period = 5000;
unsigned long       msecLst;

const int NightThresh = 600;
const int DayThresh   = NightThresh + 50;

enum { Off = HIGH, On = LOW };

char s [80];

// -----------------------------------------------------------------------------
void lightsOff ()
{
    digitalWrite (RedLedPin, Off);
    digitalWrite (GrnLedPin, On);
    digitalWrite (GardenLedPin, Off);
}

// -------------------------------------
void lightsOn ()
{
    digitalWrite (RedLedPin, On);
    digitalWrite (GrnLedPin, Off);
    analogWrite (GardenLedPin, map (analogRead (PotPin), 400, 500, 0, 255));
}

// -----------------------------------------------------------------------------
void loop ()
{
    unsigned long msec = millis ();
    int           ldr = analogRead (LdrPin);

    switch (mode) {
    case Day:
        if (NightThresh > ldr)  {
            mode    = Timed;
            msecLst = msec;
            Serial.println (s);
            sprintf (s, " ldr %d, mode timed", ldr);
        }
        break;

    case Timed:
        if (msec - msecLst > Period)  {
            mode = Night;
            Serial.println (s);
            sprintf (s, " ldr %d, mode night", ldr);
            lightsOff ();
        }
        else
            lightsOn ();
        break;

    case Night:
        if (DayThresh < ldr) {
            mode = Day;
            Serial.println (s);
            sprintf (s, " ldr %d, mode day", ldr);
        }
        break;
    }

#if 0
    Serial.println (s);
    sprintf (s, " ldr %d, mode %d, pot %d", ldr, mode, analogRead (PotPin));

    delay (1000);
#endif
}

// -------------------------------------
void setup ()
{
    Serial.begin (9600);
    pinMode (LdrPin, INPUT_PULLUP);

    pinMode (RedLedPin, OUTPUT);
    pinMode (GrnLedPin, OUTPUT);
    pinMode (GardenLedPin, OUTPUT);

    lightsOff ();
}

i tested this with a multifunction shield, using a button on an analog LdrPin the pulls the pin from 1023 to 0, a pot on A0 and a few LEDs.

yes you need to change the value of Period and possibly the Day/NightThresholds

thanks so much, this is great!

it almost works but the garden lights are constantly on.

also when they sense darkness and detect to pot value, the pot value changes does 2 sweeps from dark to light then is stuck permenently on.

im assuming it all has t do with this line

analogWrite (GardenLedPin, map (analogRead (PotPin), 400, 500, 0, 255));

but again im not to familiar with it all

I should probably explain the end goal is to have essentially 5 different states.

state 1 lights on after sunset +2h
state 2 lights on after sunset +4h
state 3 lights on after sunset +6h
state 4 lights permanently on until switched off
state 5 lights permanently off until switched on

I am hoping to control this all with a push button rotary encoder that I am using to control a menu (already built) that will control these functions.

the menu looks like this so far

// =======================================================================================
// ||                                    Libraries                                          ||
// =======================================================================================
#include <ClickEncoder.h>
#include <TimerOne.h>

#define WITH_LCD 1                          //LCD now active in all defininitions tha rely on it

#ifdef WITH_LCD
#include <Wire.h>
#include <hd44780.h>                        // include hd44780 library header file
#include <hd44780ioClass/hd44780_I2Cexp.h>  // i/o expander/backpack class
hd44780_I2Cexp lcd;
#endif

// =======================================================================================
// ||                                    Variables                                          ||
// =======================================================================================

#ifdef WITH_LCD
const int LCD_COLS = 16;
const int LCD_ROWS = 2;
#endif

ClickEncoder *encoder;
int16_t last, value; // not too sure

void timerIsr() {
  encoder->service(); //use timer interrupt library?
}

int selectButton = 8;
int menu = 1;

// =======================================================================================
// ||                                    SETUP                                          ||
// =======================================================================================

void setup() {
  int status;
  status = lcd.begin(LCD_COLS, LCD_ROWS);

  Serial.begin(9600);
  encoder = new ClickEncoder(0, 1, 8, 2);  //Sets Data pins, Button pin, and steps per notch of encoders

  Timer1.initialize(1000);                 //Initialize the timer
  Timer1.attachInterrupt(timerIsr);        //Initialize the timer intterupt

  last = 0;                                //Setting last to 0


  pinMode(selectButton, INPUT_PULLUP);    //Setting the pinmode and internal pullup resistor for "selectButton"


  //--------------------------------------------//
  lcd.clear();                  //clear the whole LCD
  lcd.setCursor(0, 0);          //Defining positon to write from first row, first column .
  lcd.print("Garden lights");
  lcd.setCursor(0, 1);          //Second row, first column
  lcd.print("Project 2022");
  lcd.setCursor(0, 2);          //Second row, first column
  delay(3000);                  //wait 3 sec
  lcd.clear();                  //clear the whole LCD
  
  //--------------------------------------------//
  updateMenu();                 //Updates the screen with the menu
}

// =======================================================================================
// ||                                  MAIN LOOP                                        ||
// =======================================================================================

void loop() {
  value += encoder->getValue(); //get the value of the Encoder position

  if ((value > last)) {         //if the value is greater than the last value
    last = value;               //Move up one space
    lcd.backlight();
    menu++;
    updateMenu();
    delay(100);
    Serial.println(value);
  }
  if ((value < last)) {
    last = value;
    lcd.backlight();
    menu--;
    updateMenu();
    delay(100);
    Serial.println(value);
  }
  if (!digitalRead(selectButton)) {
    executeAction();
    updateMenu();
    delay(100);
  }
}
// =======================================================================================
// ||                              Main Menu - Screens                             ||
// =======================================================================================
void updateMenu() {
  switch (menu) {
    case 0:
      menu = 6;

    case 1:
      lcd.clear();
      lcd.print(">Sunset Timer");
      lcd.setCursor(0, 1);
      lcd.print(" Set Brightness");
      break;
    case 2:
      lcd.clear();
      lcd.print(" Sunset Timer");
      lcd.setCursor(0, 1);
      lcd.print(">Set Brightness");
      break;
    case 3:
      lcd.clear();
      lcd.print(">All Lights On");
      lcd.setCursor(0, 1);
      lcd.print(" All Lights Off");
      break;
    case 4:
      lcd.clear();
      lcd.print(" All Lights On");
      lcd.setCursor(0, 1);
      lcd.print(">All Lights Off");
      break;
      
    case 5:
      lcd.clear();
      lcd.print(">Screen Off");
     
      break;
    
    case 6:
      menu = 0;
      break;
  }
}
// =======================================================================================
// ||                              Main Menu - Executables                              ||
// =======================================================================================

void executeAction() {
  switch (menu) {
    case 1:
      action1();
      //code for action 1 goes here//
      lcd.noBacklight();
      break;
    case 2:
      action2();
      //code for action 2 goes here//
      lcd.noBacklight();
      break;
    case 3:
      action3();
      //code for action 3 goes here//
      lcd.noBacklight();
      break;
    case 4:
      action4();
      //code for action 4 goes here//
      lcd.noBacklight();
      break;
    case 5:
      action5();
      lcd.clear();
      lcd.noBacklight();
      break;
    
  delay(3000);
  lcd.noBacklight();
  }
}

// =======================================================================================
// ||                                    Tools-Display                                          ||
// =======================================================================================

void action1() {
  lcd.clear();
  lcd.print(">Sunset + 2h");
  lcd.setCursor(0, 1);
  lcd.print(" Sunset + 4h");
  delay(1500);
}
void action2() {
  lcd.clear();
  lcd.print(">Lights at 50%");
  delay(1500);
}
void action3() {
  lcd.clear();
  lcd.print(">Lights Now On");
  delay(1500);
}
void action4() {
  lcd.clear();
  lcd.print(">Lights Now Off");
  delay(1500);
}
void action5() {
  lcd.clear();
  lcd.noBacklight();
}
void action6() {
  lcd.clear();
  lcd.clear();
  lcd.noBacklight();
  lcd.clear();
  }


  


looks at my enum for Off and On, they're probably opposite for your hardware

did you enable my ifdef and are reporting what's printed?

pot value shouldn't change based on darkness. which pin are you using?

what do you mean 2 sweeps? i set the timer for 5 seconds.

study it. hopefully it gives you ideas

so you need a few more modes. since the periods are the same, that doesn't need to change, but msecLst needs to be reset after each timeout

hopefully you see how you need to recognize different "kinds of" things that need to be done and separate that functionality. dealing with the lights is one "kind of" thing and control is another.

looking at your menu code:

  • puzzled by following, why += and not simple =
value += encoder->getValue();
  • how do you prevent menu from going out of range: < 0
  • in general, i tried to avoid redundant code
  • in loop(), i think it would be less redundant to have a condition for value != last (better of having valueLst) and a condition for > last that handle menu+/1
  • should updateMenu() have a 100 msec delay?
  • why not call lcdBacklight() once at the end of executeAction()
  • is the following under case 5 of executeAction() an oversite? it's not part of any case
  • instead of having several actionN() functions that do similar things, why not have a function that clears and prints something on the lcd and does the delay which is called instead of the actionN() in the executeAction()

void lcdDisp (
    int         row,
    const char *s0,
    const char *s1,
    )
{
    lcd.clear ();
    lcd.print (s0);
    if (s1)  {
        lcd.setCursor(0, 1);
        lcd.print (s1);
    }
    delay(1500);
}

probably other improvements that simplify the code. just look for reducing redundancy. post updates for more feedback if wanted

1 Like

Yes that totally solved that

I also changed the mapping for my pot which fixed that sweep thing when I was playing with the pot knob.

now to tackle this menu and integrate it all together

because when I do the menu wont go down, it simply very quickly bounces between:
case 1: lcd.clear(); lcd.print(">Sunset Timer"); lcd.setCursor(0, 1); lcd.print(" Set Brightness"); break; case 2: lcd.clear(); lcd.print(" Sunset Timer"); lcd.setCursor(0, 1); lcd.print(">Set Brightness"); break;

by having

 switch (menu) {
    case 0:
      menu = 6;
and

case 6:
menu = 0;
break;
}

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