Smoothing out noise in analog sensor data

Many analog sensors create signals that carry a lot of noise. This can be seen with LDR (Light Dependent Resistors), ultrasonic distance sensors and many more. To deal with this noise, one method is to use digital (software logic) filters.

There are many different kinds of filters, some that take a lot of processing, some that take less. In the example below, the filter is an IIR (infinite impulse response) and is quite efficient.

The below link shows how to install and use one implementation of IIR filters:

http://playground.arduino.cc/Code/Filters

While the above example is powerful this simple smoothing exercise is easy to understand and perhaps provides a good example to explore strategies for simple filtering in readily readable code and uses fairly fundamental structures. The code is documented/commented to make it easier for a beginner to read and understand.

//libraries required for running screen

#include <Wire.h> 
#include <Adafruit_GFX.h> 
#include <SPI.h> 
#include <Adafruit_SSD1306.h> 

Adafruit_SSD1306 display(4); // Resets the screen - i've removed usual #define.

#define filterSamples 13 // defines the number of samples that we will collect for digitalSmooth function


int sensSmoothArray1[filterSamples]; //this is an array set up to hold a number of samples for filtering- according to #define above
int photocellReading = 0; // variable to hold raw sensor reading
int smoothData1; // variable to hold filtered reading

// the setup function is called once when the program first runs

void setup() { 

 display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // sets up the display - these commands are specific to the screens driver
 // set text size
 display.setTextSize(1);
 // set the text color - we only have a choice of black or white for this display
 display.setTextColor(WHITE);
 //set the way the text behaves when it gets to the end of the line
 display.setTextWrap(1);
 // the screen needs to be cleared before drawing anything new.
 display.clearDisplay();
 
}
 

//end of the Setup funciton.

// the loop funciton plays continously follwoing setup.


void loop() { 
 
 
 display.clearDisplay(); // clear the display before writing new 'frame' of information
 display.setCursor(0, 0); // set the cursor back to the top corner of the screen
 photocellReading = analogRead(1); // read the analog pin 1 and assign it to the variable photocellReading 

display.println(photocellReading); // write the raw sensor value to the screen's buffer
smoothData1 = digitalSmooth(photocellReading, sensSmoothArray1); // call the function digitalSmooth() sending it the sensor value and the the array via a pointer
 
display.println(smoothData1); // display the smoothed data
 display.display(); //write the screen buffer to the screen
}


 // define a function digitalSmooth() with 2 parameters : an integer to hold the raw sensor read and *sensSmoothArray which is a 'pointer' to an array - this is used to pass the array values to the function

int digitalSmooth(int rawIn, int *sensSmoothArray){ 
 int j, k, temp, top, bottom; // declare the function's int variables.
 long total; // a long floating point variable which will hold the averaged results
 static int i; // this declares a static integer variable - a static variable holds its value across mutliple function calls
 static int sorted[filterSamples]; // declaring a static (see above) array called 'sorted' with the number of elements defined by 'filterSamples;
 boolean done; // declares a boolean variable neamed 'done'

// the line below is very useful and worth getting your head around - its used to count passes until the limit filterSamples 
// the modulo operator % returns the remainder of the first value divided by the second value
// - when the first value is less than the second we get a remainder of the first value
// - when the two value are equal we get a remiander of zero... 

 i = (i + 1) % filterSamples; // increment counter and uses modulo operator to see if the increment is equal to 'filterSamples' - if it is filterSamples will return to zero
 
 // the next line uses the number i defined above to put the 'rawIn' (sensor value) int the next available array address (i is the array address)
 
 sensSmoothArray[i] = rawIn; // input new data into the oldest slot


 for (j=0; j<filterSamples; j++){ // transfer data array into another array for sorting and averaging
 sorted[j] = sensSmoothArray[j];
 }

 done = 0; // flag/switch to know when we're done sorting 
 while(done != 1){ // simple swap sort, sorts numbers from lowest to highest
 done = 1;
 for (j = 0; j < (filterSamples - 1); j++){
 if (sorted[j] > sorted[j + 1]){ // numbers are out of order - swap
 temp = sorted[j + 1];
 sorted [j+1] = sorted[j] ;
 sorted [j] = temp;
 done = 0;
 }
 }
 }


 // throw out top and bottom 15% of samples - limit to throw out at least one from top and bottom
 bottom = max(((filterSamples * 15) / 100), 1); 
 top = min((((filterSamples * 85) / 100) + 1 ), (filterSamples - 1)); // the + 1 is to make up for asymmetry caused by integer rounding
 k = 0;
 total = 0;
 for ( j = bottom; j< top; j++){
 total += sorted[j]; // total remaining indices
 k++; 
 // Serial.print(sorted[j]); 
 // Serial.print(" "); 
 }

 return total / k; // divide by number of samples
}