Home » Back to basics: Potentiometers

Back to basics: Potentiometers

by Bits and Droids
Published: Last Updated on 0 comment

Perhaps one of the most basic components in our toolbox never got the attention it deserved. It’s a cheap component, versatile, and most of all easy to use. In this article, we will focus only on how to use a potentiometer as a voltage divider.

The potentiometer has a finite start and endpoint. In contrast, the rotary encoder we’ve covered in the past had an infinite start and endpoint. If you wanted to, you could turn the rotary encoder all night long until the hardware would give up. But not the potentiometer; it can range from open to fully closed and anything in between. I included this part because I can remember my own disappointment when I ordered a potentiometer in the hopes of using it for my radio stack. Obviously, I was oblivious and a complete rookie, but I hope that I’m able to save at least one like-minded rookie from the same pain. Don’t worry. I got your back!

The potentiometer is a component that provides a gradual scale of resistance to your circuits. We could use it to limit the voltage that flows from point A to point B (i.e., from your power source to an LED to control the brightness). Or perhaps more interesting would be to measure that voltage to control game states. The amount of resistance can differ per potentiometer. It will usually be listed on the product page or at the top of the potentiometer (see the image below, the resistance is marked with an Ω sign).

Potentiometer and its pin purpose

There usually are 3 pins on a potentiometer (sometimes 6 to send the same signal towards 2 outputs. Both sets will have the same function in these instances). The first (5v) and third (gnd) pins are interchangeable. The current will enter the potentiometer through either pin and tries to find its way out of the other. The middle pin is the analog output. It’s connected to a wiper internally that moves towards either side when you rotate the physical knob. This may seem logical but let’s open up the potentiometer to get a clear view of these mechanics.

In this first example we’re able to see a global representation of a potentiometer. It has the current coming in from the red dot and it tries to take the path of the least resistance towards either the wiper in the middle or ground.

A potentiometer without power applied

The moment we add some power, we can clearly see what happens to the resistance. Now, remember, when it’s fully closed, the resistance will be 10.000 Ohms, and when it’s fully open, it will be 0 Ohms. There might be some slight deviation, but in theory, the middle will roughly be the halfway point of the resistance. In our case, that would be 5K (5000) Ohms. The voltage will travel from the entry point down the wiper (if it’s hooked up to something) towards the next component.

The further we move the wiper to either side, the higher/lower the voltage will become at the output.

Where to use it?

Well, as I mentioned earlier, it has a clear start and endpoint. We’re also able to clearly measure where the wiper is positioned on a scale by measuring the voltage at the wiper. We only need to wire it up to an analog port on our Arduino if we want to do so. This makes it an ideal candidate for use cases with a gradual scale. Think about throttles, flaps, axis, etc.

Analog read

