Reading very small adjustments from a potentiometer accurately?

As some of you probably know from my previous posts, I'm building a coil gun with an arduino to control the timing. In other words, the arduino is used to swich a current pulse on an off in about 100mS (more or less).
However, this delay must be manually tuned before firing the coil gun. I'll be using a 10K potentiometer to do this. I need an accuracy of +- 1ms.
I ran into some problems trying to write a proper program to do this.

1). The potentiometer signal seems to pick up a bit of noise (is this normal?) and the analogRead function returns values that vary by +- 3. (on the standard 0 - 1023 scale).
My solution was as follows: Take two readings a few mS apart and then compare them to see if they have changed. Such a change would indicate that the pot wiper has been turned.

void setup(){

void loop(){
  int first = analogRead(3);
  int second = analogRead(3);
  if (abs(first - second)>2){ //i.e if the value has changed by more than 2 (two avoid noise)
     int mapped = map(second,0,1024,0,250); 

2.) This solution works well for fast movements of the wiper.. but when I try to move the wiper very slowly (such as when one tries to make very small changes), it breaks down. (because in 10ms, the wiper doesn't produce values that have a difference of 2. I can't reduce this difference further because of the noise issue..

What do you suggest?
Thanks in advance.

a) put 100nf capacitor from wiper to gnd (from analoginput to gnd)
b) make 33 measurements and make an average
c) make a digital filter (new=Aold+Bnew, A+B=1, B=0.05)
d) introduce hysteresis

except for the first two, I don't know what the other ones mean..

c) it is a simple digital filter, works similar like putting an RC low-pass at analog pin
d) hysteresis - Hysteresis - Wikipedia

Thanks.. I'll try those. The capacitor solution didn't work.. (my capacitor was 180nF but I thought that was close enough)

..try to put a 10-100kohm resistor between the wiper and the analog in, and that 180nf from analog in to gnd..
However, without an averaging or a hysteresis you may always get +/1bit..

Could you give me an example for some 'averaging' code?

b) make 33 measurements and make an average

Better make that 32, no overflow and it can be optrimized to shift

int sum = 0;
for (uint8_t i=0; i< 32; i++) sum += analogRead(POTMETER);
int val = sum / 32;

Wow.. simple, but works.
However, the solution is not a perfect one.. there still is some jitter in the numbers.. but that's ok.

Your other option is to use a multi-turn (e.g. 10) potentiometer.

How many distinct values you want?
The Arduino ADC reads 1024 different values and you may consider the last one to three noise (depends) leaving effectively 128-256 distinct values.
If your sketch uses two potmeters, one for bigsteps and the other for fine tuning you could have more steps.

unsigned int val = (analogRead(POT1) /4) * 256 + analogRead(POT2)/4;   // DO NOT OPTIMIZE the /4 and the *256 !!!

you could also use an averaging read for both POT's, which is left as an exercise :slight_smile:

Or use digital input switches, up and down and have you sketch increment or decrement the value as you wish with no worry about resolution limits or noise.


If you're doing a 4:1 mapping

if (abs(first - second)>2){ //i.e if the value has changed by more than 2 (two avoid noise)
int mapped = map(second,0,1024,0,250);

why not limit the change to require a difference of more than 3 instead?
0,1,2,3 all map to 0
4,5,6,7 all map to 1

I think I would just do a simpler mapping even:
mapped = analogRead( 3) >>2;
and just dump the lower 2 bits altogether.

Lefty's solution really is the most appropriate for this situation. Using an analog input to adjust a digital value is combersome, error-prone, and unnecessarily complex. If you absolutely want a dial input as opposed to button inputs, then opt for a rotary encoder. While an encoder adds it's own layer of complexity to the project, it's still better than a pot tide to an ADC input.