Set a minimum time period for sensor to read data

Hello!
It happens that sometimes a sensor reads only a fluctuation of the data, in my case I use an LDR and I do not want a shadow that falls on the sensor for 2-3 seconds to affect the desired process. In my sketch I try to set a minimum period of time for which the sensor reads data over a threshold, just after this period the next event happens. I don't know if my logic is correct. Now, the code doesn't work very well, I can see a delay between the data read over the threshold and the moment when the LED lights up, but it's not what it should be, most of the time everything happens instantly. Is it too difficult to get the desired result ?

int led = 4;
int ldr = A0;
int val = 0;
int interval = 6000;
const int threshold = 450; 
unsigned long previousMillis = 0;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(led, OUTPUT);
  pinMode(ldr, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  val = analogRead(ldr); // read data
  Serial.println(val);
  delay(10);// wait for 0.01s
  if(val >= threshold){ // if read data is >= threshold
      unsigned long currentMillis = millis(); // start counting the amount of time
      // if the time since the data exceeded the threshold is greater than interval
      if (currentMillis - previousMillis >= interval) { 
        digitalWrite(led, HIGH); // turn the led on
        previousMillis = currentMillis;
    }
  }
  else {
      digitalWrite(led, LOW);
  }
}

If you want to check that a value is above a threshold continuously for a period then you need to reset the timer when it is below the threshold - even though that sounds like upside-down logic. Like this

if (value < threshold) {
  timeVaueWasLastLow = millis(); // reset the timer
}

if (millis() -  timeValueWasLastLow >= period) {
  // value has been high throughout period
  // do something
}

...R

I recommend you take a look at the "smoothing" example in the Arduino IDE. Examples > 03.analog> Smoothing

It allows you to sample a series of LDR readings and filter out the spikes.

Robin2 has addressed the direct flaw in the code you presented, but I think there is another fundamental flaw that is dependent on your actual process. Having an LED respond to an LDR once a threshold time has been met could be the whole thing, but if not, you need to give a few more details. Are you looking for the LDR to start AND stop the process? What is the actual process? Are there times when you want to ignore input from the LDR entirely?

Robin2:
If you want to check that a value is above a threshold continuously for a period then you need to reset the timer when it is below the threshold - even though that sounds like upside-down logic. Like this

if (value < threshold) {

timeVaueWasLastLow = millis(); // reset the timer
}

if (millis() -  timeValueWasLastLow >= period) {
 // value has been high throughout period
 // do something
}




...R

Well, it works. If the val of the LDR is greater than threshold, the LED lights up after that interval specified, as soon as the val of LDR drops below threshold the LED turns off. Now the loop function looks like this:

  if (val < threshold) {
      timeValueWasLastLow = millis(); // reset the timer
    }
  if (millis() -  timeValueWasLastLow >= interval) { // whatever interval in seconds
      // value has been high throughout period
      // do something
      digitalWrite(led, HIGH);
    }
  else {
      digitalWrite(led, LOW);
  }

I was confused about the logic of your code, you are counting the time which the value of LDR is smaller than threshold, I was trying the opposite.
So, Correct me if I am wrong:
At the first counting of time: timeValueWas...= millis() means 0=0; 0-0=0 not greater than interval.
At the second counting of time: 1=1; 1-0=1 not greater than interval.
At the third counting of time: 2=2; 2-1=1 not greater than interval, and so on until millis-timeValueWas.. is greater than interval.

hmeijdam:
I recommend you take a look at the "smoothing" example in the Arduino IDE. Examples > 03.analog> Smoothing

It allows you to sample a series of LDR readings and filter out the spikes.

I will take a look at it. Thanks for reply.

Perehama:
Robin2 has addressed the direct flaw in the code you presented, but I think there is another fundamental flaw that is dependent on your actual process. Having an LED respond to an LDR once a threshold time has been met could be the whole thing, but if not, you need to give a few more details. Are you looking for the LDR to start AND stop the process? What is the actual process? Are there times when you want to ignore input from the LDR entirely?

I was just trying to check the functionality of my sketch, I have a homework that I wanted to try this thing on, it has enough shortcomings and I try to improve it. This is the main function, I also have a LCD display on which I will try to display the message Possible Fire, ONLY during the time when the sensor values exceed the thresholds, after that reset the LCD display.

