Contents

Deciphering the Endless Rotary Potentiometer - Experiments and Insights

Introduction:

In my last post, we explored how to replace one of the encoders on the Novation Circuit. I also shared my experiences in locating the replacement part and the wonderful support I received from Novation. However, during the repair process, I made an intriguing discovery. What I initially believed to be encoders turned out to be potentiometers. This left me with questions. Don’t potentiometers resist current? How can these seemingly conventional components offer endless rotary behavior, and how can we leverage them in our microcontroller sketches?

Before we delve into another service video, I thought it would be beneficial to conduct some experiments to gain a deeper understanding of these enigmatic components, especially in the context of their use in code.

If you’d like to follow along, here’s what you’ll need:

  1. Breadboard
  2. Arduino Nano (or any other model, adjusting for your specific pins)
  3. B10K “Endless potentiometer” (like this one: Datasheet)
  4. A 10K resistor (to control current)
  5. At least one multimeter (I will use two)
  6. Jumper wires.
  7. USB cable (to upload the sketch to the microcontroller)
  8. An IDE (either the Arduino IDE or Platform IO) As long as it can be configured with a Serial Console.

Part 1: Exploring the Endless Potentiometer:

Let’s begin by examining the schematic for this component and making sense of its inner workings.

B10K Schematic

This potentiometer differs from conventional ones in that it features 4 pins, including an additional pin for a second wiper. When we refer to the datasheet’s schematic, it becomes evident that internally, this component comprises two distinct potentiometers with their resistance and wiper positions phased at a 90-degree offset.

The datasheet provides a clear graph to illustrate this:

B10K Resistance Taper

Here is a peek at the inner workings of this component:

B10K Pot Internals

Now, let’s compare this with the operation of a typical encoder. Encoders typically possess 3 pins: two for signal and one for grounding.

Encoder

Internally, encoders incorporate switches, allowing them to determine the direction of rotation as you turn the shaft clockwise or counterclockwise. For example:

Encoder Internal

If we intend to employ this potentiometer in a manner similar to an encoder, we must monitor the voltage changes produced by the potentiometer. As the signals increase and decrease during rotation in the same direction, we need distinct logic based on the wiper’s rotational position. Fortunately, someone has already devised a solution.

During my exploration of rotary potentiometers, I came across this blog post. In this post, the author, Bjørn, delves into detail and provides example code. We will examine this code later, but first, let’s gather some voltage readings from the pot.

Part 2: Initial Experiments:

For this test, we’ll construct a simple circuit comprising a supply voltage (in my case, 12v) and add a 10k resistor for short protection to the potentiometer.

Initial Experiment

We’ll connect our probes to each of the wipers, respectively, and record the values when the potentiometer shaft is at 12 o’clock, 3 o’clock, 6 o’clock, and 9 o’clock. We’ll then compare these values with the Taper graph provided in the schematic. If all goes well, we should obtain values of 0v, 3v, 6v, and 3v for each of the wipers, with the second wiper offset, something like 3v, 6v, 3v, 0v.

Part 3: Connecting to an Arduino:

For the next part of the experiment, we’ll introduce an Arduino microcontroller and wire up the potentiometer to read its input from pins A0 and A1. Here’s the schematic for what we’ll set up:

B10K with Arduino

Note that we don’t use a protection resistor in this case. The analog input pins have a high input impedance, which means they draw very little current from the connected circuit. This makes them suitable for direct connection to potentiometers and other analog sensors without a significant impact on the circuit.

Part 4: Experiment 1 - Voltage Relationship:

I have a straightforward sketch loaded onto the Arduino here. All we’re doing is printing the values from each of the wiper leads of the potentiometer. Using the serial plotter, we can observe that the values change relative to each other. You may also notice a degree of noise, which is easier to discern with the Serial Monitor. As you can see, the numbers fluctuate somewhat instead of holding steady. This behavior is commonly known as jitter.

Part 5: Experiment 2 - Directional Rotation:

Here, we examine a port from Bjørn’s code. The logic remains consistent, but I’ve made a few adjustments. The most notable modification is converting the code into an Arduino library. Other changes aim to enhance code clarity.

To ascertain the direction, we must determine the movement of each individual input and use that information to identify the quadrant in which we are situated. This is crucial because each quadrant requires distinct logic.

First, we compare the current value with the previous one. If the new value exceeds the previous one, we infer an upward movement. The threshold adds a buffer to reduce jitter, so we incorporate it when evaluating. Conversely, if the value decreases, we conclude that there is downward movement.

  // decode each wiper value direction.
  if (valueA > (previousValueA + threshold)) {
    dirA = UP;
  } else if (valueA < (previousValueA - threshold)) {
    dirA = DOWN;
  } else {
    dirA = NOT_MOVING;
  }

  if (valueB > (previousValueB + threshold)) {
    dirB = UP;
  } else if (valueB < (previousValueB - threshold)) {
    dirB = DOWN;
  } else {
    dirB = NOT_MOVING;
  }

