Keep your LCD clock updating while your loop is going?

I have a solar powered, automated watering system I just put together. Right now I have it set so that at the start of every loop, it tests soil saturation, and if dry, triggers relay that opens sprinklers for x seconds. Easy. I have it connected to a serial LCD that prints, at the start of every loop, soil saturation and time since last relay trigger.

I want to have a 2 hour loop between soil tests. My problem is that if I just throw a 2hr delay at the end of my loop, the time since last watering will only update every 2hrs when the loop restarts making my time not very accurate.

Is there any way to make my timer run/update constantly only resetting when the relay is tripped, but only test the soil every 2hrs? Here's the code I wrote (It just has test values right now - it tests the soil every 5 seconds and only turns sprinklers on for 2 seconds, but it works great other than the timer being inaccurate if I extend the loop with a delay)

int soil=0;
int Relay = 8;
int hygvcc = 13;
int relay_level=40;

//print test
//int switchPin = 8;                   // the number of the input pin

long startTime; // the value returned from millis when the switch is pressed
long duration;  // variable to store the duration


//#include LiquidCrystal lcd(a5, a4); /*commented from original to replace with I2C code*/
// the setup routine runs once when you press reset:

//I2c Code
   //I2C Includes
#include 
#include  
#include 

#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
   
int n = 1;

LiquidCrystal_I2C   lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);


void setup() {
  
  
// initialize serial communication at 9600 bits per second:

Serial.begin(9600); // set up the LCD's number of columns and rows:
pinMode(hygvcc, OUTPUT); //defines pin to turn hygrometer on and off
pinMode(Relay, OUTPUT);

//print test
 // pinMode(switchPin, INPUT);

}

// the loop routine runs over and over again forever:

void loop() {

// read the input on analog pin 0:
delay(5000); //simple delay just to check hygrometer on command is working correctly
digitalWrite(hygvcc, HIGH); //turns hygrometer on

delay(50);

int sensorValue = analogRead(A0);

sensorValue = constrain(sensorValue, 485, 1023);

// print out the value you read: Serial.println(sensorValue);

// initialize the digital pin as an output.

//pinMode(Relay, OUTPUT); //Appears to be in the code twice for some reason?

//map the value to a percentage

soil = map(sensorValue, 485, 1023, 100, 0);

// print out the soil water percentage you calculated to the LCD Screen:
Serial.print(soil);

Serial.println("%");
// set the cursor to column 0, line 1

// (note: line 1 is the second row, since counting begins with 0):


lcd.begin(20, 4);
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home
// Print a message to the LCD.

lcd.clear();

lcd.print("Soil Saturation:");

lcd.setCursor(0, 1);

// print the number of seconds since reset:

lcd.print(soil);

lcd.print ("%");

// delay in between reads for stability
digitalWrite(hygvcc, LOW); //turns hygrometer off


// This next loop turns on the relay for one minute if the moisture is below the level we want (in this case 60%), if the moisture is above that level, it keeps testing. If the level of moisture is below that level it turns the relay (and subsequently the water valve for as long as you would like (in this case it is set for 60000 milliseconds (1 minute)). It then waits another minute before testing the soil again in order to see if it is wet enough.


if (soil < relay_level) {

digitalWrite(Relay, HIGH);

// turn the Relay on (HIGH is the voltage level)

delay(5000);

// wait for a minute 

digitalWrite(Relay, LOW);

// turn the LED off by making the voltage LOW
startTime = millis();
} 
//print test
     // here if the switch is pressed
     
     //while(digitalRead(Relay) == LOW); // wait while the switch is still pressed
     long duration = ((millis() - startTime) / 1000)/60;
     Serial.println(duration, 0);
     lcd.setCursor(0,2);
     lcd.print("Time Since Last H20:");
     lcd.setCursor(0,3);
     lcd.print(duration);


delay(5000);

// wait for a minute


}

(TAGS ADDED BY MODERATOR)

