## filtering analog data

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### filtering analog data

The LED's will change state when the analog output is within six or seven numbers of the threshold, making accurate transitions impossible. The colors oscillate back and forth. There must be a better way! Would appreciate your thoughts.

Code: Select all

``````unsigned long starTime;
int flashRed = 20000;

int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;

#define blueLED 11
#define redLED 12
#define greenLED 13
#define AnIn 1
#define redThresh 670
#define blueThresh 660
#define greenThresh 0

int val;

void setup()
{

starTime = millis();  //timer

pinMode(redLED,OUTPUT);
pinMode(blueLED,OUTPUT);
pinMode(greenLED,OUTPUT);

Serial.begin(9600);
}
void loop()
{
// advance to the next position in the array:
index = index + 1;
// if we're at the end of the array...
// ...wrap around to the beginning:
index = 0;
// calculate the average:

starTime = millis();  //timer

// val = val/2;
if(average >= redThresh){
digitalWrite(redLED,HIGH);
digitalWrite(blueLED,LOW);
digitalWrite(greenLED,LOW);
delay(1000);
}
else {
digitalWrite(redLED,LOW);

if((average>= blueThresh) && (average <= redThresh))
{
digitalWrite(blueLED,HIGH);
digitalWrite(redLED,LOW);
digitalWrite(greenLED,LOW);
delay(1000);
}
else {
digitalWrite(blueLED,LOW);

if((average>= greenThresh) && (average <= blueThresh))
{
digitalWrite(greenLED,HIGH);
digitalWrite(blueLED,LOW);
digitalWrite(redLED,LOW);
delay(1000);
}
else {
digitalWrite(greenLED,LOW);
}
}
Serial.println(average);
}

}

``````

Posts: 87337
Joined: Sat Feb 07, 2009 10:11 am

### Re: filtering analog data

Analog inputs on the Arduino are pretty noisy. I usually shift off the 2 low order bits as it is generally all noise. In this case, you may need to do three bits.

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### Re: filtering analog data

Sorry, I have no idea how to shift off lower order bits but I certainly need to learn. Would you please write a snippet that illustrates the procedure? Thanks.

Posts: 87337
Joined: Sat Feb 07, 2009 10:11 am

### Re: filtering analog data

To shift off the lowest three bits you can simply divide by 8.

Or you can use the C shift operator: '>>'

as in:

Code: Select all

``value = value >> 3;``

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### Re: filtering analog data

That is more simple than expected. Thanks. I have tried to plug this filter into the sketch but the results are quacky; the red flashes, the blue stay on, and the green is never high. I have rewritten this sketch a bunch of different ways but the LED's will not cooperate. Analog readings from 640~680

Code: Select all

``````#define FILTER_SHIFT 3

#define redLED 13
#define blueLED 12
#define greenLED 11
#define redThresh 680
#define blueThresh 678
#define greenThresh 0

int32_t filter_reg;
int16_t filter_input;
int16_t filter_output;
void setup(){
pinMode(redLED,OUTPUT);
pinMode(blueLED,OUTPUT);
pinMode(greenLED,OUTPUT);
Serial.begin(9600);

}
void loop(){

// low pass filter to get rid of noise
filter_reg = filter_reg - (filter_reg >> FILTER_SHIFT) + filter_input;
filter_output = filter_reg >> FILTER_SHIFT;
delay(100);

if(filter_output >= redThresh)
{
digitalWrite(redLED, HIGH);
digitalWrite(blueLED,LOW);
digitalWrite(greenLED, LOW);
delay(100);

}
else{
digitalWrite(redLED, LOW);

}

if((filter_output < redThresh) && (filter_output > greenThresh))
{
digitalWrite(blueLED, HIGH);
digitalWrite(redLED, LOW);
digitalWrite(greenLED, HIGH);
delay(100);

}
else{
digitalWrite(blueLED, LOW);

}
if((filter_output >= greenThresh) && (filter_output <= blueThresh))
{
digitalWrite(greenLED, HIGH);
digitalWrite(redLED, LOW);
digitalWrite(blueLED, LOW);
delay(100);

}
else{
digitalWrite(greenLED, LOW);
}

Serial.println(filter_output);

}

``````

Posts: 87337
Joined: Sat Feb 07, 2009 10:11 am

### Re: filtering analog data

You want to be doing the shift on the analog reading. This is not really a low-pass filter. This is just throwing away random bits.

Of course, after the shift the resulting value is divided by 8, so you will need to scale your thresholds accordingly.

Adding a time-domain filter is not a bad idea, but I think your first implementation was closer to what you want. Something like:

Code: Select all