void Detect(){ 
  flame_val = analogRead(flame); 
  gas_val = analogRead(gas); 
  /*if ( (gas_val >= gasThreshold)||(flame_val >= flameThershold) ) // what if both values are greater than thresholds ??!
	{ 
        digitalWrite(Buzzer, HIGH); 
        delay(1000);
		digitalWrite(Relay, HIGH);
	}
    else if ((gas_val < gasThreshold) && (flame_val < flameThershold)) { // what if only one value is smaller than threshold ??!
          digitalWrite(Buzzer, LOW);
          digitalWrite(Relay, LOW); */
	if ( flame_val < flameThershold ) { // not working with (gas_val < gasThreshold)||(flame_val < flameThershold)
			timeValueWasLastLow = millis(); // reset the timer
		}
	if (millis() -  timeValueWasLastLow >= period) { // whatever period in seconds
			digitalWrite(Buzzer, HIGH); 
			delay(1000); 
			digitalWrite(Relay, HIGH); 

		}
	else {
			digitalWrite(Buzzer, LOW); 
			digitalWrite(Relay, LOW); 
		}
} // close Detect

cristian10001:
At the third counting of time: 2=2; 2-1=1 not greater than interval, and so on until millis-timeValueWas.. is greater than interval.

Yes

...R

Robin2:
Yes

...R

I understand. Why do you think it doesn't work for this instruction as well, with two sensors ?

if ( (gas_val < gasThreshold)||(flame_val < flameThershold) )

I know that I have 4 scenarios in which the respective sensors can be found, but at least for it to work.

cristian10001:
I understand. Why do you think it doesn't work for this instruction as well, with two sensors ?
if ( (gas_val < gasThreshold)||(flame_val < flameThershold) )
I know that I have 4 scenarios in which the respective sensors can be found, but at least for it to work.

With just a single line of code I can't figure it out.

What, exactly do you want to happen - please describe the requirement in English without using code.

...R

Robin2:
With just a single line of code I can't figure it out.

What, exactly do you want to happen - please describe the requirement in English without using code.

...R

Ooh ... sorry for this misunderstanding. I have attached a function Detect() above in the post, it`s about a flame and gas sensor working at the same time, as a fire alarm (for demonstration purposes). It has several shortcomings, but I want to add the lines of code you wrote for LDR.

cristian10001:
Ooh ... sorry for this misunderstanding. I have attached a function Detect() above in the post,

Where?

Please don't make changes to earlier Posts other than to correct typos. Please post the the latest version of your program in your next Post where it will be easy to find and where the chronological flow of the discussion is maintained.

it`s about a flame and gas sensor working at the same time, as a fire alarm (for demonstration purposes). It has several shortcomings, but I want to add the lines of code you wrote for LDR.

I'm afraid that does not tell use how you want the system to work - it only tells us what components the system will have.

...R

Robin2:
Where?

Please don't make changes to earlier Posts other than to correct typos. Please post the the latest version of your program in your next Post where it will be easy to find and where the chronological flow of the discussion is maintained.

I'm afraid that does not tell use how you want the system to work - it only tells us what components the system will have.

...R

void Detect(){
  flame_val = analogRead(flame);
  gas_val = analogRead(gas);
  /*if ( (gas_val >= gasThreshold)||(flame_val >= flameThershold) ) // what if both values are greater than thresholds ??!
	{
        digitalWrite(Buzzer, HIGH);
        delay(1000);
		digitalWrite(Relay, HIGH);
	}
    else if ((gas_val < gasThreshold) && (flame_val < flameThershold)) { // what if only one value is smaller than threshold ??!
          digitalWrite(Buzzer, LOW);
          digitalWrite(Relay, LOW); */
	if ( flame_val < flameThershold ) { // not working with (gas_val < gasThreshold)||(flame_val < flameThershold)
			timeValueWasLastLow = millis(); // reset the timer
		}
	if (millis() -  timeValueWasLastLow >= period) { // whatever period in seconds
			digitalWrite(Buzzer, HIGH);
			delay(1000);
			digitalWrite(Relay, HIGH);

		}
	else {
			digitalWrite(Buzzer, LOW);
			digitalWrite(Relay, LOW);
		}
} // close Detect

It was just a little above, now you will understand, it is a simple program. The commented lines are what I wrote before, if the version I made with LDR will not work, I will return to what I wrote.

