DIY Servo Motor




Introduction: DIY Servo Motor

DC Motors can be made to turn either clockwise or counter-clockwise by changing the polarity of the voltage applied to their terminals. The torque that is generated at the output shaft can be scaled up or scaled down by using a gear train. In most motors, like the one shown below, the gear train scales up the torque of the motor by using a reduction gearing that outputs a much higher torque (albeit at the cost of a much reduced output RPM).

The problem with DC motors is that when they have a voltage applied to their terminals, they tend to rotate forever in a particular direction, stopping or reversing the motor can only be achieved by cutting off electric supply or reversing polarity. In a DC Motor, speed control can be achieved by varying the terminal voltage but position control of the shaft is very difficult to implement.

Servo motors on the other hand, allow us to control the position (or angle) of the motor output shaft. This can be very useful when we want to move a control surface such as a rudder or a thruster to a particular position.

Step 1: Dealing With Cost

Servo motors are expensive. They get more and more expensive as their output torque increases. Digital servos are even more expensive than analog ones and I am not sure that one can even buy servo motors with torques higher than about 30 kgf-cm.

I needed a servo motor that could generate 120 kgf-cm of torque for a robotics application. As you can see from my extrapolated price graph, this type pf servo would have cost me a few hundred dollars!

To get around this, I decided to make my own servo motor using a cheap (under 20$) DC motor and some simple electronics.....

Step 2: How It Works...

In order to control the shaft position of a DC motor (and thereby convert it into a servo motor), you need to be able to ‘encode’ the position of the shaft. This ‘current position’ will be compared against an ‘desired position’ and a ‘positional error’ will be generated. Voltage applied to the motor terminals will be so as to cause the shaft to turn to reduce ‘positional error’ to zero. This sort of a ‘feedback’ system is also used in commercially available servo motors.

To implement the feedback loop you will need to fix the shaft of a rotary potentiometer to the foundation of the motor while allowing the potentiometer’s body to rotate freely with the motor shaft. This arrangement is known as a shaft encoder. Now, as the motor shaftrotates , there will be a corresponding angular movement between the potentiometer shaft and its body. By sensing the voltage at the wiper terminal of the potentiometer, you can measure the angular position of the motor shaft. This angle will then be fed into the feedback loop allowing software on a microcontroller to control the angular position of the motor shaft.

Step 3: Circuit Diagram

I used Fritzing to create the circuit diagram for my DIY servo motor. You can download the circuit by clicking the link below.

To be able to turn the motor shaft in both directions, you will need to use a H-bridge IC like an L293N. Any microcontroller can be used to control the servo, in the circuit above, I use an Arduino Mega.

Step 4: Code

Download the arduino sketch (customServo.ino) that controls the motor using the links below. Within the code is the c++ class DCMotor. This class has a member function GoToAngle(i,j) where 'i' is the desired angle of the shaft and 'j' is the turning speed of the motor. You can call GoToAngle() repeatedly or whenever required to bring the motor shaft to any desired angle. The code is heavily commented at every step so that it is easy to read.

The embedded video demonstrates the whole build process. The code shown in the video is of a slightly older version and is not the same as contained in customServo.ino



  • Creative Misuse Contest

    Creative Misuse Contest
  • Water Contest

    Water Contest
  • Metalworking Contest

    Metalworking Contest

47 Discussions

Hello vu2aeo,

Thnx for your code example, its was very helpfull! I changed your code a bit so it would work with my adafruit motorshield. It use it to just set the motor in a desired porsition. It works fine for angles higher than around 70 degrees but if the potentiometer is set at at lower angle than 70 degrees, the program freezes(altough the motor shaft keeps turning). For higher angels it works perfectly. Have you run into a similar problem?

3 replies

Sorry, I'm starting to lose my patience. Please read the instructable properly. The circuit diagram and its software, source code, everything is given. I'm happy to try and clear any genuine questions you may have, but unfortunately it appears that you are not really trying to apply yourself.

Many potentiometers are designed for use in audio equipment and the resistance of the wiper is not linear at all. It may be logarithmic. So a change of 10 degrees angle will not translate to the same analog reading when the knob is at different positions.

I think this may be the problem you are facing.

You may need to manually check the wiper resistance values at different positions of the knob to verify if this is what is happening...

sir, tq for the tutorial...

but can i ask about how to control it multiple?

can i just use the signal(wiper) pin to put as input...?

3 replies

I quite certain that would work......but you would need to remember to
poll BOTH the wiper pins every so often. You will need to modify the
code for this.

Im sorry but i never heard the term... Cant really blame mr cause im new... I google but not found...
So... Erm... What is poll?

Ermm... Found it... Sorry...
Tq for reply and take your time...
(im not good with english)

hi there!

are you using the motor driver as H-Bridge( 4 quadrant) configuration or 2 quadrant operation?

Because i am thinking of controlling 2 servomotors with single motor driver.

I feel we can use 2 quadrant operation because we are not using braking.. only direction control.


1 reply

I quite certain that would work......but you would need to remember to poll BOTH the wiper pins every so often. You will need to modify the code for this.

it is very useful. but where can i get the code of obey order that is showed in the video

I have a doubt...

I have done something like this project, but I have used a encoder. When I put some weight, the motor turn hot (like this is overworked for the change of sense in the turn of the motor). What can I do?

sorry, just use santaservo.ino. All the code is included in there.

How can I use the motor shaft when it is attached to the potentiometer

1 reply

If you look closely in the video, you will see i have attached a matchstick to a shaft coupler. I connect my output shaft (in my case the arm of my robot) to this shaft coupler. As long I as don't rotate the motor shaft beyond the physical limits of the potentiometer support, it works just fine and allows me about 200 degrees of movement.

In your gotoAngle() -function you have the calculation of the current angle. I couldn't get it to work without some modifications. (I'm using Arduino UNO.)

I changed your code:

int currentAngle = (analogRead(potPin) -


to this:

int currentAngle = ((Float)analogRead(potPin) - POT_VALUE_MIN)/(POT_VALUE_MAX - POT_VALUE_MIN) * MAX_ANGLE;

The problem I faced with the original code was that all the values in the calculation were integers, so the division gave me allways either 0 or MAX_ANGLE. Using Cast (Float) on one of the values I managed to keep the decimals in the calculation.

The second thing I noticed is that it was not giving the correct angle. If you test the calculation with the given max values, it doesn't output the currentAngle == MAX_Angle.

2 replies

casting that int into a float seems like an excellent idea. thanks.

I've always been able to get fairly accurate angles with this setup. perhaps the potentiometer you are using is not linear...logarithmic perhaps? in which case you may need to add a small function to convert the log readout of the potpin to a linear readout.

If you test your currentAngle calculation with these values:




analogRead(potPin) = 700


currentAngle = (700 - 200) / 700 * 180;

This gives you that currentAngle is 128.57, not 180 as it should?