Using Software Filter Libraries - I need advice

I have an experiment where I am simply using a pot to generate a voltage between 0-5 vdc where the results are displayed on 4 digits of a MAX7219.

It works fine but I would like to implement and experiment with a basic / simple low pass filter library to quiet down the units digit.

I have read about many of the available libraries but they all seem quiet complicated. I think my noise is mostly power supply based.

I know how to fix this with hardware but this experiment is to see if a "library based" solution may offer me a solution without changing the wiring.

Rather than just shotgun one of the numerous Filter Libraries out there, I was hoping to get a little advice here.

For what it's worth, I have included the working code I now have.

Thank You

//This test program reads a pot and displays it on the lower 4 digits of MAX7219

#define MAX7219_DIN 5
#define MAX7219_CS  6
#define MAX7219_CLK 7
#define analogPin A0

uint16_t val;           // variable to store analog value read

uint8_t digit_4;  // variable to use for digit 4
uint8_t digit_3;
uint8_t digit_2;
uint8_t digit_1;  // variable to use for digit 1

void initialise()
{
  digitalWrite(MAX7219_CS, HIGH);
  pinMode(MAX7219_DIN, OUTPUT);
  pinMode(MAX7219_CS, OUTPUT);
  pinMode(MAX7219_CLK, OUTPUT);
}

void output(byte address, byte data)
{
  digitalWrite(MAX7219_CS, LOW);
  shiftOut(MAX7219_DIN, MAX7219_CLK, MSBFIRST, address);
  shiftOut(MAX7219_DIN, MAX7219_CLK, MSBFIRST, data);
  digitalWrite(MAX7219_CS, HIGH);
}

void setup() {

  // Setup the MAX7219

  initialise();
  output(0x0f, 0x00); //display test register - test mode off
  output(0x0c, 0x01); //shutdown register - normal operation
  output(0x0b, 0x07); //scan limit register - display digits 0 thru 7
  output(0x0a, 0x0f); //intensity register - max brightness
  output(0x09, 0xff); //decode mode register - CodeB decode all digits
}

void loop() {

  // read the input pin
  val = analogRead(analogPin);

 // delay(1000);
 // val = val/4;

  //Create digits 1 thru 4 from val variable
  digit_4 = (val / 1000) % 10;
  digit_3 = (val / 100) % 10;
  digit_2 = (val / 10) % 10;
  digit_1 = (val % 10);

  // Write to digits 1 thru 4

  output(8, 15); //blank
  output(7, 15);
  output(6, 15);
  output(5, 15);
  output(4, digit_4);
  output(3, digit_3);
  output(2, digit_2 + 128); //decimal point +128
  output(1, digit_1); //digit 1, data LSD (right)

}

a simple way is to keep track of the past n values and always display the average
something like this (typed here, don’t know if it works)

const byte potPin = A0;

const size_t maxData = 10;
unsigned int potValues[maxData];
size_t potIndex;
unsigned long sum;

void getNextValue()
{
  // replace the value at index potIndex
  sum -= potValues[potIndex];
  potValues[potIndex] = analogRead(potPin);
  sum += potValues[potIndex];
  potIndex = (potIndex + 1) % maxData;
}

void setup()
{
  Serial.begin(115200);
  pinMode(potPin, INPUT);
  sum = 0;
  // initialize the array
  for (potIndex = 0; potIndex < maxData; potIndex++) {
    potValues[potIndex] = analogRead(potPin);
    sum += potValues[potIndex];
    delay(10);
  }
  potIndex = 0;
}

void loop()
{
  getNextValue();
  Serial.println(sum / maxData);
  delay(100);
}

If the problem is "flicker" on the last digit, perhaps the solution is to apply hysteresis (in this case stickiness) to stabilize it:

http://forum.arduino.cc/index.php?topic=526806.0

Hey 6V6gt! (I had to go back and ad the GT. I know what it is)

I like this approach. I use analog inputs to read a large number of signals on my projects. This experiment I am doing now is just a pot but I also have a fuel level gauge and a few others that would be assisted by such a technique.

I will give this some more study.

Thanks!

Greg

It depends on what you are trying to filter out and what you are trying to filter in. If there's a 60Hz mains hum on the line, then a notch filter can remove that frequency very effectively. If you're trying to get a rapid response to big changes but a slower response to small changes then a completely different filter is required.

I just love The Scientists and Engineers Guide to Digital Signal Processing It has a very simple description of the major types of filters and when is a good idea to use each type.