Please select Edit, or More:Modify and clear out all the color tags in that post.

It is no good you asking a question in your code in Reply #12

// what if both values are greater than thresholds ??!

You need to tell us how you want it to behave in each of the 4 possible situations.

...R

That's why I said that the project has some shortcomings. Regarding that commented question [it was not necessary to pay attention :confused: ] I was thinking of introducing a switch with 4 cases, something like this [ I haven't written a switch-case from a long time :slightly_frowning_face: :sweat_smile: ] :

void Detect(){ 
  flame_val = analogRead(flame); 
  gas_val = analogRead(gas);
  int x;
  switch(x){ // something like that, I have to experiment a lot with this instruction
    case 1:
      x = (gas_val <= gasThreshold) && (flame_val <= flameThershold)
          digitalWrite(Buzzer, LOW);
          digitalWrite(Relay, LOW);
    break;
    case 2:
      x = (gas_val < gasThreshold) && (flame_val > flameThershold)
        digitalWrite(Buzzer, HIGH); 
        delay(interval); 
        digitalWrite(Relay, HIGH);
    break;
    case 3:
      x = (gas_val > gasThreshold) && (flame_val < flameThershold);
        digitalWrite(Buzzer, HIGH); 
        delay(interval); 
        digitalWrite(Relay, HIGH);
    break;
    case 4:
      x = (gas_val > gasThreshold) && (flame_val > flameThershold);
        digitalWrite(Buzzer, HIGH); 
        delay(interval); 
        digitalWrite(Relay, HIGH);
    break;
  } // close switch case 
} // Close Detect

Mainly it is enough for me to enter the lines of code for that period of data acquisition at the sensors in the function you saw [void Detect()], because I do not know if I have enough time to introduce switch-cases. I would also introduce a Possible Fire message on the LCD when the sensors exceed the thresholds, then when they go back down, the initial Alarm Activated message will appear.
And this is the complete code in which I want to introduce what you told me about LDR (if I have time I will do more for this project) :

#include <Wire.h> 
#include <LiquidCrystal_I2C.h> 
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); 

const int buttonPin = 2; 
int buttonState = 0;         
int lastButtonState = 0;     

int Buzzer = 7; 
int Relay = 8; 

int flame = A0; 
int flameThershold = 900; 
int flame_val = 0; 

int gas = A1; 
int gasThreshold = 800; 
int gas_val = 0; 

bool flag = false; 
const long interval = 5000; // ms

void setup() {
  Serial.begin(9600);
  pinMode(flame,INPUT); 
  pinMode(gas, INPUT); 
  pinMode(Buzzer, OUTPUT); 
  pinMode(Relay, OUTPUT); 
  pinMode(buttonPin, INPUT);
  lcd.begin(16,2); 
  lcd.clear(); 
  lcd.setCursor(0,0); 
  lcd.print("Fire Alarm"); 
} // void setup

void loop() {
  buttonState = digitalRead(buttonPin); 
  //filter out any noise by setting a time buffer
  if (buttonState != lastButtonState) {
    if (buttonState == LOW) { 
      if (flag){
          lcd.clear(); 
          lcd.setCursor(0,0);
          lcd.print("Alarm "); 
          lcd.setCursor(0,1);
          lcd.print("deactivated");
          flag = false;
          delay(500); // delay between transitions
      }
        else { 
          lcd.clear();
          lcd.setCursor(0,0);
          lcd.print("Alarm activated");
          flag = true; 
          delay(500);
        }
     }
      delay(50); 
 }  //close if(time buffer)  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;
  if(flag){ // 
    Detect(); 
  }
} // void loop

void Detect(){ 
  flame_val = analogRead(flame); 
  gas_val = analogRead(gas); 

  if ( (gas_val >= gasThreshold)||(flame_val >= flameThershold) ) 
  { 
        digitalWrite(Buzzer, HIGH); 
        delay(interval); 
        digitalWrite(Relay, HIGH); 
  }
    else if ((gas_val < gasThreshold) && (flame_val < flameThershold)) {
          digitalWrite(Buzzer, LOW);
          digitalWrite(Relay, LOW);
          }
 
} // Detect

