How to read stable values from potentiometers.

Hello everyone, hope you are all fine. I have started building some midi controllers, in some of those using potentiometers, but the values I am getting are not stable, not even when I am not touching the pots. Does anyone knows a way to read stable values from potentiometes? Is it about Hardware or Software?

akiskaps:
Hello everyone, hope you are all fine. I have started building some midi controllers, in some of those using potentiometers, but the values I am getting are not stable, not even when I am not touching the pots. Does anyone knows a way to read stable values from potentiometes? Is it about Hardware or Software?

Since you have not given a schematic, nor provided any code that demonstrates the problem,, I can only guess you are not using a stable power supply. Remember the Arduino compares your potentiometer/voltage divider voltage to the voltage powering the Arduino and reports to difference.

Paul

How many potentiometers?

Which ADC are you using?

Which voltage reference are you using?

How much variation of the ADC reading are you seeing from the potentiometers?

How much variation of the ADC reading are you seeing from the voltage reference (when connected to the ADC)?

I am sorry that I didnt provided anything. I simply use analogRead and devide the value by 8.Then I check if the previous value of the potentiometer has changed and only then i send the data. But some times the the midi debugger prints the same value continuously or it is slightly changing.
I am using a 9V battery to power my circuit through arduino uno since i upgraded my midi to communicate through bluetooth. But when I used it over usb(powered by pc,that i think the power is constant) the same problem appers. I am using only one potentiometer as I am experimenting with the whole thing, in the futute I am gonna use ten or more pots.
Thank you both for your response.

Still need full details !!

hammy:
Still need full details !!

Ok, what exactly should I post? The code is really simple as I said I just use analogRead and devide the value by 8 and when the value I read is different than the previous I send it.Furthermore I have not make any schematic as it is so simple, I am just using one potentiometer. Thank you for your fast response.

P.S. I can suspect only one think. The delay between every reading. I use delay(5).

akiskaps:
Ok, what exactly should I post? The code is really simple as I said I just use analogRead and devide the value by 8 and when the value I read is different than the previous I send it.Furthermore I have not make any schematic as it is so simple, I am just using one potentiometer. Thank you for your fast response.

P.S. I can suspect only one think. The delay between every reading. I use delay(5).

Can you share the value of the potentiometer and how you have it wired?

Paul

akiskaps:
Ok, what exactly should I post? The code is really simple as I said I just use analogRead and divide the value by 8 and when the value I read is different than the previous I send it.Furthermore I have not make any schematic as it is so simple, I am just using one potentiometer. Thank you for your fast response.

P.S. I can suspect only one think. The delay between every reading. I use delay(5).

Post your code, no matter how simple. delay(5) already sounds like you are making problems for yourself.

Dividing by 8 is a valid method of reducing the data but you've already seen that it can bounce up and down by a few points just due to external noise. What if those few points cross an 8-point boundary? Then it will flick up and down by a big step even though the noise is small.

A moving average is the simplest and most effective way of reducing the noise. If you save the last 3 values of your divide-by-8 result, you might get 7, 7, 8. What is the correct output? Obviously 7. (Not 7.3 - we round off to an integer after taking the average.) Since the Arduino works really fast, it is equally feasible to store the last 30 or 300 values.

Here's a very simple class that I use in a lot of places. I never published it as a library because it's actually part of a larger library I wrote myself...