Now, we can compare the relationship between the two values. When both values decrease, with A being greater than B, we are rotating clockwise. If the opposite is true, we are rotating counterclockwise. When both values increase, the opposite of the first condition applies. The last two cases occur when the two values intersect. In these situations, we know that one is increasing while the other is decreasing, so we must determine whether the values are above or below half the total range. This distinction will reveal the actual direction of the pot.

  // Now evaluate the actual direction of the pot.
  if (dirA == DOWN && dirB == DOWN) {
    if (valueA > valueB) {
      direction = CLOCKWISE;
    } else {
      direction = COUNTER_CLOCKWISE;
    }
  } else if (dirA == UP && dirB == UP) {
    if (valueA < valueB) {
      direction = CLOCKWISE;
    } else {
      direction = COUNTER_CLOCKWISE;
    }
  } else if (dirA == UP && dirB == DOWN) {
    if ((valueA > (adcMaxValue / 2)) || (valueB > (adcMaxValue / 2))) {
      direction = CLOCKWISE;
    } else {
      direction = COUNTER_CLOCKWISE;
    }
  } else if (dirA == DOWN && dirB == UP) {
    if ((valueA < (adcMaxValue/2)) || (valueB < (adcMaxValue/2))) {
      direction = CLOCKWISE;
    } else {
      direction = COUNTER_CLOCKWISE;
    }
  } else {
    direction = NOT_MOVING;
  }

One advantage of having this as a library and object is that we can readily reuse the code. Here, I employ a single potentiometer to control values for 3 variables.

  • value illustrates the value stored locally within the object created from the library class.
  • localValue relies on the library’s internal settings but calculates a value within the main sketch, outside the object’s scope. This can be valuable when dealing with multiple values that share the same constraints.
  • altValue entails more logic within the main script but affords greater control. Arguably, we could use the map() function in the sketch, but I find this approach helpful for illustrating the logic when working with such components. Additionally, this aspect of the logic could be applied to an encoder as well, but that’s a topic for another day.
  pot.updateValues(analogRead(PIN_A), analogRead(PIN_B));

  if (pot.isMoving) {
    // Use this method if you are controlling a single value with this pot. The object comes with its own
    // Internal value variable to keep track of.
    value = pot.value;

    // Or you can pass the value you want calculated. This is useful if all your values use the same
    // range.
    localValue = pot.getValue(localValue);

    // However, if you want to take advanatage of the relative nature of the component, you may
    // instead be interested in using the value changed instead. This will allow you to use the
    // same pot with different values ranges. But there is a few more lines of code needed.
    if (pot.direction == pot.CLOCKWISE) {
      altValue = altValue + pot.valueChanged;
    } else if (pot.direction == pot.COUNTER_CLOCKWISE) {
      altValue = altValue - pot.valueChanged;
    }

    if (altValue < 0) {
      altValue = 0;
    } else if (altValue > MAX_POT_VALUE) {
      altValue = MAX_POT_VALUE;
    }

The code mentioned above can be found on GitHub. If you make some improvements why not submit a PR? I’m sure others will appreaciate it.

Part 6: Advantages of Endless Potentiometers vs. Encoders:

So, why might we choose to employ an endless potentiometer instead of an encoder? Both have their unique advantages, but here are some considerations.

Potentiometers offer a continuous analog output, making them ideal when you require a smooth, continuous response in your circuit. Potentiometers are typically more cost-effective than encoders, especially when considering high-precision encoders. Wiring a potentiometer to a microcontroller is simpler because most GPIOs can directly receive a signal from the potentiometer due to its high-impedance configuration. Encoders, in contrast, often necessitate additional components like capacitors to provide a smoother signal and debounce.

Encoders, on the other hand, may be preferable when dealing with digital data processing.

Conclusion:

Today, we’ve explored the enigmatic world of endless potentiometers. We’ve scrutinized the relationship between the two wipers as they traverse the resistive track and visualized this data to understand how to utilize them in code. Furthermore, we’ve implemented code to discern the potentiometer’s movement, allowing us to increment values in code. Lastly, we’ve briefly discussed the advantages of integrating an endless potentiometer into our projects.

One thing is certain: I now possess an additional tool for use whenever I require a single input for multiple values in my sketches. I hope you’ve found this article informative; I certainly learned from the research I conducted for it! If you have any ideas about using this potentiometer, please share them in the comments. As always, I encourage you to undertake these experiments yourself and share your findings with us, fostering a community of shared knowledge. If you enjoyed this content consider subscribing to my YouTube channel!

Until next time!