How to Detect Which Way a Potentiometer is Rotating?

Hi all,
I'm wondering if there is a way to figure out which direction a potentiometer is rotating (counterclockwise vs clockwise) and print it to the serial monitor.

Here is how to find the angle if that helps at all:

float maxreading=170;
float minreading=30;
int anglePin = 2;
void setup()
{
Serial.begin(9600);
}
void loop()
{
float multValue = ((maxreading-minreading)/180);
float input = analogRead(anglePin);
//Uncomment the line below when finding the maximum and minimum readings by turning your potentiometer 180 degrees.
//Serial.println(analogRead(anglePin));
float angleReading = (input - minreading) / multValue;

//Uncomment the line below to get the angle reading.
//Serial.println(angleReading);
delay(500);
}

The background story is: I'm trying to use a potentiometer to control a servo which would move a r/c quadcopter controller stick.
If the pot rotates 1 degrees clockwise, it must move the servo a preset distance clockwise for a delay that is equal to the potentiometer degree value, and then spring back to center.
If the pot rotates counterclockwise, it must move the servo a preset distance counterclockwise for a delay that is equal to the pot degree value, then spring back to center. This would tell the quadcopter to rotate the same amount of degrees as the potentiometer.

I'm wondering if there is a way to figure out which direction a potentiometer is rotating

Read the pot each time through loop() and save the value. When you read it again next time through loop() if the value has increased then the pot is turning one way and if it has decreased then it is moving the other way.

In practice you will need to allow a certain amount change of value before making a decision due to the likely instability of the input voltage.

How do I leave room for potentiometer voltage instability as mentioned?

I have this so far, It’s not working. The servo just moves all the way to the side and then stops, regardless of all potentiometer adjustments.

#include <Servo.h>

Servo myservo2;

int potpin2 = A2;

int valB;

int oldvalB=0;

void setup()
{
  Serial.begin(9600);
  myservo2.attach(10);// attaches the servo on pin 9 to the servo object
  
}

void loop() 
{  
  
  oldvalB = analogRead(potpin2);
  delay(100); //space between readings.
  valB = analogRead(potpin2);
   int difference = oldvalB - valB;
   
  if (valB > oldvalB) { //if degrees are increasing, reset counter and start counting again
    difference = oldvalB - valB;
    Serial.print(difference);
    Serial.print(',');
    myservo2.write(0); //stick fully to the left.  
   delay(15); // wait for servo to catch up
   delay(difference); //MIGHT NEED TO BE MAPPED TO DESIRED TIMING FIRST!
   myservo2.write(90); //stick back to center.
   delay(15); // wait for servo to catch up.
   
  } 
 if (valB < oldvalB) { 
 myservo2.write(180); //stick fully to the right.
 delay(15); // wait for servo to catch up.
 delay(difference); //MIGHT NEED TO BE MAPPED TO DESIRED TIMING FIRST!
 myservo2.write(90); //stick back to center.
 delay(15); // wait for servo to catch up.
 
 }
 
    Serial.print("\n");  
 
  }

Not the best way to do it: take two readings 100msec apart. Worse: totally disable the Arduino from doing anything else by using a delay(100).

Better: take a new reading, compare it to the old one. If more than X different, do the action and then save the new reading to 'old'. That way even a slow movement will eventually be picked up.

Your original description didn't seem to require any minimum speed on the pot.

I followed your suggestions and also removed the 100ms delay between measurements.
I don’t understand why, but the servo just swings back and forth rapidly by small amounts between 0 and 90 degrees. The servo is heating up! I have adjusted the number in if (difference > 10) { do actions} to 10, 3, 5, 20, 30, 40, and 50. but it always either doesn’t move at all, or flutters too rapidly.
The goal is to only swing the servo arm from 90 degrees to 0 degrees and back if pot moves counterclockwise (or from 90 to 180 degrees and back if pot is moving clockwise) with the delay between swinging there to back being equal to how far the pot has rotated.

Here’s the troublesome code:

#include <Servo.h>

Servo myservo2;

int potpin2 = A2;

int valB;

int oldvalB=0;

void setup()
{
  Serial.begin(9600);
  myservo2.attach(9);// attaches the servo on pin D9 to the servo object
  
}