class Average {
  public:
    void setStrength(int numSamples) {
      //should be called rarely, like once in setup and maybe once if the configuration changes
      //call begin() after this to re-populate the array and reset the current sample
      //numSamples should be a number like 10 or 100
      if (numSamples <= 0) return;
      _value = (int*)realloc(_value, numSamples * sizeof(int));
      _numSamples = numSamples;
      _numSamples_2 = numSamples / 2;
    }
    void begin(int firstValue) {
      if(_value==NULL) setStrength(1); //If you forgot to set the strength, default strength = no averaging
      _currentTotal = 0;
      for (int i = 0; i < _numSamples; i++) {
        _value[i] = firstValue;
        _currentTotal += firstValue;
      }
      _currentSample = 0;
    }
    void update(int sample) {
      if (_numSamples < 1) return;
      if (_currentSample >= _numSamples) _currentSample = 0;
      _currentTotal += sample - _value[_currentSample];
      _value[_currentSample] = sample;
      _currentSample++;
      return;
    }
    int value() {
      return (_currentTotal + _numSamples_2) / _numSamples;
    }
    Average& operator=(int sample) {
      //allows us to use the assignment filterInstance=x; to update the filter
      update(sample);
      return *this;
    }
    operator int() {
      //allows us to return a default value, for the construction x=filterInstance;
      return value();
    }
  private:
    int _numSamples = 0;
    int _numSamples_2 = 0;
    int* _value = NULL;
    int _currentSample = 0;
    int _currentTotal = 0;
    int _rawValue = 0;
};

You use it like this:

Average myAvg;
const int inputPin = A0; 

void setup() {
 myAvg.setStrength(30);  //use a larger number for "stronger" averaging. Watch out you don't use all your memory if you are on a smaller Arduino like an UNO
 myAvg.begin(analogRead(inputPin));
 Serial.begin(9600);
 while(!Serial && millis()<5000){
   //on native USB Arduinos, wait for a USB serial connection
 }
 Serial.println("Beginning averager");
}

void loop() {
 const unsigned long inputDataPeriod = 5; //milliseconds; only take a sample from the input this often
 static unsigned long lastDataInput = millis();
 const unsigned long outputDataPeriod = 50; //milliseconds; only print to the output this often
 static unsigned long lastDataOutput = millis();
 static int inputValue;

 //read the input on the defined frequency and store in the averager
 if(millis() - lastDataInput > inputDataPeriod) {
   lastDataInput += inputDataPeriod;

   inputValue = analogRead(inputPin);
   myAvg.update(inputValue);  //may also be written as myAvg = inputValue;
  }

  //output the average and the last instantaneous reading on the defined frequency
  if(millis() - lastDataOutput > outputDataPeriod) {
   lastDataOutput += outputDataPeriod;

   Serial.print(inputValue);
   Serial.print(',');
   Serial.print(myAvg);   
   Serial.println();
  }
}

Run this with the Serial Plotter to observe the output. It doesn't attempt to print every input value but by printing the most-recent, you will get some idea of the randomness of the input.

With A/D conversion there is ALWAYS a CHANCE of being on the "hairy edge" between two values and you get unstable readings. Dividing by 8 will reduce the odds of that happening, but it will not eliminate it. The Arduino ADC should be stable +/- 1 bit as long as the reference is stable. If you use the default Vcc reference and Vcc as your pot-supply voltage, that should also give you stable readings (+/- 1 bit).

Longer wires, unshielded wires, and higher impedance pots will be more prone to noise pickup.

A capacitor between the analog input and ground will tend to "smooth" the noise and/or you can use [u]software smoothing[/u], but neither of those will solve the "hairy edge" issue.

You can (usually) completely stabilize the readings by ignoring small changes. That usually requires some "timing" so after you make an actual pot movement the sensitivity comes back for awhile allowing fine-adjustments. Then it can "lock-up" again after the readings mostly-stabilize.

Reading 10kB (linear) pots with the A/D of an Uno should not give any jumping values.
Well, maybe +/- one value.

The problem is in your code or in your hardware. Need to see both.
Is it related to this post.
Read the "How to post" sticky.
Leo..

akiskaps:
but the values I am getting are not stable, not even when I am not touching the pots.

You probably need to explain what you mean by 'not stable'. Give examples of values. Show some results.

I've been struggling with the same problem for a long time as well. My solution was two-fold: 1) Filter the signal using a low-pass EMA filter, 2) Apply hysteresis to the filtered signal.

My MIDI Control Surface library now supports this out of the box.

If you're willing to check out the libraries, you could start here: Control Surface: Getting Started
If you don't want to use the libraries, you can check the source code and documentation here:

Pieter


Edit August 2023: update broken links