Lesson 97-2: Controlling a Servo Motor Using a Rotary Encoder
This project demonstrates how to build a precise servo motor controller using a rotary encoder. Unlike a potentiometer, a rotary encoder spins continuously, allowing you to make fine adjustments or rapid multi-turn changes without hitting a physical stop. This makes it an excellent choice for applications requiring high durability and precise control, such as robotic arms, camera gimbals, pan-tilt mechanisms, or any DIY project where you need to set a position with a physical knob.
Here are a few practical examples of what you can build with this setup:
- Creating a manual control panel for a robotic arm to set joint angles.
- Building a precise pan-and-tilt head for a camera or sensor.
- Developing a motorized valve or damper controller for home automation.
- Making an interactive art installation where turning a knob moves a physical element.
Hardware Components
To follow along with this guide, you will need the following parts. The rotary encoder can be a bare component or a module with a PCB that includes the necessary pull-up resistors. The wiring diagrams in the video cover both versions.
- Arduino Uno (or similar board)
- Rotary Encoder (with or without a PCB)
- Servo Motor (standard or metal gear)
- 3x Resistors (1kΩ to 100kΩ, only if using a bare encoder)
- I2C LCD Display (16x2)
- Jumper Wires and a Breadboard (optional)
Wiring Guide
This project involves connecting three main components to the Arduino: the rotary encoder, the servo motor, and the optional LCD display. The video provides detailed diagrams for both the PCB and bare-bones versions of the encoder. The core connections are outlined below.
Rotary Encoder: The encoder has two signal pins (often called A and B) and a push-button switch pin. For the PCB version, connect its two signal pins to Arduino pins 2 and 3, and the switch pin to pin 4. The power and ground pins go to 3.3V and GND, respectively. For a bare encoder, you must add pull-up resistors to the signal pins.
Servo Motor: The servo has three wires. Connect the signal wire (often yellow or orange) to a PWM-capable pin on the Arduino, which is pin 9 in the code. Connect the power wire (usually red) to the 5V pin and the ground wire (brown or black) to GND. For a powerful servo, the video recommends using an external 5V supply to avoid overloading the Arduino's voltage regulator, ensuring the grounds are common.
I2C LCD Display: The LCD uses the I2C protocol, which requires only two data connections. Connect the SDA pin to Arduino's A4 and the SCL pin to A5. Power the display with 5V and GND. The video also shows an alternative method of powering it from the ICSP header.
Code Explanation
The code is designed to be easily configurable. The primary settings are defined at the top of the sketch, allowing you to adjust the pins and behavior without diving into the logic. Here are the key variables you might want to change (in video at 17:52).
const int SW_PIN = 4;
const int PIN_A =2;
const int PIN_B =3;
const int homePosition = 90; //initial position
const int stepValue = 5;
const int servoPin = 9;//must be a pin that that is labeled with ~
- PIN_A and PIN_B: These define the Arduino pins connected to the encoder's output. If your servo moves in the opposite direction of your knob, you can simply swap these two values (in video at 17:32).
- SW_PIN: This is the pin for the encoder's built-in push-button switch. Pressing it will send the servo to the defined
homePosition(in video at 19:06). - homePosition: This is the angle (in degrees) the servo will move to when the switch is pressed, and it's also the servo's starting position at power-up.
- stepValue: This controls how sensitive the knob is. A value of 5 means the servo angle changes by 5 degrees for each detent you feel when rotating the encoder. Increase this for faster movement or decrease it for finer control (in video at 20:39).
- servoPin: This is the control pin for the servo. It is critical to use a pin that supports PWM (pulse-width modulation), which is indicated by a
~symbol next to the pin number on the Arduino. Pin 9 is a safe choice (in video at 21:03).
The main loop continuously reads the encoder's position. When a change is detected, it compares the new position to the old one to determine the direction of rotation. If you turn it clockwise, the servoAngle is increased by the stepValue, and if counter-clockwise, it is decreased. The code includes checks to ensure the angle stays within the valid 0 to 180-degree range for a standard servo.
The encoder's push-button is read as a digital input. When pressed, it pulls the pin low, triggering the code to reset the servoAngle to your defined homePosition (in video at 27:13). If you are using the LCD version of the code, a custom function handles clearing and updating the angle display on the screen to prevent text ghosting (in video at 27:56).
Live Project Demonstration
In the video demonstration, the setup is shown working with both a standard and a metal-gear servo. As the knob is rotated, the servo arm follows the direction of rotation, stopping at the 0 and 180-degree limits. The response is immediate and accurate. The video also shows the switch functioning, where pressing the knob instantly returns the servo to its home position, which can be a very handy feature for homing a mechanism (in video at 30:01).
Driving the servo from the encoder is covered in Lesson 97-1: Detecting Rotary Encoder Rotation and Pushing the Switch.
Related projects from this video
- Lesson 97-1: Detecting Rotary Encoder Rotation and Pushing the Switch
- Lesson 97-3: Controlling a Servo Motor Using a Rotary Encoder and Display Angle on LCD
Chapters
- [00:01] Introduction to Controlling a Servo with a Rotary Encoder
- [00:51] Wiring the PCB Version Encoder
- [06:55] Wiring the Bare Encoder with Resistors
- [09:56] Wiring the Servo Motor and LCD
- [13:28] Installing the Required Libraries
- [14:54] Testing the Basic Encoder Code
- [17:52] Modifying the Code for the Servo and Switch
- [20:20] Code Explanation for Servo Control
- [27:56] Explaining the LCD Display Function
- [30:01] Final Demonstration and Conclusion
/*
* Lesson 97-2: Controlling Servo Motor using Rotary Encoder
Download and resource page https://robojax.com/RJT453
*
* Watch full details video: https://youtu.be/6xVLbNlmK-g
This video is part of the Arduino Step by Step Course, which starts here: https://youtu.be/-6qSrDUA5a8
If you found this tutorial helpful, please support me so I can continue creating
content like this. Make a donation using PayPal or a credit card: https://bit.ly/donate-robojax
* This code is "AS IS" without warranty or liability. Free to be used as long as you keep this note intact.*
* This code has been downloaded from Robojax.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
* Updated by Ahmad Shamshiri on Jan 23, 2022
* Encoder Library - Basic Example
* http://www.pjrc.com/teensy/td_libs_Encoder.html
*
* This example code is in the public domain.
*/
const int SW_PIN = 4;
const int PIN_A =2;
const int PIN_B =3;
#include <Encoder.h>
// Change these two numbers to the pins connected to your encoder.
// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(PIN_A, PIN_B);
// avoid using pins with LEDs attached
const int homePosition = 90; //initial position
const int stepValue = 5;
const int servoPin = 9;//must be a pin that that is labeled with ~
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int servoAngle =homePosition;
void setup() {
Serial.begin(9600);
pinMode(SW_PIN, INPUT);
Serial.println("Basic Encoder Test:");
myservo.attach(servoPin); // attaches the servo on pin
myservo.write(servoAngle);//move servo to initial position
}
long oldPosition = -999;
void loop() {
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
if(newPosition > oldPosition)
{
int newStep = abs(newPosition - oldPosition);
Serial.print("Angle ");
Serial.println(servoAngle);
servoAngle -= stepValue;
if(servoAngle <0)
servoAngle =0;
myservo.write(servoAngle);
}
if(newPosition < oldPosition )
{
int newStep = abs(newPosition - oldPosition);
Serial.print("Angle ");
Serial.println(servoAngle);
servoAngle += stepValue;
if(servoAngle >180)
servoAngle =180;
myservo.write(servoAngle);
}
oldPosition = newPosition;//remember the new position
}
if( digitalRead(SW_PIN) == LOW)
{
Serial.print("Home: ");
Serial.println(homePosition);
servoAngle =homePosition;
myservo.write(servoAngle);
}
//Watch full details video: https://youtu.be/6xVLbNlmK-g
//delay(200);
}
Things you might need
-
Amazon
-
Amazon
-
Amazon
-
eBay
-
eBay
-
AliExpressPurchase an LCD1602 from AliExpresss.click.aliexpress.com
Resources & references
-
ExternalPurchase an LCD1602 from AliExpresss.click.aliexpress.com
-
External
-
External
-
ExternalPurchase ZK-5DA from Amazon UKamzn.to
Files📁
No files available.