How to run a program only once inside another program in every loop?

Hi, I am new to Arduino. Currently using an Arduino UNO and programmed it for a specific task using LDR to detect sunrise and sundown. However, Since I will be outside my house for a couple of days, I wish to program it to actuate a 12V DC valve which could water my plants for say 4 minutes everyday each time after sunrise and also after sundown. Can anyone help me with the basic coding as using for() loop inside the If () is not viable. Also, I want the valve to close after 4 minutes and not to open till the relay 2 change its state after sundown or sunrise.

Thanks in advance.

my program so far;

void loop()
{
lightvalue = analogRead (ldr);
lightvalue = 100 - lightvalue/10.24;

if (lightvalue >= 90)
{
digitalWrite (relay2, LOW);
}
else
{
digitalWrite (relay2, HIGH);
}
delay (500);
}

If you just want the pump to operate when daylight is first detected and again when darkness is first detected then you program need to activate when the LDR value changes from dark to light and again when it changes from light to dark. But that is not going to be easy because the daylight level changes very slowly and, if there are clouds, it may get bright and go dark again a few minutes later. You may be able to compensate for that by leaving a long interval between successive readings of the LDR - perhaps 20 minutes or 30 minutes.

I suspect it would be more reliable to use time to determine when the pump operates.

If you do want to use the change in light level then you need code something like this pseudo code

previousLightLevel = lightLevel;
lightLevel = analogRead(LDRpin);
if (lightLevel >= brightValue and previousLightLevel <= darkValue) {
   // it just got bright
}

if (lightLevel <= darkValue and previousLightLevel >= brightValue) {
   // it just got dark
}

I would not bother with this conversion

lightvalue = 100 - lightvalue/10.24;

I would just work with the value that analogRead() produces.

…R

Robin2:
If you just want the pump to operate when daylight is first detected and again when darkness is first detected then you program need to activate when the LDR value changes from dark to light and again when it changes from light to dark. But that is not going to be easy because the daylight level changes very slowly and, if there are clouds, it may get bright and go dark again a few minutes later. You may be able to compensate for that by leaving a long interval between successive readings of the LDR - perhaps 20 minutes or 30 minutes.

I suspect it would be more reliable to use time to determine when the pump operates.

If you do want to use the change in light level then you need code something like this pseudo code

previousLightLevel = lightLevel;

lightLevel = analogRead(LDRpin);
if (lightLevel >= brightValue and previousLightLevel <= darkValue) {
  // it just got bright
}

if (lightLevel <= darkValue and previousLightLevel >= brightValue) {
  // it just got dark
}




I would not bother with this conversion


lightvalue = 100 - lightvalue/10.24;



I would just work with the value that analogRead() produces.

...R

Thank You for the suggestion. As you said the light intensity value may vary during cloudy days which could turn on and off the valve several times. I will try with timer coding but I fear the counter may accumulate time wrongly leading to water my plants in an odd time interval. However, let me try that also and I believe to run both functions, timer and LDR simultaneously, I will be needing While() to write the coding?

noiasca:
There is no "program in another program" on the Arduino (for you).
It has to be "ONE" program, but you can use functions.

First you need a clear understanding of your total program flow.

Just as an example:
You could block your watering after the watering until it is released due to the LDR

IF (LDR is above threashold AND blockingFlag is false) THEN
{

  • set blockingFlag = false
    }

IF LDR is below threashold THEN
{

  • do watering
  • blockingFlag = true
    }

BTW: I wouldn't leave the house with an untested sketch :wink:

Hi, Thanks for the guidance. I understand this will give control over the valve actuator in any one particular state of my LDR value, say for example, when the LDR value is above x, the valve would open. But how do I run it only once? considering I do not want it to water my plants again as the LDR value will remain above x for a long time during the day or night. Please forgive my poor knowledge over logic.

a) recap the flow again:
the blocking flag will be released only after the LDR will geht above a certain threshold. So if it gets dark, the plant gets watered once only. So it is basically a question how you set your two thresholds.

b) if you don’t rely on the threshold only, consider something like robin2 explained. Add a time component.
when you are finished with watering, remember the actual timestamp

previousMillis=millis();

and add a conditon in your check that you want at least - let’s say 18h difference

if (ldr > onThreshold && blockingFlag == false && millis()-previousMillis > 18601000UL)

edit, just as POC:
not tested with hardware:

// https://forum.arduino.cc/index.php?topic=713828.0

const uint16_t brightValue = 900;
const uint16_t darkValue = 200;
const uint8_t blockHours = 18;    // minimum waiting time for next watering
bool blockingFlag = false;        // watering not allowed

const uint8_t ldr = A0;           // Pin with LDR
const uint8_t relay2 = 13;        // Pin with active low relay

uint32_t previousMillis;

void setup()
{
  Serial.begin(115200);
  Serial.println(F("waters plants once after dark"));
  Serial.println(F("send w for manual watering"));
  pinMode(relay2, OUTPUT);
}

void loop()
{
  int lightvalue = analogRead(ldr);


  if (lightvalue > brightValue && blockingFlag == false && millis() - previousMillis > blockHours * 60 * 1000UL)
  {
    Serial.println(F("activated"));
    doWatering();
    blockingFlag = true;
  }

  if (lightvalue < darkValue)
  {
    Serial.println(F("reset blocking"));
    blockingFlag = false;
  }

  if (millis() % 5000 == 0)  // roughly/unsecure all 5 seconds...
  {
    Serial.print(F("last watering "));
    Serial.print((millis() - previousMillis) / 60 / 1000UL);
    Serial.print(F(" minutes ago. LDR="));
    Serial.print(lightvalue);
    if (blockingFlag) 
      Serial.println(F(" - blocked"));
    else
      Serial.println(F(" - active"));
  }

  if (Serial.available())
  {
    char c = Serial.read();
    switch (c)
    {
      case 'w' :
        doWatering();
        break;
      case '\r' :  // just ignore CR
      case '\n' :  // just ignore LF
        break;
      default:
        Serial.println(F("Command unknown"));
    }
  }
}

