A/D value to 7 segments - Volume Control

Hi everyone!

I'm using Arduino UNO to programming a volume control reading from a potentiometer.

Basically I reading the voltage from a potentiometer connected directly in A/D of Arduino.

So 5V is 1023 -> Max. volume -> 100k ohms
0V is 0 -> Min. Volume -> 0 ohms

I convert the value to resistance and I will show the value in the 7 segments display. So:

Reading 1000 -> show 01 - represent 1% of volume
reading 2000 -> show 02 - represent 2% of volume
reading 100000 - > show 99 - represent 100% of volume

When the user rotate the potentiometer, the display change according.

I take the A/D reading and plot direct to 7 segments display.

The big problem is a histerese between the next value. For exemplo.
A/D reading


The 7 segments change fast between 7 and 8. I need solve this problem to fix 8 in this case, no change. Because I don't want this effect to the user.

I try make average of value, but the problem continue... return average for 8 and 7... variation continue.

How can I solve this problem, to read A/D and always show the value in display without histerese?

Please, I need some suggested in this case.

Many Thanks!

Hysteresis is something different, you mean fluctuations or noise?
Simplest solution is a RC low pass filter before the ADC.

How many segments do you want to use for the full scale display?
Which scale function (lin, log...)?

Against flicker you can use any sliding average algorithm.

Any A/D measurement is always + or - 1 least significant bit. Your raw reading is between 0 and 1023, so displaying something in the 8000 range is not going to give you a reading of all possible values, because the smallest change in the reading is reflected in at least a change of 8.

Putting a capacitor between your pot's wiper and ground will help, but you will never get a stable reading for all positions of the pot because sometimes you are just on the edge. Using a 10K pot instead of a 100K pot is also going to help, as it will pick up less noise.

Basically I think you have too much expectations of how things in the real world work.

Often called a running avrage. Even that, in theory, will not stop the readings alternating.

You can only display when the change between the current reading and the last one is greater than a certain threshold. Subtract the current reading from the last displayed value and use the abs function to see if this threshold is passed.

The other thing is that the pot is linear but our hearing is not, therefore your display will not reflect the change in volume the user hears.

I think the problem is the moment the value change to next number.

I'm usgin a log potentiometer. After solve the display show I will work in a anti-log curve to plot to the display.... firts I need solve this problem.

I try sliding average algorithm but always the potentiometer can stop in an point the number change to next number...

I think is not a noise... is the 10 bit resolution of A/D

I think there is a language translation problem here. Try expressing it in your native language and let Google translate it for you.

Because, at the moment, the behaviour of the device seems completely normal. So, "the problem" is in some aspect of the behaviour that you haven't been able to communicate clearly.

Either hysteresis, or filtering, or both, can reduce the jitter on the display, but if there are specific limitations to using those with your device, you have to explain what they are.

Yes and no :wink:

"Histeresis" was used in the wrong way by the TO, it had suppressed flicker if applied.

Most probably I mixed up sliding window and running average.

With plot I associate a bar graph, not a numeric display.

The rest is typical A/D conversion stuff.

For better undestanding...

Follow the code... in resume. The funciton I use to convert and plot values.


float ResValue(int val){
return val*(100000.0/1023.0);


int DisplayValue(int val){

if(val <= 1000.00){ // SHOW 00
return 0;

if(val > 1000.00 && val < 2000.00){ // SHOW 01
return 1;
if(val >= 2000.00 && val < 3000.00){ // SHOW 02
return 2;
if(val >= 3000.00 && val < 4000.00){ // SHOW 03
return 3;
if(val >= 4000.00 && val < 5000.00){ // SHOW 04
return 4;
............ continue to SHOW 99...

When I plot the value, following my DisplayValue() function, I can always have, depending on the position of the potentiometer, values read between 1999 and 2000 for example and the display will be changing between 01 and 02.

OK! Isn't that hysteresis? Would it solve stability with RC filter? I do not think so. It is due to the reading resolution...

The usual word is "jitter".. it is NOT hysteresis. Hysteresis will CANCEL jitter...

Again, you are objecting to filtering without explaining why... we need to know the reason.

If you have low pass filtering (averaging), there will be jitter but it will be slower. That would make it less noticeable. If you have hysteresis, the jitter can be completely eliminated in most cases.

In all cases, a digital conversion of an analog reading will be truncated. That's because the resolution of an analog signal is infinite, but the digital conversion is finite.

If the conversion has insufficient resolution, you need to use A/D hardware that produces more bits per sample.

Why not simply
return trunc(val / 1000);

I still suspect that you don't understand hysteresis and running average :frowning:

Because, when the value of potentiometer is for example 1560.00.. will return 1.5

I can't show 1.5 in the display. I need 01 and than 02...

I need check the range to show 1% (01), 2% (02), 3% (03)... I have only two digits.

trunc(/1000) of 1560.00 will return 1.5. Correct?

How will display 1 when read 1.5 in this case?


I can use trunc() and round() to return 1 when is 1.5 1.9 for example... But I always will have the problem to show 01 and 02 depending on the position of potentiometer

For example. Apply trunc and round in 1999 I can have the same problem...

I need a function to work like a encoder... Like this..

Do you want to truncate or round? Mathematically, there isn't much else you can do.

What output do you want to see for 1999 input?

"work like an encoder" means nothing, as there are so many different aspects to an encoder. Please supply example conversions that exhibit your problems, and also example conversions that are correct in your opinion.

If you want to show "01" instead of "1" then convert the binary value into text and check the length. If too short prefix a '0'.

For 1999 it can be 01... and > 2000 can be 02

But the big problem is how I can fix the variation of reading to get a good transition in the display without changing when the potentiometer is in the middle of two ranges?

This is my main point!

We already anwered that! You can use hysteresis, low pass filtering (averaging), or both.

...that you can not read or understand the suggested solutions :frowning:

Ok! I understand all the suggestion, mister.

I will try and back with results.

Here is a sample low pass filter:

unsigned int smooth(unsigned int newVal) {
  static unsigned int oldVal = 0;
  unsigned long sum;
    //  optimize sum = (oldVal * 3 + newVal) / 4;
    sum = ( (oldVal << 1) + oldVal + newVal) >> 2;
  oldVal = sum;
  return oldVal;
  // example use
  smoothedValue = smooth( analogRead(A0) );


Thanks! I will try!

Just to note that there is no way you can get those readings with your setup.

Yes that means you can only get numbers between 0 and 1023, which is why you can never get the numbers 1999 and 2000. Even if you multiply your reading, you get holes left behind, that is numbers a that are impossible to get.

OK on the log pot, you do not need an antilog pot. However even with a log pot your display will still be linear, and that is all the user sees.