Is there any way to make my timer run/update constantly only resetting when the relay is tripped, but only test the soil every 2hrs?

Yes. Take a look at the blink without delay example in the IDE.

Please use code tags when posting code.

Thanks! And sorry for my newbness and not using code tags. Thanks for your patience with folks like me :blush: I'll get better.

If you have a minute, here's the code I've come up with. I'd be interested to know what you think.

The idea is that at start up, it turns on the hygrometer for an initial test and waters if necessary. At that time it prints the soil saturation and records millis to two differents ints, one for use with the updating timer and one for use with the blink with no delay code (I didn't know a better way to do a one time only check, so I just made it if(millis()<3000) thinking it will skip over every time in the future. Seems ugly, but that's all I got.

From there, I think that I have a loop that will keep my clock updating based off currentMillis - startTime with startTime being reset every time the watering device is triggered.

Then I have the Blink without delay code set to perform a soil test every time currentMillis is 90 minutes more than previousMillis (I know it's only 1 second in the code, I'll change to 90 min later)

In short, it preforms a check at startup, then timer starts updating LCD with Time Since Last H20, and every 90 minutes the hygrometer is turned on for a reading and if water is low then system waters, resets clock, and waits another 90 minutes.

The system is way out in the fields, so I'll head out later today to do some tests.

int soil=0;
int Relay = 8;
int hygvcc = 13;
int relay_level=65;
long interval = 1000; // Interval between soil tests
//print test
//int switchPin = 8;                   // the number of the input pin

unsigned long startTime; // will store last time watering system was triggered 
unsigned long duration;  // variable to store the duration
unsigned long previousMillis; // will store last time soil was tested

//#include LiquidCrystal lcd(a5, a4); /*commented from original to replace with I2C code*/
// the setup routine runs once when you press reset:

//I2c Code
   //I2C Includes
#include 
#include  
#include 

#define I2C_ADDR    0x27 // <<----- Add your address here.  Find it from I2C Scanner
#define BACKLIGHT_PIN     3
#define En_pin  2
#define Rw_pin  1
#define Rs_pin  0
#define D4_pin  4
#define D5_pin  5
#define D6_pin  6
#define D7_pin  7
   
int n = 1;

LiquidCrystal_I2C   lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);


void setup() {
  
  
// initialize serial communication at 9600 bits per second:

Serial.begin(9600); // set up the LCD's number of columns and rows:
pinMode(hygvcc, OUTPUT); //defines pin to turn hygrometer on and off
pinMode(Relay, OUTPUT);

//print test
 // pinMode(switchPin, INPUT);

}

// the loop routine runs over and over again forever:

void loop() {

//Initial reading
if(millis() < 3000) {
  digitalWrite(hygvcc, HIGH); 
  delay(50);
  int sensorValue = analogRead(A0);
  sensorValue = constrain(sensorValue, 485, 1023);
  soil = map(sensorValue, 485, 1023, 100, 0);
  //print soil reading
  Serial.print(soil);
  Serial.println("%");
  lcd.begin(20, 4);
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  // Print soil saturation message to the LCD.
  lcd.clear();
  lcd.print("Soil Saturation:");
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(soil);
  lcd.print ("%"); 
  //save millis for use in blink with no delay code 
  previousMillis = millis();
  //save millis for us in printing time since last h20 - before first watering it will be time since turn on
  startTime = millis();
  //if statement to determine if water is needed
  if (soil < relay_level) {
    digitalWrite(Relay, HIGH);
    delay (4000);
    digitalWrite(Relay, LOW);
    }
}
    //print time since last H20 to LCD - constant update
     duration = ((millis() - startTime) / 1000)/60;
     Serial.println(duration, 0);
     lcd.setCursor(0,2);
     lcd.print("Time Since Last H20:");
     lcd.setCursor(0,3);
     lcd.print(duration);
     
//blink without delay code to test soil every 90 minutes 
unsigned long currentMillis = millis();

if(currentMillis - previousMillis > interval) {
  previousMillis = currentMillis;
  digitalWrite(hygvcc, HIGH); 
  delay(50);
  int sensorValue = analogRead(A0);
  sensorValue = constrain(sensorValue, 485, 1023);
  soil = map(sensorValue, 485, 1023, 100, 0);
  //print soil reading
  Serial.print(soil);
  Serial.println("%");
  lcd.begin(20, 4);
  lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
  lcd.setBacklight(HIGH);
  lcd.home (); // go home
  // Print soil saturation message to the LCD.
  lcd.clear();
  lcd.print("Soil Saturation:");
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(soil);
  lcd.print ("%"); 
  //if statement to determine if water is needed
  if (soil < relay_level) {
    digitalWrite(Relay, HIGH);
    delay (4000);
    digitalWrite(Relay, LOW);
    startTime = millis();
  }
   
}
}