`````` // subtract the last reading:
// advance to the next position in the array:
index = index + 1;
// if we're at the end of the array...

...``````
You might consider printing the inputs & outputs of the filter to aid in debugging.

zener

Posts: 4567
Joined: Sat Feb 21, 2009 2:38 am

### Re: filtering analog data

You might post an algorithym or flow chart.

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### Re: filtering analog data

Seems that no matter which way the sketch is tweaked, the odd ball reading sneaks in and makes the lights change state even when their is nothing but silence. I've seen somewhere a procedure that compares new data with old and if there is not close to the current figure, nothing happens. After an exhausting search, I can't find it. Would this eliminate the problem? A sample of the analog input is listed below the code.

Code: Select all

``````#define FILTER_SHIFT 1
int val;

int ledRed =12;
int ledYellow = 11;
int ledGreen = 13;

unsigned long starTime;
int flashRed = 20000;

int index = 0;                  // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average
int inputPin = 1;
void setup()
{
val = 0;
pinMode(ledRed,OUTPUT);
pinMode(ledYellow,OUTPUT);
pinMode(ledGreen,OUTPUT);
pinMode(inputPin, INPUT);

// initialize serial communication with computer:
Serial.begin(9600);
// initialize all the readings to 0:

starTime = millis();  //timer
}
void loop() {
// advance to the next position in the array:
index = index + 1;
// if we're at the end of the array...
// ...wrap around to the beginning:
index = 0;
// calculate the average:
delay(100);

starTime = millis();  //timer

// if  ((average >200) && (average < 300)) {  // code here will execute if average > 100

if (average > 342) {
digitalWrite(ledRed,HIGH);
digitalWrite(ledYellow,LOW);
digitalWrite(ledGreen,LOW);
delay(1000);
val= val +1;

if((val >8) && (starTime> flashRed )) {

digitalWrite(ledRed,HIGH);
delay(1000);
digitalWrite(ledRed,LOW);
delay(1000);
digitalWrite(ledRed,HIGH);
delay(1000);
digitalWrite(ledRed,LOW);
delay(1000);
digitalWrite(ledRed,HIGH);
delay(1000);
digitalWrite(ledRed,LOW);
delay(1000);
digitalWrite(ledRed,HIGH);
delay(1000);
digitalWrite(ledRed,LOW);
delay(1000);
if (val>8){
val = 0;
}
}

}
else if ((average > 332) && (average < 341)) {  // code here will execute if average <= 100 and average > 80

digitalWrite(ledRed,LOW);
digitalWrite(ledYellow,HIGH);
digitalWrite(ledGreen,LOW);
delay(1000);
}
else {  // code here will execute if average <= 80

digitalWrite(ledRed,LOW);
digitalWrite(ledYellow,LOW);
digitalWrite(ledGreen,HIGH);
delay (200);
}

// Serial.println(average, DEC);
Serial.println(average, DEC);
//val = 0;

}

``````
332

332

332

333

333

328

339

336

339

338

337

337

331

338

333

340

338

338

339

339

339

339

338

336

332

335

328

338

336

335

333

331

332

326

335

334

333

332

zener

Posts: 4567
Joined: Sat Feb 21, 2009 2:38 am

### Re: filtering analog data

The advice I have been given before is "Post the absolute smallest piece of code that exhibits the problem."

As for analog filtering, the top two methods used are:

Hardware RC filter
Running average (also known by another name I cannot think of...)

ahdavidson

Posts: 131
Joined: Wed Jun 03, 2009 9:59 am

### Re: filtering analog data

Running average (also known by another name I cannot think of...)
Isn't that one of the simplest of low-pass filters?

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### Re: filtering analog data

Is this the running average?

zener

Posts: 4567
Joined: Sat Feb 21, 2009 2:38 am

### Re: filtering analog data

An easier way is you have a counter and an analog-read total. Every time you take an analog read you add it to your total and increment the counter. At some point you divide the total by the counter and there is your average. Then you start over. That is a simple way that works for many applications.
Last edited by zener on Mon Nov 09, 2009 4:03 pm, edited 1 time in total.

Posts: 87337
Joined: Sat Feb 07, 2009 10:11 am

### Re: filtering analog data

Your analog values range from 326 to 340 (a range of 14). If that represents "silence" you have about 4 bits of noise.

What is the application?

keithg

Posts: 82
Joined: Thu Oct 30, 2008 8:30 pm

### Re: filtering analog data

We want a noise monitor for our cafeteria, which is near riot every lunch hour. Similar commercial units are about \$70 but we need one for each classroom if I can persuade the circuit/sketch to turn red, blue, and green LED's on when the noise exceeds their relative threshold.