We will use the Arduino function called analogRead(//parameter byte potentiometer pin) to measure the position. The analogRead function converts the analog signal to a digital one. What we perceive in the IDE is actually a digital signal and not an analog signal. What’s up with that? Well, our Arduino uses an ADC (analog to digital converter). This makes it easy for us to interpret and work with the signal on our pc. The Arduino family uses a 10-bit ADC. This will map the signal on a scale of 0 to 1023. Other microprocessors can sometimes support an even higher resolution, but it often comes at the cost of extra noise. For most use cases, a 10-bit resolution will be more than enough for our needs.

An ADC is an analog to digital converter. The other way around would be a DAC. Most people are more familiar with DAC’s since they are widely used in audio setups. An example would be a DAC to drive your stereo where it converts digital music to analog signals.

When we want to read the analog value, we use the Arduino function called analogRead(*Pin of the potentiometer*). It’s important to wire up your rotary encoder to an analog port on your board (usually prefixed with an A).

// in this example the potentiometer is hooked up to A0
byte potPin = A0;

void setup(){
  Serial.begin(115200);
  // this sets the pin (A0) to act like an input
  pinMode(potPin, INPUT);
}

void loop(){
  Serial.println(analogRead(potPin);
  // if you don't add a delay you will be bombarded by values in the monitor
  delay(100);
}

Smoothing the output

Now if we upload this sketch and open the serial monitor we will be greeted by something that looks like this.

Serial monitor with potentiometer

Now, these values will change depending on the direction you twist the knob. But perhaps you already noticed that the first 7 values are awfully similar. These little jumps will occur even when we don’t touch the pot. This can be caused by slight fluctuations in the voltage, dust in the potentiometer, or a range of other factors. Ideally, we might want to smooth the results slightly to avoid this jitter. There isn’t always a “best” approach in coding, and perhaps you can even come up with a better solution to the same problem. But today, we’re going to check 2 commonly used solutions.

Averaging (big jumps)

If you encounter large jumps in your values averaging your output might be a good solution. Each value individually will be stored in an array of a defined size—the bigger the array, the smoother the result. But the time it takes to calculate 1 value increases as well. Finding that sweet spot is a matter of trial and error to see what suits your needs.

int BitsAndDroidsFlightConnector::smoothPot(byte potPin){
    int readings[10]={};
    total = 0;
    for (byte i = 0; i < samples; i++){
        total = total - readings[i];
        readings[i] = analogRead(potPin);
        total = total + readings[i];
        delay(1);
    }
    average = total / samples;
    return average;
}

The example above is the one that is actually present in the library I made. It takes 10 samples. Runs through a loop 10 times. Storing the value in an array -> adding that value to the total and dividing it by 10 once the loop is finished. This will give us an average of 10 readings smoothing out any big jumps.

EXAMPLE: (10 10 11 11 10 15 10 11 12 10) / 10 = 11. We can clearly see that even with a jump towards 15, the average still came out to only 11.

Filtering (small jumps)

Sometimes the jumps are tiny. Let us take these values as an example: 80, 81, 81, 81, 80. Even though the potentiometer isn’t touched, it can still flicker slightly. The easiest solution would be to check the current value versus an old value and compare the result to a cut-off value. How bigger the value we check against, the less noise will be present. The bigger the cut-off value will also cause the fidelity to drop. When we use it for throttle or as a flaps lever, a slight loss of fidelity (in my opinion) isn’t really noticeable.

int value;
int oldValue;

void setup(){
}

void loop(){ 
  value = analogRead(potPin);
  //The abs function will return a positive result even when the result will be negative. This makes it easier to use for our statement
  if (value != oldValue && abs(oldValue - value) > 1) {
    oldValue = value;
  }
}

The code example above will filter out the results if the difference isn’t bigger than 1 (2 and onwards). You must not forget to set the old value to the new value when it passed our filter.

I promise you that your Serial monitor will be calm as an ocean when you apply these smoothing tricks.

Out of the box

You don’t have to use a potentiometer to represent a curve/axis or scale. We could also come up with solutions that bind certain actions to certain values. In the example below, we read the value. If it’s bigger than 600, we will decrease the Com 1 Khz. If the value is smaller than 400, we increase the Khz value of com 1. To smoothen the experience, we added a slight delay to make sure our frequency doesn’t skyrocket.

Notice how there is a gap (<400 | 400-600 |>600)? In the range of 400 to 600, there won’t be any commands sent. In theory, we created a digital 3-way switch. Depending on the value, we switch between an upstate, downstate, and idle state. This way, we can use our potentiometer to control our radio (perhaps in a rather complicated way).

#include <BitsAndDroidsFlightConnector.h>
BitsAndDroidsFlightConnector connector = BitsAndDroidsFlightConnector(true);
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(A0, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  int value = analogRead(A0);
  if(value < 400){
    Serial.println(connector.sendCom1FractInc());
    delay(200);
  }
  if (value > 600){
    Serial.println(connector.sendCom1FractDecr());
    delay(200);
  }
}

You may also like

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept