Analog Input Filtering

The performance of this data filter is very good, and you can easily use it on multiple sets of data (analog inputs).  


/*
  Display the Serial Plotter after running this code.
  
*/

/////////////////////////////////////////////////////////////////////////
const byte pinBuiltInLED = 13;
//////////////////////////////////////////////////////////////////////////////
// Exponential Filter
// https://github.com/mrkaleArduinoLib/gbj_filter_exponential
// Includes dependencies for the Particle and Arduino platform.
#include "gbj_filter_exponential.h"
// Smooth Factor parameter below is within the range of 0.0 to 1.0
// 0.623  the sampling time interval is equal to the time constant
// 0.5    regular running average
// 0.2    the sampling time interval ~4.5 times shorter than the time constant.
// 0.1    the sampling time interval ~9.5 times shorter than the time constant.
// 0.01   the sampling time interval ~100 times shorter than the time constant.
gbj_filter_exponential FilterA0 = gbj_filter_exponential(0.5);
gbj_filter_exponential FilterA1 = gbj_filter_exponential(0.5);
gbj_filter_exponential FilterA2 = gbj_filter_exponential(0.5);
gbj_filter_exponential FilterA3 = gbj_filter_exponential(0.5);
gbj_filter_exponential FilterA4 = gbj_filter_exponential(0.5);
gbj_filter_exponential FilterA5 = gbj_filter_exponential(0.5);
/////////////////////////////////////////////////////////////////////////
const byte timerSampleInterval = 1;  
unsigned long timerSampleLast = 0;  // timer
const unsigned long timerPublishInterval = 5000;  
unsigned long timerPublishLast = 0;  // timer
unsigned long publishCount = 0;
const byte ADCbits = 10;  // Arduino Uno is 10 bits
unsigned long samples = 0;

unsigned int A0Fil = 0;
double A0mV = 0.0;

unsigned int A1Fil = 0;
double A1mV = 0.0;

unsigned int A2Fil = 0;
double A2mV = 0.0;

unsigned int A3Fil = 0;
double A3mV = 0.0;

unsigned int A4Fil = 0;
double A4mV = 0.0;

unsigned int A5Fil = 0;
double A5mV = 0.0;
/////////////////////////////////////////////////////////////////////////


void setup () {
  pinMode(pinBuiltInLED, OUTPUT);

  Serial.begin(9600);
  while (!Serial) {
    delay(1);
  }
  
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(A3, INPUT);
  pinMode(A4, INPUT);
  pinMode(A5, INPUT);
  delay(1);
 
  // Setup serial plotter
  Serial.print("A0"); Serial.print(" ");
  Serial.print("A1"); Serial.print(" ");
  Serial.print("A2"); Serial.print(" ");
  Serial.print("A3"); Serial.print(" ");
  Serial.print("A4"); Serial.print(" ");
  Serial.println("A5");

  // About the Exponential library.
  //Serial.println("Libraries:");
  //Serial.println(gbj_filter_exponential::VERSION);
  //Serial.println("---");
  //Serial.print("Smoothing factor: ");
  //Serial.println(Filter.getFactor(), 4);

  timerPublishLast = millis();
  timerSampleLast = millis();
} // setup()


void loop() {
  
  if (timerSampleLast > millis())  timerSampleLast = millis();
  if ((millis() - timerSampleLast) > timerSampleInterval) {
    ReadAnalogInputs();
    timerSampleLast = millis();
  } // timer
  
  if (timerPublishLast > millis())  timerPublishLast = millis();
  if ((millis() - timerPublishLast) > timerPublishInterval) {
    CalcPlotAvgOfInputs();
    timerPublishLast = millis();
  } // timer
  
} // loop()


////////////////////////////////////////////////////////////////

void ReadAnalogInputs() {
    A0Fil += FilterA0.getValue(analogRead(A0));
    A1Fil += FilterA1.getValue(analogRead(A1));
    A2Fil += FilterA2.getValue(analogRead(A2));
    A3Fil += FilterA3.getValue(analogRead(A3));
    A4Fil += FilterA4.getValue(analogRead(A4));
    A5Fil += FilterA5.getValue(analogRead(A5));
    samples++;
} // ReadAnalogInputs()


void CalcPlotAvgOfInputs() {
    // Calculate the average ADC values and update global double
    // variable A0mV .. A5mV.

    publishCount++;
    
    A0mV = ((A0Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A0Fil = 0;

    A1mV = ((A1Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A1Fil = 0;

    A2mV = ((A2Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A2Fil = 0;

    A3mV = ((A3Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A3Fil = 0;

    A4mV = ((A4Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A4Fil = 0;

    A5mV = ((A5Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A5Fil = 0;

    // Ignore the first value calculated from the first 2461 samples
    if (publishCount > 1) {
      Serial.print(A0mV); Serial.print(" ");
      Serial.print(A1mV); Serial.print(" ");
      Serial.print(A2mV); Serial.print(" ");
      Serial.print(A3mV); Serial.print(" ");
      Serial.print(A4mV); Serial.print(" ");
      Serial.println(A5mV);
    }
    
    samples = 0;  
} // CalcPlotAvgOfInputs()

Filtering performance of this library is fantastic.   Unfortunately, you cannot apply the filtering to more than one set of data (some sort of crossover in the code causes one data set to affect the other).  


//////////////////////////////////////////////////////////////////////////////
// Microsmooth - a lightweight and fast signal smoothing library
// https://github.com/AsheeshR/Microsmooth
// https://gist.github.com/asheeshr/bc87f8c6486f649ef029
// WARNING:  You can only use this filter on ONE set of values (analog input).
// WARNING:  Do not use delay() anywhere, it will affect the analogRead();
// Simple Moving Average (SMA)
// Cumulative Moving Average (CMA)
// Exponential Moving Average (EMA)
// Savitzky Golay Filter (SGA)
// Ramer Douglas Peucker Algorithm (RDP)
// Kolmogorov Zurbenko Algorithm (KZA)
#include <microsmooth.h>
uint16_t *A0hist;
/////////////////////////////////////////////////////////////////////////
const byte pinBuiltInLED = 13;
/////////////////////////////////////////////////////////////////////////
const byte timerSampleInterval = 1;  
unsigned long timerSampleLast = 0;  // timer
const unsigned long timerPublishInterval = 5000;  
unsigned long timerPublishLast = 0;  // timer
unsigned long publishCount = 0;
const byte ADCbits = 10;
unsigned long samples = 0;
unsigned int A0Fil = 0;
double A0mV = 0.0;
/////////////////////////////////////////////////////////////////////////


void setup () {
  pinMode(pinBuiltInLED, OUTPUT);

  Serial.begin(9600);
  while (!Serial) {
    delay(1);
  }
  
  pinMode(A0, INPUT);
  delay(1);
  
  // Microsmooth
  // SMA CMA EMA SGA RDP KZA
  A0hist = ms_init(SGA);
  if (A0hist == NULL ){
    Serial.println("ERROR - insufficient memory for Microsmooth filter");
    while(1) { blinkERR(pinBuiltInLED); }
  }
  
  // Setup serial plotter
  Serial.println("A0");

  timerPublishLast = millis();
  timerSampleLast = millis();
} // setup()


void loop() {
  
  if (timerSampleLast > millis())  timerSampleLast = millis();
  if ((millis() - timerSampleLast) > timerSampleInterval) {
    ReadAnalogInputs();
    timerSampleLast = millis();
  } // timer
  
  if (timerPublishLast > millis())  timerPublishLast = millis();
  if ((millis() - timerPublishLast) > timerPublishInterval) {
    CalcPlotAvgOfInputs();
    timerPublishLast = millis();
  } // timer
  
} // loop()


////////////////////////////////////////////////////////////////

void ReadAnalogInputs() {
    //A0Fil += analogRead(A0);
    A0Fil += sma_filter(analogRead(A0), A0hist);
    samples++;
} // ReadAnalogInputs()


void CalcPlotAvgOfInputs() {
    // Calculate the average ADC values and update global double
    // variable A0mV .. A5mV.

    publishCount++;
    digitalWrite(pinBuiltInLED, HIGH);
    
    A0mV = ((A0Fil / samples) * (5000.0 / (double(pow(2,ADCbits)))))*1.0/1.0;
    A0Fil = 0;

    // Ignore the first value calculated from the first 2461 samples
    if (publishCount > 1) {
      Serial.println(A0mV);
    }
    
    digitalWrite(pinBuiltInLED, LOW);
    
    samples = 0;  
} // CalcPlotAvgOfInputs()


void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} //blinkLED()


void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()

Other Filters Not Tested

gbj_filter_smoothing

Running Median

Filter by Karlward - filter & also calc mean, max, min, stdDev

LibSimpleFilters - ButterWorth, Median, Moving Avg, High & Low Pass

Smoothing

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.