I am measuring the level of sound coming into the Arduino using analogRead();
My current code says to light an LED every time the analogRead is above 50.
The problem i'm having is that the LED lights up EVERY TIME the level is above 50 (see attachment- left side). What I want is to light the LED only when the level has gone above 50 for 300 milliseconds (see attachment- right side).
My objective is to build some code that filters out any stray readings above 50 and only triggers the LED when the reading has gone above 50 more that a few times in a row.
The code I'm imaging would be something like what I have sketched below, but I'm unsure how the functioning code would need to look:
void loop(){
val = analogRead(A5);
Serial.println(val);
delay(10); //so I don't flood the serial with data
if (val > 50 && val > 50 more than three times in 300 MS){
digitalWrite(13, HIGH);
Serial.print("ABOVE THRESHOLD! ");
Serial.println(val);
delay(10); //Pause to see the LED
} else {
digitalWrite(13, LOW);
}
}
What I want is to light the LED only when the level has gone above 50 for 300 milliseconds
Time stamp when the reading goes above threshold.
If the reading dips below threshold, reset.
If the current time minus the time stamp is greater than 300 milliseconds, light the LED.
Another thought (someone else on this list had a similar question today) is to use the median of samples. (Not quite the same as an average, more of order a set of samples and pick the middle one so the outliers don't actually change your median.) I found that someone has come up with a class for doing just this on the playground. See Arduino Playground - HomePage
What I want is to light the LED only when the level has gone above 50 for 300 milliseconds
Time stamp when the reading goes above threshold.
If the reading dips below threshold, reset.
If the current time minus the time stamp is greater than 300 milliseconds, light the LED.
I've always had trouble with Millis(). Does this code look right? (previousMillis is initialized at 0).
You need to reset the value of previousMillis to millis() (not zero) when the timer has exceeded the required time.
Can I suggest that you change the name of the variable to something that more accurately indicates what it represents ?
if (millis() - delayStart > 300)
{
digitalWrite(LED, HIGH);
delay(20);
digitalWrite(LED, LOW);
delay(20);
delayStart = millis(); //start the delay timer again
}
else
{
digitalWrite(LED, LOW);
}
This will flash the LED very briefly every 300 milliseconds.
Depending on what you are doing there is probably no need for the else clause if nothing is to change if the required time has not elapsed.
I like how you wrote that code. It makes it easier to understand without as many variables.
However, this is slightly different from what I want to do. I am reading an analog input (audio from a headphone cable). I am trying to detect when a 1Khz tone sounds for ~1 second (I have attached an audio file of a good example).
I cannot use a simple if/else, because the audio level peaks above 50 occasionally:
//won't work because of stray "blip" sound reading above 50
if ( audioLevel > 50) { light LED }
What I need is for the LED to light up when the analog reading goes above 50 more than 5 times in 1 second.
if ( audioLevel > 50 for more than 5 times in 1 second) ) { light LED }
Here is the what the serial monitor looks like for a 'blip':
0
0
0
0
0
0
0
32
55 //"blip" to filter out
20
0
0
0
0
0
0
And here is the what the serial monitor looks like when the tone is played (this is what I want to detect):
Thank you UKHeli, I've been going through this code line by line, but still cannot make it work.
I have added some comments to walk us through the code. I'm confused about where to reset the counter to zero, and also what you meant about setting counting to true in the code. Where would I do that? Thank you so much.
#define THRESHOLD 50
#define INTERVAL 2000 // Exaggerated for testing purposes.
#define LED 13
boolean counting = true;
int counter = 0;
long startTime = 0;
void setup(){
Serial.begin(9600);
pinMode(LED, OUTPUT);
}
void loop() {
// If the audio level is above 50 and counting is true, start incrementing counter.
if (analogRead(A5) > THRESHOLD && counting == true)
{
counter++;
}
// If two seconds have gone by, counting is false. Should counter also reset to zero here?.
if (millis() - startTime > INTERVAL)
{
counting = false;
// counter = 0; ???
startTime = millis();
}
if (counter > 4 && counting == false)
{
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
Serial.print(counter);
Serial.print(" ");
Serial.println(analogRead(A5));
}
What happens when you run the code ? As I said, it is untested and may have flaws in it.
The aim of the code, as presented, is to count values above 50 in a 2 second window. Once the 2 seconds are up the counting variable is set to false to stop further counting. Where you have commented would be a suitable place to reset the counter to zero. Because the count process. but not the program, is stopped by setting counting to false you can do another count at any time by setting the counting variable to true which will make the if (analogRead(inPin) > 50 && counting == true)test active again.
If you want the process to be continuous then don't set counting to false after 2 seconds or, better, remove it from the program and just reset counter where you have indicated. I was not sure whether you wanted to be able to turn the count process on/off hence its inclusion.
Bingo! Thanks again for the assistance UKHeli. I removed the counting boolean and made some other adjustment and now the code seems to be working well.
I noticed that before I started storing the analogRead(A5) in a variable called inPin, the counter would begin incrementing even if the value was not greater than the threshold.
I'm thinking that maybe there were readings that happened quicker that the Serial could print them. So storing that reading in a variable each time through the loops seems to trigger the counter correctly.
Thanks again for your help. I really appreciate it.
I'm glad you got it working. It is very pleasing to see someone take a skeleton of an idea and add their own spin to meet their needs rather than just ask for code to be written for them from which they learn nothing. Implementing this code will have given you some tools to use in other code, particularly the use of millis() as a timer.
From a personal point of view I like to put each { and } on its own line which helps me to see the start of each code block, but that is a matter of personal preference.