If-Condition over time

Hello folks,

I want to program a very simple if-condition.

For example:

if(lightLevel < minLevel) {

digitalWrite(led, HIGH);

}

This is the first step. Now I want to implement the time. I know that there is a time library, but how can I archive that the condition is only met if the lightLevel is smaller than the minLevel for let's say 5 seconds?

I hope someone is able to help me.

Kind regards,
Naderk

Save the time of the reading (taken from millis()) when the light level falls below the minLevel and set a boolean variable to true. Keep reading the light level each time through loop() and if it goes above minLevel set the boolean variable to false. Each time through loop() if the variable is true and the time now (millis()) minus the start time is greater than or equal to 5 seconds then the value has been below minLevel for 5 seconds.

I would go about this the other way round - by resetting the timer whenever the value is exceeded. Then the timer will only expire if the light level stays low for long enough. Something like this

void loop() {
    lightLevel = analogRead(lightPin);
    if (lightLevel > threshold) {
        startMillis = millis();  // reset the clock
    }
   if (millis() - startMillis >= interval) {
      // do stuff for time-up
   }
}

...R

First, thank you both for your answers.

However until now, I am not able to set up as I imagined.
I understood that if the if-condition is met, it will somehow save the actual time from millis() into startMillis. After that, millis will continue to count the ms. In the next if condition, it will check if the 'new' millis() - startMilllis is over or equal than 5000. If it's true, it will power the led.

The Problem I think, is, that the second if condition is not directly related to the first, isn't it?

int photoRPin = 5;
int minLight; //Used to calibrate the readings
int maxLight; //Used to calibrate the readings
int lightLevel;
int adjustedLightLevel;
int led = 8;
int t = 0;
boolean light;
int startMillis;
void setup() {
Serial.begin(9600);

//Setup the starting light level limits
lightLevel=analogRead(photoRPin);
minLight=lightLevel-20;
maxLight=lightLevel;

pinMode(led, OUTPUT);
}

void loop(){
//auto-adjust the minimum and maximum limits in real time
lightLevel = analogRead(photoRPin);
if(minLight>lightLevel){
minLight=lightLevel;
}
if(maxLight<lightLevel){
maxLight=lightLevel;
}

//Adjust the light level to produce a result between 0 and 100.
adjustedLightLevel = map(lightLevel, minLight, maxLight, 0, 100);

//Send the adjusted Light level result to Serial port (processing)
//Serial.println(adjustedLightLevel);
//delay(50);

if(adjustedLightLevel < 10) {
startMillis = millis(); //reset the clock
light = true;
}

if(millis() - startMillis >= 5000 && light) {
digitalWrite(led, HIGH);
}
else {
digitalWrite(led, LOW);
light = false;
}
}

The problem is that you keep reseting the startMillis variable EVERY time the light condition is correct.
Also startMillis should be an unsigned long type not an int.

naderk:
if(adjustedLightLevel < 10) {
startMillis = millis(); //reset the clock
light = true;
}

Because you are using < in your test you are doing the exact opposite of what I recommended.
And I don't think there is any need for the line light = true;

You only reset the clock when the desited condition is NOT met.

...R

Each time the loop finishes and starts, there are three things to worry about:

  • light level threshhold
  • timeout
  • is the light currently on or off

This gives you 8 possible states, but there are really only three, because we are only interested in the states in which the previous execution of the loop (or the setup function) might have left things when it finished.

  • light is off, and we are not counting down
  • light is off, and we are counting down
  • light is on, and we are not counting down

Let's call these three states "OFF, COUNTDOWN, ON". We define a variable outside the loop to "remember" the current state, and we'll also add the countdown start millis because we are going to need it later :slight_smile:

enum state {
  OFF, COUNTDOWN, ON
};

state current_state;
unsigned long countdownMs;

void setup() {
  // put your setup code here, to run once:

  current_state = OFF;
}

Now, each time we run through the loop, we do something (or nothing) based on the current state and what our sensors tell us - millis() in this respect is a sensor that tells us the time.

void loop() {
  switch (current_state) {
    case OFF:
      break;
    case COUNTDOWN:
      break;
    case ON:
      break;
  }
}

the question then becomes - what code belongs in those cases? Well, it will probably be something like:

void loop() {
  switch (current_state) {
    case OFF:
      if (isLightAboveThreshhold()) {
        // do nothing;
      }
      else {
        countdownMs = millis();
        current_state = COUNTDOWN;
      }
      break;

    case COUNTDOWN:
      if (isLightAboveThreshhold()) {
        // don't need to perform any action to stop counting down
        current_state = OFF;
      }
      else if (millis() - countdownMs > 5000) {
        digitalWrite(LIGHT_PIN, HIGH);
        current_state = ON;
      }
      else {
        // do nothing
      }
      break;

    case ON:
      if (isLightAboveThreshhold()) {
        digitalWrite(LIGHT_PIN, LOW);
        current_state = OFF;
      }
      else {
        // do nothing
      }      break;
  }
}

boolean isLightAboveThreshhold() {
  return analogRead(LIGHT_SENSOR) > LIGHT_THRESHOLD;
}

Now, you can refactor this many ways, but this style of programming - a state transition machine - works well with an arduino and is relatively easy to document and diagram. We have a variable named currentState, our loop is a single switch statement that handles all of the states, and for each state we have a chain of if-else-if statements that always has a final "else" clause and a break.

Each clause in the if-else chain has an action (or there's a comment saying "no action"), and as it's final act sets the current state to the new state (or there's a comment saying that the state is left as it is).

This means that we can read off the state transitions by looking at the main loop alone.

Conditions or actions that are complex or need to be done in more than one case (like checking the light sensor) get shoved out into functions. In this case, the "start timer" and "check timer" functionality might have been made into functions, even though they are only needed once.

  • give your functions meaningful names
  • whenever a variable stores a physical quantity, suffix it with a unit of measurement - ms, ohms, feet or meters.

Thank you very much for your help.

It finally worked, as I imagined.

Best,
Naderk