For your basic "smooth an analog reading from a pot" type of filtering, jump straight to chapter 19. JML's code above is an implementation of the recursive moving-average filter, explained in the last section of chapter 15. Chapter 21 gives you the clues on when to select each type of filter.

I love the reference to the above online book. Makes me want to buy a hard copy.

I also experimented with 6N6GT’s hysteresis method which I have working, but I need a larger range output and I don’t think I am smart enough to do that yet. The sample above reduces the A0 value to a single digit. I need a full 1024 (or about) value so I can ultimately calibrate the reading to something useful in a 3 digit range.

The averaging method is where I am now. I think I have implemented it properly as it seems to be working. I changed the sample to 20 times instead of 10 and took out some of the delay. I now only get a little running in the units digit which is not too bad. With a better power supply, I should be in pretty good shape.

Any comments on the implementation of the technique would be appreciated.

// This test program reads a pot 20 times then takes an average and displays
// it on the lower 4 digits of MAX7219

#define MAX7219_DIN 5
#define MAX7219_CS  6
#define MAX7219_CLK 7
#define analogPin A0
const size_t maxData = 20;
unsigned int potValues[maxData];
size_t potIndex;
unsigned long sum;

uint8_t digit_4;  // variable to use for digit 4
uint8_t digit_3;
uint8_t digit_2;
uint8_t digit_1;  // variable to use for digit 1

void getNextValue()
{
  // replace the value at index potIndex

  sum -= potValues[potIndex];
  potValues[potIndex] = analogRead(analogPin);
  sum += potValues[potIndex];
  potIndex = (potIndex + 1) % maxData;
}

void initialise()
{
  digitalWrite(MAX7219_CS, HIGH);
  pinMode(MAX7219_DIN, OUTPUT);
  pinMode(MAX7219_CS, OUTPUT);
  pinMode(MAX7219_CLK, OUTPUT);
}

void output(byte address, byte data)
{
  digitalWrite(MAX7219_CS, LOW);
  shiftOut(MAX7219_DIN, MAX7219_CLK, MSBFIRST, address);
  shiftOut(MAX7219_DIN, MAX7219_CLK, MSBFIRST, data);
  digitalWrite(MAX7219_CS, HIGH);
}

void setup() {

  // Setup the MAX7219

  initialise();
  output(0x0f, 0x00); //display test register - test mode off
  output(0x0c, 0x01); //shutdown register - normal operation
  output(0x0b, 0x07); //scan limit register - display digits 0 thru 7
  output(0x0a, 0x0f); //intensity register - max brightness
  output(0x09, 0xff); //decode mode register - CodeB decode all digits
  sum = 0;

  // initialize the array

  for (potIndex = 0; potIndex < maxData; potIndex++) {
    potValues[potIndex] = analogRead(analogPin);
    sum += potValues[potIndex];
    delay(1);
  }
  potIndex = 0;
}

void loop() {

  getNextValue();

  //Create digits 4 thru 1 from (sum / maxData) variable

  digit_4 = ((sum / maxData) / 1000) % 10;
  digit_3 = ((sum / maxData) / 100) % 10;
  digit_2 = ((sum / maxData) / 10) % 10;
  digit_1 = ((sum / maxData) % 10);

  // Write to digits 1 thru 4

  output(8, 15); //blank
  output(7, 15);
  output(6, 15);
  output(5, 15);
  output(4, digit_4);
  output(3, digit_3);
  output(2, digit_2 + 128); //decimal point +128
  output(1, digit_1); //digit 1, data LSD (right)

  delay(100);

}

For simple averaging of numbers that show random noise variations, the standard deviation is reduced by sqrt(N), where N is the number of samples averaged.

So, if the value currently displayed shows a variation of up to +/- 4 in the last digit, this should be reduced to +/- 1 if you average 16 samples. To put it another way, the noise is reduced by a factor of 10 if you average 100 samples.

If you try to average thousands of samples per second then you will find that getNextValue() will be a big drag on your performance.

The % operator uses division which is very slow on AVR processors. I have not checked what size_t will resolve to but it may be only 8 bits which won't be stupidly slow.

But if you find you need it to work faster, checking a greater-than will be faster than division.

The % operator is way way faster than a single call to analogRead(), so that's completely irrelevant(!)

I think the same is also true for using floats and single-pole digital filter.

If you worry about the speed of the modulo operator (%), just use a sample count that is a power of 2 (like 8 or 16 instead of 10). The compiler can optimize that to one or two instruction cycles.