void loop() 
{  
  
   oldvalB = analogRead(potpin2);
// delay(100); //space between readings. //REMOVED AS SUGGESTED.
   valB = analogRead(potpin2);
   int difference = oldvalB - valB;
   
   if (difference > 20) { 
   
  if (valB > oldvalB) { //if degrees are increasing, reset counter and start counting again
    //difference = oldvalB - valB;
    Serial.print(difference);
    Serial.print(',');
    myservo2.write(0); //stick fully to the left.  
    delay(15); // wait for servo to catch up
    delay(difference); //MIGHT NEED TO BE MAPPED TO DESIRED TIMING FIRST!
    myservo2.write(90); //stick back to center.
    delay(15); // wait for servo to catch up.
    oldvalB = valB;
   
  } 
 if (valB < oldvalB) { 
  myservo2.write(180); //stick fully to the right.
  delay(15); // wait for servo to catch up.
  delay(difference); //MIGHT NEED TO BE MAPPED TO DESIRED TIMING FIRST!
  myservo2.write(90); //stick back to center.
  delay(15); // wait for servo to catch up.
  oldvalB = valB;
 
 }
 
  Serial.print("\n");  
 
  }
}

The goal is to only swing the servo arm from 90 degrees to 0 degrees and back if pot moves counterclockwise (or from 90 to 180 degrees and back if pot is moving clockwise) with the delay between swinging there to back being equal to how far the pot has rotated.

understand your arduino is way faster than your operator... imagine the pot is at 600 and the operator turns it to the right towards 800... while it's doing this it takes a few ms for the operator and your arduino will have checked probably 100 times the value of the pot and thus possibly activated your action 100 times because every time it sees the pot moving higher.

Isn't what you want to do is start from a STABLE position of the pot, detects when it starts moving. Wait until it is STABLE again. compare the start and end value and then do your action?

if so - then this is what you need to code :wink:

side question: what movement do you expect the operator to do? turn only in one direction? or at some point go back to middle positon but not willing to trigger the servo movement?

How would I pause the analogRead() so that I can only get stable values? I'm just not sure how and where that would fit into my sketch so far.

To answer your side question; A remote control quadcopter uses a movement of the left control stick leftwards to rotate the quadcopter counter clockwise, and a rightwards movement of the same stick to rotate clockwise. The farther the stick is moved, the faster the rotation is. I am trying to make a custom joystick that in turn move servos that mechanically move these sticks in a unique way instead of directly by hand. Basically, there would now be a sliding left potentiometer for altitude control, and a right custom joystick made from a 2nd and third potentiometer that controls forward s and rotation movement. This code is to get the rotation control potentiometer working, the rest I already have working in a prototype sketch. So the rotation controlling pot needs to work uniquely by flicking the left stick left wards or rightwards and then back to center depending on the direction of the pot rotation. The theory is that the amount of time the stick stays away from center causes the distance the quadcopter rotates.
The whole idea is to mount the forward speed controlling pot on top of the rotatiom pot with a knob attached to the forward pot for grip. The forward stick can only move to center or forwards. The overall purpose is for a "drag" mode. Imagine the joystick being over a map: the position of the joystick tells the quadcopter which direction to head to; away, towards, leftwards, or rightwards of the pilot on the ground. So it's like the tip of the stick represents the quadcopter.

I hope this makes as much sense as possible.
Thanks,
Triops124.

2 of 2

I just realized that a potentiometer would not work for this project because it needs to have unlimited rotation...Duh!
A rotary encoder is the way to go!
But the question for how to move the stick back and forth with a specific delay based on rotary encode data is still a puzzle!

I am trying to make a custom joystick that in turn move servos that mechanically move these sticks in a unique way instead of directly by hand. Basically, there would now be a sliding left potentiometer for altitude control, and a right custom joystick made from a 2nd and third potentiometer that controls forward s and rotation movement.

that's why people use joysticks for this kind of things.

When the joystick is centered nothing happens, when the joystick is to the left you turn your servo in one direction and the angle of the servo is connected to the value of the pot - the further away from center you move, the harder the servo turns and then you keep the servo there for as long as the joystick is held off neutral position.

Are you saying for rotation you want something different?

Yes, I want something different.

