I am using Arduino and ESP to build flight simulator components, and like many others, I've been faced with the age-old issue of fluctuating analogue readings from cheap potentiometers.
Sure I could spend more money on a quality potentiometer, or use a rotary hall effect device, but these would put the price of the end-product up.
The need for really accurate readings from pots in the scenario I'm working with isn't horribly important, so I created a simple coding workaround to provide me with a stable reading from a $0.50 pot. I thought I'd share here, as I've not seen this solution posted anywhere else, and thought it might help someone else.
// ----------------------------
// Potentiometer Handler
// ----------------------------
void handlePot() {
int currPotValue = analogRead(potPin);
/*
Potentiometer noise means values are constantly moving.
We can try to smooth out the changes by adding a capacitor between the +ve and -ve
legs which smooths out some of the noise, but there a still fluctuations which tie up the
ESP processor.
Dividing by 400 and then multipying by 409.6 irons out small changes
this gives a smooth 0-4096 setting for the potentiometer, without noise jitter.
LEDPwr, LEDLamp, currPotValue, and potValue are all declared in the setup function.
*/
potValue=(currPotValue/400)*409.6;
if (LEDPwr != potValue) {
Serial.print("Pot Value changed to : "); // debug code
Serial.println(potValue); // debug code
LEDPwr=potValue;
tlc.setPWM(LEDLamp, LEDPwr);
tlc.write();
}
}
So you read a integer value between 0 and 4095, and you divide that by the integer value 400.
That gives you an integer value of 0 to 10.
Mulitplying that 0 to 10 value by 409.6 gives you 11 discrete values between 0 and 4096, which are all integer multiples of 409.6.
So you've gone from 4096 possible values to 11.
Am I understanding your snippet correctly, or have I missed something?
Edit: I only ask because it seems to me that you've traded a jitter of 1 count when your analogRead value is bouncing between 399 and 400 for a jitter of 409 counts. Instead of seeing the value change between 399 and 400, it's now changing between 0 and 409.
Now if your routine included some hysteresis, so that the output value switched at one step size when increasing, but switched at a slightly lower value than the step size when decreasing, that would be interesting. I still wouldn't call 11 steps smooth, but perhaps it depends on the application.
I use this to get consistent analog values. It is blocking !
It removes the highest and lowest values, then averages the rest.
If you choose too many readings then it becomes quite "blocking" since it's about 100usec per analog read. Start with a value of 6 and see how it goes.
Call it with something like: potValue = AnalogSmoothed(potPin, 6);
int AnalogSmoothed(int read_pin, int num_reads)
{
if (num_reads < 4) num_reads = 4;
int SmoothValue = 0;
int raw_read = 0;
int raw_min = 1024;
int raw_max = 0;
for (int count = 0; count < num_reads; count++)
{
raw_read = analogRead(read_pin);
if (raw_read > raw_max) raw_max = raw_read;
if (raw_read < raw_min) raw_min = raw_read;
SmoothValue = SmoothValue + raw_read;
}
// throw away the greatest and least values and average the rest
SmoothValue = (SmoothValue - raw_max - raw_min) / (num_reads - 2);
return SmoothValue;
}
This would be my solution to the problem.
The parameter noise determines the threshold when to accept a new value.
Furthermore the extremes of the potmeter are always accepted.
This way one can define the noise level (even per call) while keeping the resolution of the potmeter except around “lastValue”.
Drawback of current code below is that it only supports one pin.
(make it multipin is left as an exercise
int stableAnalog(int pin, int noise= 5)
{
const int MAXVALUE = 4095; // adjust if needed
static int lastValue = -999;
int value = analogRead(pin);
// determine if the new value should be accepted.
if ((value == 0) || (value == MAXVALUE) || (abs(value - lastValue) >= noise))
{
lastValue = value;
}
return lastValue;
}
I like ``potValue= currPotValue & 0xFFE0; as it is super performant, however it still can have values that show the noise, and it reduces the resolution of the potmeter.
When we do math on paper/chalkboard we can use factors and get clean answers.
When we do math on 8 to 32 bits we have to be careful to get clean answers.
When scaling values (what that factor is about) we should NOT do in essence this:
X = (A/B) * C
but rather
X = (A * C) / B
that makes a bigger more precise number to divide rather than a smaller less precise number to then multiply the error with the value instead.
It may take promoting 16 bits to 32 to hold the bigger value, especially if the multiplier is 1000 just to Generate More Precision Before The Remainder-Losing Divide. You can actually work it to where every digit lost is below your desired precision, honest.
Many beginners have to go through lessons about variables to get why.
Strong point in your solution is that it works when you have multiple analog pins to read.
You might want to look at my runningMedian library, it only needs one new sample and might be blocking for a substantial shorter period (which really depends on the speed of the analogRead calls).
And yes, one need a runningMedian object per pin, so it takes extra memory.
Which Arduino...
Different Arduinos have different A/D converters.
The Uno R3 has a ratiometric A/D, which makes it perfect to read a (ratiometric) pot.
An ESP has an absolute A/D, that is basically not suitable to read a pot.
The absolute A/D makes the pot also dependent on supply variations.
Much better to use a rotary encoder with an ESP.
Leo..
A pot outputs a higher voltage with higher VCC.
An A/D with VCC as reference becomes less sensitive with higher VCC, thus compensating.
An A/D with fixed reference voltage doesn't compensate.
Leo..
There is only ratiometric compensation when pot supply is the same as Aref.
Not only the same voltage, but also the same voltage variations.
But when you use 1.1volt Aref, then you don't have access to that point to power the pot.
1.1volt Aref makes it an absolute A/D.
Good for voltage measurements, bad for ratiometric sensors.
Default Aref (on an Uno) is good for ratiometric and bad for voltage.
An ESP only has absolute (1volt internal Aref) and so has an ADS1115.
A pot is not the only ratiometric sensor. The ACS712 is another example.
Many pressure sensors also fall on that category.
Leo..