uhohsmack: I have a solar powered, automated watering system I just put together. Right now I have it set so that at the start of every loop, it tests soil saturation, and if dry, triggers relay that opens sprinklers for x seconds. Easy. I have it connected to a serial LCD that prints, at the start of every loop, soil saturation and time since last relay trigger.

I want to have a 2 hour loop between soil tests. My problem is that if I just throw a 2hr delay at the end of my loop, the time since last watering will only update every 2hrs when the loop restarts making my time not very accurate.

Why not set an int to 7200 (number of seconds in 2 hours), then update your display, delay 1 second, decrement the counter, do whatever else you want to do each second and when the int hits zero, reset to 7200 and do whatever else you want to do every 2 hours?

Note that this is approximate and will NOT accurately follow "clock time". If you want that accuracy, you will need an RTC (real time clock) circuit.

Krupski: Why not set an int to 7200 (number of seconds in 2 hours), then update your display, delay 1 second, decrement the counter, do whatever else you want to do each second and when the int hits zero, reset to 7200 and do whatever else you want to do every 2 hours?

Note that this is approximate and will NOT accurately follow "clock time". If you want that accuracy, you will need an RTC (real time clock) circuit.

Not worried about accuracy, but what would be the advantage of doing it that way over the second code I posted with one loop running the clock and another piece of code looking for a when millis hits a certain interval?

Your one second delay got me to thinking. There is no reason I need the LCD updating as fast as I have it right now. Right now I have it only read whole minutes anyway, so I 'm going to through a 1 minute delay after the LCD update.

Serial.begin(9600); // set up the LCD's number of columns and rows:

It is always helpful when comments explain exactly what a complicated line of code does.

UKHeliBob: Serial.begin(9600); // set up the LCD's number of columns and rows:

It is always helpful when comments explain exactly what a complicated line of code does.

Now THATS what you call a high resolution display!

uhohsmack: Not worried about accuracy, but what would be the advantage of doing it that way over the second code I posted with one loop running the clock and another piece of code looking for a when millis hits a certain interval?

Your one second delay got me to thinking. There is no reason I need the LCD updating as fast as I have it right now. Right now I have it only read whole minutes anyway, so I 'm going to through a 1 minute delay after the LCD update.

I didn't look at your second piece of code.... I was replying to the first one.

If once a minute LCD update is good enough, then do the same thing, just with 120 instead of 7200 (fits in a uint8_t too!)

Krupski: I didn't look at your second piece of code.... I was replying to the first one.

If once a minute LCD update is good enough, then do the same thing, just with 120 instead of 7200 (fits in a uint8_t too!)

The problem was that i needed LCD to update every minute, but the water test to take place only once every two hours. I was just wondering if there was any advantage to your countdown method as opposed to my code that kept the LCD updating with a new time while looking for a when the time counted up to 2 hours.

Don't know much about coding form, so always curious to hear if there advantages to other solutions/of they are just good form/or if they are just different.

Thanks