I'm trying to make the angle the quadcopter rotates to the same as the angle the rotary encode is at. So if rotary encoder rotates 1 tick clockwise, then rotate quadcopter one tick clockwise. Similar for counter-clockwise rotation.

It would be the equivalent of flicking the stock remote controller stick to the left or to the right with a finger, then letting it spring back. One flick per degree of rotation desired.

Another challenge might be the servo might not be able to 'flick' fast enough. Hence using "difference" as "delay" between flick and spring back.

In pseudo code:

setup,
listen for rotary encoder 'ticks':

if tick detected:
if cw {
myservo2.write(0); //stick fully to the left. //might need to be mapped to the speed of rotary encoder switching (frequency)
delay(15); // wait for servo to catch up ////Might need to be mapped to how many increments the rotary encoder has turned.
myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
delay(15); // wait for servo to catch up.
}

if ccw {
myservo2.write(180); //stick fully to the right. //might need to be mapped to the speed of rotary encoder switching (frequency)
delay(15); // wait for servo to catch up. //Might need to be mapped to how many increments the rotary encoder has turned.
myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
delay(15); // wait for servo to catch up.
}

So If you take a rotary encoder then it's easy to get the ticks you describe through interrupts and set flags and do what you said in the main loop reading the flags.

The idea of giving full command and back to zero is probably going to create lots of instability due to the inertia of your physical device and approximation in mechanical parts and flight conditions. You probably will want to be way more subtle than this. When you pilot a cheap toy quad give it a try to see what happens. It's better to give a bit of command in one direction and wait long enough for the movement to start and then slowly go back to neutral.

Thanks! I adapted the script to work with one of the “Encoder_Polling” libraries.
Here’s what I have so far:

#include "Encoder_Polling.h"
//library from http://forum.arduino.cc/index.php?topic=174967.0
#include <Servo.h>

Servo myservo2;
const int encoderPin_A = 8;
const int encoderPin_B = 9;

int counter = 0;

void setup()
{
  
  Serial.begin(9600);
  myservo2.attach(9);// attaches the servo on pin D9 to the servo object
  encoder_begin(encoderPin_A, encoderPin_B); // Start the decoder

}


void loop() 
{  
 
  int dir = encoder_data(); // Check for rotation
  
  if(dir != 0)              // If it has rotated...
  {
    Serial.println(dir);   // Print the direction
  
   if (dir = -1)
   {
    myservo2.write(2); //stick fully to the left.  //might need to be mapped to the speed of rotary encoder switching (frequency)
    delay(15); // wait for servo to catch up     ////Might need to be mapped to how many increments the rotary encoder has turned.
    myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
    delay(15); // wait for servo to catch up. 
    }
     
     if (dir = 1)
     {
     myservo2.write(178); //stick fully to the right. //might need to be mapped to the speed of rotary encoder switching (frequency)
     delay(15); // wait for servo to catch up. //Might need to be mapped to how many increments the rotary encoder has turned.
     myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
     delay(15); // wait for servo to catch up.
    }
  }
}

I plan on using a dirt cheap $13 Floureon H101 toy quadcopter, so I have room for experimentation & errors!
I ordered a rotary encoder for $0.75USD, so I haven’t tested anything yet.

I'm trying to make the angle the quadcopter rotates to the same as the angle the rotary encode is at.

Without feedback from the quad I don't see how you are going to achieve this.

UKHeliBob:
Without feedback from the quad I don’t see how you are going to achieve this.

It doesn’t have to be that reliable in that way because I could always simply spin the controller farther than normal (move the rotary encoder dial extra far), and that would help counter any minor issues.

So the rotary encoder I ordered finally arrived, and I am having some issues with my code. It ought to, depending on the rotation direction of the encoder, rotate the servo in one direction, then going back to center position, OR the other direction, then back to center.
Instead it just does both movements in a set; 2 degrees, 90 degrees, 178 degrees, 90 degrees, 2 degrees, 90 degrees, 178 degrees, 90 degrees. That’s with delay slowed down to an impractical 1000ms!

Serial printing seems to work fine. There are some serious errors (it sometimes prints the direction measurement multiple times for the just one tick of movement, and it sometimes prints the incorrect direction), but I think that is caused by loose wiring in my breadboard and jumper wires. I ought to add a resistor haha.