Operation (currently): power the circuit; the message Fire Alarm appears; press the button once, the message Alarm Activated appears, the sensors record data; press the button second time, the message Alarm Deactivated appears, the sensors are off.

Robin2:
It is no good you asking a question in your code in Reply #12

// what if both values are greater than thresholds ??!

You need to tell us how you want it to behave in each of the 4 possible situations.

...R

cristian10001:
That's why I said that the project has some shortcomings. Regarding that commented question [it was not necessary to pay attention :confused: ] I was thinking of introducing a switch with 4 cases, something like this [ I haven't written a switch-case from a long time :slightly_frowning_face: :sweat_smile: ] :

  switch(x){ // something like that, I have to experiment a lot with this instruction

case 1:
      x = (gas_val <= gasThreshold) && (flame_val <= flameThershold)
          digitalWrite(Buzzer, LOW);
          digitalWrite(Relay, LOW);
    break;

That is a strange way to think of using SWITCH/CASE.

The more usual way would be to reduce the gas_val relative to threshold and flame_val relative to threhold to one of 4 numbers (there can only be 4 situations for 2 variables) and then use that for the SWITCH - something like

switch(combinedState) {
  case 1:
      digitalWrite(Buzzer, LOW);
      digitalWrite(Relay, LOW);
     break;
  // etc

You could use an ENUM to give meaningful names to the states.

I still don't see any description of what you want to happen for each of the 4 states. Just to be clear the 4 states are

  • gasLow and flameLow
  • gasHigh and flameLow
  • gasHigh and flameHigh
  • gasLow and flameHigh

...R

Robin2:
That is a strange way to think of using SWITCH/CASE.

The more usual way would be to reduce the gas_val relative to threshold and flame_val relative to threhold to one of 4 numbers (there can only be 4 situations for 2 variables) and then use that for the SWITCH - something like

switch(combinedState) {

case 1:
     digitalWrite(Buzzer, LOW);
     digitalWrite(Relay, LOW);
    break;
 // etc




You could use an ENUM to give meaningful names to the states.


I still don't see any description of what you want to happen for each of the 4 states. Just to be clear the 4 states are

- gasLow and flameLow
- gasHigh and flameLow
- gasHigh and flameHigh
- gasLow and flameHigh


...R

Or I can use if statements and that`s it (as you can see, my basic knowledge of C does not help me with a switch-case statement ).

if (gas_val >= gasThreshold)&&(flame_val >= flameThershold) // case A
	digitalWrite(Buzzer, HIGH); // turn on the buzzer
	delay(2000); // wait for 1s - this delay came to my head
	digitalWrite(Relay, HIGH); // turn on the relay - it should control a sprinklern but not necessary
if (gas_val < gasThreshold)&&(flame_val < flameThershold) // if both reads are < Thresholds - no need 
	digitalWrite(Buzzer, LOW); // to turn HIGH the buzzer
	digitalWrite(Relay, LOW); // neither the relay
if (gas_val >= gasThreshold)&&(flame_val < flameThershold) // case B
	digitalWrite(Buzzer, HIGH); // turn on the buzzer
	delay(2000); // wait for 1s - this delay came to my head
	digitalWrite(Relay, HIGH); // turn on the relay
if (gas_val < gasThreshold)&&(flame_val >= flameThershold) // case C
	digitalWrite(Buzzer, HIGH); // turn on the buzzer
	delay(2000); // wait for 1s - this delay came to my head
	digitalWrite(Relay, HIGH); // turn on the relay

If I will use these if statements, how can I introduce the thing you did for LDR? For this case nothing is happening, maybe because I used | | instead of &&.
if ( (gas_val < gasThreshold) || (flame_val < flameThershold) )
timeValueWasLastLow = millis(); // reset the timer
}
if (millis() - timeValueWasLastLow >= period) { // whatever period in seconds
digitalWrite(Buzzer, HIGH);
delay(1000);
digitalWrite(Relay, HIGH);

}
else {
digitalWrite(Buzzer, LOW);
digitalWrite(Relay, LOW);
}
If that doesn't work, I'll just try to display a message on the LCD for only cases A, B, C.. The message Alarm Activated is displayed throughout the program.

cristian10001:
Or I can use if statements and that`s it (as you can see, my basic knowledge of C does not help me with a switch-case statement

SWITCH / CASE is just a convenient but less versatile version of IF / ELSE

...R