How to stabilze a pot read

I am trying to use analogRead to output 0-63 from a pot. There is some jitter in the read that frequently causes the output to change even when the pot is not being turned. I only want it to output a new number when the pot is turned.

The following code was an attempt to dampen the jitter, but it doesn't work when the jitter is at the boundary of a divide by 16.

Suggestions?

int num, oldNum;

void setup() {
  Serial.begin(9600);
}

void loop() {
  oldNum = num;
  num = analogRead(A3);
  if (num / 16 != oldNum / 16)
  {
    Serial.print(num / 16);
  }
}

Rather than comparing exactly, give yourself an bit of range, +- something. .

Google “hysteresis”.

I like to use a software low-pass filter for this sort of thing, rate limited to get a consistent read:

unsigned sampleTime = 0; // don't need a long for this
float filteredReading;
int oldNum;

void loop() {
  int num = oldNum; // default

  // #1 - rate-limit the sample. Take a sample every 4ms
  unsigned sampleNow = (millis() >> 2); // sample every 4 ms
  if(sampleNow != sampleTime) {
    sampleTime = sampleNow;

    // #2 - low-pass filter.
    filteredReading = filteredReading * .75f + analogRead(A3) * .25f;

    // #3 - hysteresis of .25
    if(filteredReading < oldNum - .25f || filteredReading > oldNum + 1.25f) {
      num = (int) filteredReading;
    }
  }

  
  // blah blah blah - if newnum <> oldNum etc;


  oldNum = num;
}

Put a 1k resistor between pot wiper and analog in pin, a 0.1uF capacitor from analog in pin to GND, and try this sketch.

int total, results;
byte ainPin = 0;


void setup() {
  Serial.begin(9600);
  for(byte i = 0;i < 8;i++)
    total += analogRead(ainPin);
}

void loop() {
  total -= (total / 8);
  total += analogRead(ainPin);
  results = total / 128;
  Serial.println(results);
  delay(500);
}

PaulMurrayCbr:
Google “hysteresis”.

I like to use a software low-pass filter for this sort of thing, rate limited to get a consistent read:

unsigned sampleTime = 0; // don't need a long for this

float filteredReading;
int oldNum;

void loop() {
 int num = oldNum; // default

// #1 - rate-limit the sample. Take a sample every 4ms
 unsigned sampleNow = (millis() >> 2); // sample every 4 ms
 if(sampleNow != sampleTime) {
   sampleTime = sampleNow;

// #2 - low-pass filter.
   filteredReading = filteredReading * .75f + analogRead(A3) * .25f;

// #3 - hysteresis of .25
   if(filteredReading < oldNum - .25f || filteredReading > oldNum + 1.25f) {
     num = (int) filteredReading;
   }
 }

// blah blah blah - if newnum <> oldNum etc;

oldNum = num;
}

Here is how I implemented this suggestion but it is still pretty erratic.

unsigned sampleTime = 0; // don't need a long for this
float filteredReading;
int newnum, oldNum;

void setup()
{
  Serial.begin(9600);
}

void loop() {
  newnum = oldNum; // default
  // #1 - rate-limit the sample. Take a sample every 4ms
  unsigned sampleNow = (millis() >> 2); // sample every 4 ms
  if (sampleNow != sampleTime)
  {
    sampleTime = sampleNow;
    //   Serial.println("hi");
    // #2 - low-pass filter.
    filteredReading = filteredReading * .75f + analogRead(A3) * .25f;

    // #3 - hysteresis of .25
    if (filteredReading < oldNum - .25f || filteredReading > oldNum + 1.25f) {
      newnum = (int)filteredReading;
    }
  }
  if (newnum != oldNum)
    Serial.println(newnum);
  oldNum = newnum;
}

PickyBiker:
Here is how I implemented this suggestion but it is still pretty erratic.

Now that I think about it: try making the filter a bit more agressive. The filter currently passes .25 of the fresh input through, which means that one read is enough to trip the hysteresis. Maybe dial back the incoming sample to .1

    filteredReading = filteredReading * .9f + analogRead(A3) * .1f;