I am using this library: GitHub - frodofski/Encoder_Polling
And the EC11 rotary encoder: http://www.alps.com/prod/info/E/PDF/Switch/Encoder/EC11/EC11.PDF
Here is my code:

#include "Encoder_Polling.h"
//library from http://forum.arduino.cc/index.php?topic=174967.0
#include <Servo.h>

Servo myservo2;
const int encoderPin_A = 8;
const int encoderPin_B = 9;

int counter = 0;

void setup()
{
  
  Serial.begin(9600);
  myservo2.attach(13);// attaches the servo on pin D10 to the servo object
  encoder_begin(encoderPin_A, encoderPin_B); // Start the decoder

}


void loop() 
{  
 
  int dir = encoder_data(); // Check for rotation
  
  if(dir != 0)              // If it has rotated...
  {
    Serial.println(dir);   // Print the direction
  
   if (dir = -1)
   {
    myservo2.write(2); //stick fully to the left.  //might need to be mapped to the speed of rotary encoder switching (frequency)
    delay(1000); // wait for servo to catch up     ////Might need to be mapped to how many increments the rotary encoder has turned.
    myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
    delay(1000); // wait for servo to catch up. 
    
    }
     
     if (dir = 1)
     {
     myservo2.write(178); //stick fully to the right. //might need to be mapped to the speed of rotary encoder switching (frequency)
     delay(1000); // wait for servo to catch up. //Might need to be mapped to how many increments the rotary encoder has turned.
     myservo2.write(90); //stick back to center. //might need to be mapped to the speed of rotary encoder switching (frequency)
     delay(1000); // wait for servo to catch up.
     
    }
  }
}

Got any theories why the servo is doing this?

if (dir = -1)
if (dir = 1)

Oh dear

There are some serious errors (it sometimes prints the direction measurement multiple times for the just one tick of movement, and it sometimes prints the incorrect direction), but I think that is caused by loose wiring in my breadboard and jumper wires. I ought to add a resistor haha.

No, that's called "switch bounce". It's not normally a problem for encoders as the bounces are all reversible and it will settle. But if you print something on every bounce, you will get a lot of printing.

A resistor and capacitor on the encoder lines is sometimes required to reduce this bouncing. There's nothing 'wrong' with your current wiring.

UKHeliBob:

if (dir = -1)
if (dir = 1)

Oh dear

Thanks. I see it now. Should've been a

==

not

=

laughing at myself
It's definitely faulty wiring because when I wiggle the wires a tiny bit, it prints readings and moves the servo erratically. I'll dig through my box of scrap wire to find replacements.

triops124:
How do I leave room for potentiometer voltage instability as mentioned?

My favourite way of doing this is to use a low-pass filter.

First, sample your pot at regular intervals. I like to use 4ms. By storing bits 10 to 3 in a byte, we get a value that is accurate to 4ms and that rolls over every second - plenty of time.

Second, low-pass filter. A very, very simple "recursive filer" newV = oldV * .75 + .25 * sampleedV . I use floats, because the value is a statistical average and not an actual value.

byte potTime;
float potValue;

void loop() {
  byte prevPotTime = potTime;
  potTime = (byte)(millis()>>2);
  if(potTime == prevPotTime) return; // not time to re-read the pot yet

  float oldPotValue = potValue;
  potValue = .75 * potValue + .25 * analogRead(potPin);

  // compare old pot value with new pot value
  // inster your working code here
}

PaulMurrayCbr:
My favourite way of doing this is to use a low-pass filter.

First, sample your pot at regular intervals. I like to use 4ms. By storing bits 10 to 3 in a byte, we get a value that is accurate to 4ms and that rolls over every second - plenty of time.

Second, low-pass filter. A very, very simple "recursive filer" newV = oldV * .75 + .25 * sampleedV . I use floats, because the value is a statistical average and not an actual value.

byte potTime;

float potValue;

void loop() {
 byte prevPotTime = potTime;
 potTime = (byte)(millis()>>2);
 if(potTime == prevPotTime) return; // not time to re-read the pot yet

float oldPotValue = potValue;
 potValue = .75 * potValue + .25 * analogRead(potPin);

// compare old pot value with new pot value
 // inster your working code here
}

That is so cool! Thanks!