void doWatering()
{
  Serial.println(F("watering - blocking"));
  digitalWrite (relay2, LOW);
  delay(4242);  // dirty delay
  digitalWrite (relay2, HIGH);
  previousMillis = millis();
}

noiasca:
a) recap the flow again:
the blocking flag will be released only after the LDR will geht above a certain threshold. So if it gets dark, the plant gets watered once only. So it is basically a question how you set your two thresholds.

b) if you don’t rely on the threshold only, consider something like robin2 explained. Add a time component.
when you are finished with watering, remember the actual timestamp

previousMillis=millis();

and add a conditon in your check that you want at least - let’s say 18h difference

if (ldr > onThreshold && blockingFlag == false && millis()-previousMillis > 18601000UL)

edit, just as POC:
not tested with hardware:

// https://forum.arduino.cc/index.php?topic=713828.0

const uint16_t brightValue = 900;
const uint16_t darkValue = 200;
const uint8_t blockHours = 18;    // minimum waiting time for next watering
bool blockingFlag = false;        // watering not allowed

const uint8_t ldr = A0;           // Pin with LDR
const uint8_t relay2 = 13;        // Pin with active low relay

uint32_t previousMillis;

void setup()
{
 Serial.begin(115200);
 Serial.println(F(“waters plants once after dark”));
 Serial.println(F(“send w for manual watering”));
 pinMode(relay2, OUTPUT);
}

void loop()
{
 int lightvalue = analogRead(ldr);

if (lightvalue > brightValue && blockingFlag == false && millis() - previousMillis > blockHours * 60 * 1000UL)
 {
   Serial.println(F(“activated”));
   doWatering();
   blockingFlag = true;
 }

if (lightvalue < darkValue)
 {
   Serial.println(F(“reset blocking”));
   blockingFlag = false;
 }

if (millis() % 5000 == 0)  // roughly/unsecure all 5 seconds…
 {
   Serial.print(F(“last watering “));
   Serial.print((millis() - previousMillis) / 60 / 1000UL);
   Serial.print(F(” minutes ago. LDR=”));
   Serial.print(lightvalue);
   if (blockingFlag)
     Serial.println(F(" - blocked"));
   else
     Serial.println(F(" - active"));
 }

if (Serial.available())
 {
   char c = Serial.read();
   switch (c)
   {
     case ‘w’ :
       doWatering();
       break;
     case ‘\r’ :  // just ignore CR
     case ‘\n’ :  // just ignore LF
       break;
     default:
       Serial.println(F(“Command unknown”));
   }
 }
}

void doWatering()
{
 Serial.println(F(“watering - blocking”));
 digitalWrite (relay2, LOW);
 delay(4242);  // dirty delay
 digitalWrite (relay2, HIGH);
 previousMillis = millis();
}

Hi, Thanks again. I could sum up to use your method and am able to stop water (relay1) in any particular instance. However, since I am changing the blockingFlag = true to stop the watering, it is not allowing me to start watering my plants in the next instance. The watering seems to stop after only one run. Can you please help with my below coding? I have kept the timings short to check in real time.
#include <elapsedMillis.h>
int relay1 = 13;
int ldr = A0;
int relay2 = 8;
int lightvalue = 0;
unsigned long previousMillis=0;
boolean blockingFlag = false;
void setup()
{
pinMode (relay1, OUTPUT);
pinMode (relay2, OUTPUT);
}
void loop()
{
lightvalue = analogRead (ldr);
lightvalue = 100 - lightvalue/10.24;
if (lightvalue >= 90)
{
digitalWrite (relay2, LOW);
if (lightvalue >=90 && blockingFlag == false && millis()-previousMillis > 21000UL)
{
digitalWrite (relay1, HIGH);
previousMillis = millis();
delay (5000);
digitalWrite (relay1, LOW);
blockingFlag = true;
}
}
else
{
digitalWrite (relay2, HIGH);
if (lightvalue <=90 && blockingFlag == false && millis()-previousMillis > 2
1000UL)
{
digitalWrite (relay1, HIGH);
previousMillis = millis();
delay (5000);
digitalWrite (relay1, LOW);
blockingFlag = true;
}
}
delay (500);
}

Please follow the advice on posting a programming question given in Read this before posting a programming question

In particular note the advice to Auto format code in the IDE and to use code tags when posting code here as it prevents some combinations of characters in code being interpreted as HTML commands such as italics, bold or a smiley character, all of which render the code useless

UKHeliBob:
Please follow the advice on posting a programming question given in Read this before posting a programming question

In particular note the advice to Auto format code in the IDE and to use code tags when posting code here as it prevents some combinations of characters in code being interpreted as HTML commands such as italics, bold or a smiley character, all of which render the code useless

Ok. I will do so from onwards.

33-arnab:
However, let me try that also and I believe to run both functions, timer and LDR simultaneously, I will be needing While() to write the coding?

As a general rule use IF rather than WHILE except in those cases where the WHILE loop will complete in a few millisecs.

Have a look at how the code is organized in Several Things at a Time

Note how each function runs very briefly and returns to loop() so the next one can be called. None of the functions tries to complete a task in one call. And there may be dozens of calls to a function before it is actually time for it to do anything.

...R

Be very careful. Bugs in your code or wiring may end up with a valve left open. Best if that valve is only attached to a reservoir rather than mains water.

This is the sort of project that I would want to have had running for a long time (months preferably) before I would want to leave it to run unattended.