You could also make the hysteresis more agressive.

    if (filteredReading < oldNum - .75f || filteredReading > oldNum + 1.75f) {
      newnum = (int)filteredReading;
    }

Heck, if you are dividing by 4 to get a value from 0-255,

    if (filteredReading < oldNum - 2 || filteredReading > oldNum + 3) {
      newnum = (int)filteredReading;
    }

This may mean that noise might make it skip a number … but it would be pretty unlikely.

PickyBiker: I am trying to use analogRead to output 0-63 from a pot. There is some jitter in the read that frequently causes the output to change even when the pot is not being turned. I only want it to output a new number when the pot is turned.

The following code was an attempt to dampen the jitter, but it doesn't work when the jitter is at the boundary of a divide by 16.

Suggestions?

int num, oldNum;

void setup() {   Serial.begin(9600); }

void loop() {   oldNum = num;   num = analogRead(A3);   if (num / 16 != oldNum / 16)   {     Serial.print(num / 16);   } }

While you have received valid suggestions about adding a hardware or software filter to your analog signal, you have a pretty simple bug in your original code.

Dividing by 16 is going to result in values where 1 bit of jitter will still cause the if statement to be executed. For example, if your analog value is alternating between 15 and 16 you don't want it to run, but 15/16 = 0 where 16/16 = 1.

You can fix the code this way, so that only a change of 16 or greater results in the if statement executing:

int num, oldNum;

void setup() {
  Serial.begin(9600);
}

void loop() {
  oldNum = num;
  num = analogRead(A3);
  if (abs(num - oldNum) >= 16)
  {
    Serial.print(num);
  }
}

BigBobby: While you have received valid suggestions about adding a hardware or software filter to your analog signal, you have a pretty simple bug in your original code.

Dividing by 16 is going to result in values where 1 bit of jitter will still cause the if statement to be executed. For example, if your analog value is alternating between 15 and 16 you don't want it to run, but 15/16 = 0 where 16/16 = 1.

You can fix the code this way, so that only a change of 16 or greater results in the if statement executing:

int num, oldNum;

void setup() {   Serial.begin(9600); }

void loop() {   oldNum = num;   num = analogRead(A3);   if (abs(num - oldNum) >= 16)   {     Serial.print(num);   } }

That doesn't seem to work at all because turning the pot doesn't make a large enough change to between oldnum and num.

PickyBiker: That doesn't seem to work at all because turning the pot doesn't make a large enough change to between oldnum and num.

Heh...OK, sure...it may be tough to turn the pot faster than the code can read it. Change it this way then, so that you need the pot to turn 16 from the last time you recognized it as new pot value.

int oldNum;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int num = analogRead(A3);
  if (abs(num - oldNum) >= 16)
  {
    Serial.print(num);
    oldNum = num;
  }
}

BTW - What is the intention of this pot? You could potentially make this code very clean using an enum value.

Also, what is the circuit for your pot? If it's something like a 100kΩ from Vcc to Gnd with the Wiper connected to an analog pin, then you should expect a lot of variation in your reading. You'd be best off adding a HW filter.

Ok, here’s what works so far. It is not perfect, but it is very good.

I added a hw filter: 1k resistor between the wiper and the input pin and a .1uf cap from the input pin to ground. Then I added a 10 read average to smooth out the bumps in the reading. As I said, it isn’t perfect, but it is very good.

The purpose of this is to use a pot to adjust the volume digitally on a radio Si4730. That radio uses digital volume levels of 0-63.

Here is the final code (so far):

int num, oldnum;
#define cnt 10

void setup()
{
  Serial.begin(9600);
}

void loop() {
  oldnum = num;
  num = 0;
  for (int i = 0; i < cnt; i++)
    num += analogRead(3);
  num /= cnt;
  num /= 16;
  if (num  != oldnum)
  {
    Serial.println(num);
  }
}

I see. Well, that explains why you have the / 16 then.

I imagine your final code is much improved over your original post. You might have a little variation exactly at the point where your pot crosses from one multiple of 16 to another, but with 10 samples you probably see very little.

If your code is working to your satisfaction just go with it. If you want to simplify it a little, you could probably reduce the amount of filtering you do.