Download PDF Parts List View on YouTube Download Code

What do you do when you need a big servo motor but don’t want to spend a big amount of money? Make your own, of course!



Servo motors are essential components when working with robotics, model aviation, and other animated projects. They are used in all kinds of industrial applications as well.

Servo motors, for those not familiar with them, are motors whose shaft position can be precisely controlled by an external signal. Unlike stepper motors, which also have controllable shaft positions, servo motors don’t need a reference point to start with. You just specify the angle of shaft rotation, and the servo motor will oblige by moving into that position.

Large industrial servo motors use sophisticated controllers and cost quite a bit of money. Smaller, hobby-grade servo motors (sometimes called “RC Servo Motors”) are much more affordable, but they have their limitations.

The biggest limitation with hobby-grade servo motors is the amount of power, or torque, they can provide.   The larger the motor, the greater the torque, but this gain comes with a corresponding price increase.

Another limitation is the range, or degrees of rotation, that these motors are made for. The most common hobby-grade servos rotate 180 degrees, some models are available that have 270 degrees of rotation.

But what do you do if you want a smaller, or larger, amount of rotation? It’s entirely possible that you might want to rotate more than 360 degrees. And what if you need a large amount of torque, greater than that provided by inexpensive hobby servo motors?

The answer, of course, is to make your own servo motor. A custom design, based upon your specific needs.

In fact, I know someone who requires a custom servo motor.

Jeremy Fielding

I’m sure most of you are already familiar with Jeremy Fielding through his YouTube Channel.  For the few of you who are not familiar with Jeremy’s work, he is a mechanical wizard who builds amazing projects in his workshop, many of them using 3D-printed or reclaimed materials. If you want to know anything about mechanical engineering or working with motors, then Jeremy is the fellow you need to know.

Jeremy and I were chatting, and he mentioned that he required a very large servo motor for one of his projects.  Not only does it need to have a lot of torque, it also has to be able to turn two full revolutions (720 degrees) instead of 180 degrees.  All driven by a signal intended for a standard hobby-grade servo.

So we decided that the easiest way to meet his requirements was to build a custom servo motor, using a high-torque DC motor.

Now, of course, your requirements may differ from Jeremy’s, and that’s fine – this custom design can be tailored to just about any application.

Before we get into the design and the hardware, let’s just review how a servo motor actually works.

Servo Motors

As I’ve already mentioned, a servo motor is a motor whose shaft position can be set using an external control signal.  

With a standard hobby servo motor, that control signal is a logic-level PWM waveform. The pulse width of the PWM waveform is how the servo position is controller.

The width of the pulses ranges between 1000 and 2000 microseconds. At 1500 microseconds the servo is in its Top Dead Center (TDC) position, on a standard 180-degree servo motor this is the 90-degree position.

Internally, a servo motor consists of a DC motor driven by a controller. The controller takes in the PWM signal. Measures its pulse width and turns the motor accordingly.

The key to making all of this work is that the motor shaft is coupled to a sensor, which reads the motor position. This position information is fed back to the controller, which then makes the appropriate adjustments to the motor speed and direction.

This diagram illustrates how it all fits together.

Note that the controller can be either a digital or analog design, as both types of circuits are used in modern hobby servo motors.

So by building our own controller and position sensor, we should be able to turn just about any brushed DC motor into a servo motor.

Controller Design Consideration

Here is a summary of what we would need to build our own custom servo motor:

  • A DC Motor with enough torque to suit our requirements.
  • A motor driver with enough current capability to drive the above motor
  • A microcontroller to compute the motor movements and provide a signal for the motor driver.
  • A position sensor of some sort.  There are a few possibilities here

For our demonstration, we will be using the following components:

  • A 12-volt DC gear motor.
  • A 5 to 3.3-volt Logic Level Converter.
  • A Cytron MD25HV H-Bridge motor driver.
  • A Seeeduino XIAO microcontroller.
  • A 10-turn potentiometer

Here is how these fit into the servo motor design.

Seeeduino XIAO Microcontroller

We have used the Seeeduino XIAO before, it’s a low-cost Arduino-compatible microcontroller that is based upon the SAMD21 processor.

Seeeduino XIAO Other Functions

I selected the XIAO as it is inexpensive, tiny, and easy to work with. I also chose it as it has a 12-bit analog to digital converter (ADC), as opposed to the 10-bit ADC that the Arduino Uno and other Arduino AVR boards have.

We will be programming our XIAO using the Arduino IDE. If you haven’t used the IDE with the XIAO before, you’ll need to set it up first. This involves (a) adding a new board JSON file to your Arduino IDE Properties window and (b) installing the board using the Boards Manager.

It’s a pretty simple installation, and you’ll find all the details and instructions in the article I did about using the Seeeduino XIAO.

Logic Level Converter

We will also be using a logic-level converter in our design, as the XIAO is a 3.3-volt device and most PWM sources provide either 5 or 6 volts.  Of course, we could also have just used a transistor for this, but the logic-level converter is inexpensive and very easy to use.

I used a 2-channel bidirectional logic level convertor, simply because I have a drawer full of them! The bidirectional feature is not being used, so a convertor that has unidirectional channels can also work fine. If you do use a unidirectional converter, remember that the servo connector is the 5-volt input and the 3.3-volt output goes to the XIAO.

Cytron MD25HV Motor Driver

I chose this driver simply because it’s the same one that Jeremy will be using in his project. You certainly don’t have to use it, as any motor driver will work as long as it has (a) a PWM input to control motor speed and (b) a direction control.

The MD25HV has the following specifications:

  • Bidirectional control for one brushed DC motor.
  • Operating Voltage: DC 7V to 58V
  • Maximum Motor Current: 25A continuous, 60A peak
  • 5V output for the host controller (250mA max)
  • Buttons for quick testing.
  • LEDs for motor output state.
  • Dual Input Mode: PWM/DIR or Potentiometer/Switch Input.
  • PWM/DIR Inputs compatible with 1.8V, 3.3V, 5V, 12V and 24V logic (Arduino, Raspberry Pi, PLC, etc.).
  • PWM frequency up to 40kHz (output frequency is fixed at 16kHz).
  • Overcurrent protection with active current limiting.
  • Temperature protection.
  • Under-voltage shutdown.

One very nice feature about this motor driver is that it has a regulated 5-volt output, which we can use to power the Seeeduino XIAO.

10-Turn Potentiometer

We will be using a 10-turn 10K linear-taper potentiometer as a position sensor for this design.

If you are building a servo motor that is only traveling 180 degrees, then a standard potentiometer will work just fine. In fact, most commercial servo motors use a standard pot. Just be sure it is a linear-tapered potentiometer, which means when the shaft is in the middle position, the resistance between the wiper and both legs is exactly half of the pot’s total resistance.

It can also be another value other than 10k, anything above 5K will work just fine.

You’ll also have to figure out how to couple the potentiometer to your motor shaft.  In my case, I got off easy, as Jeremy was kind enough to provide a 3D-printed demo that had the motor coupled to the potentiometer via a number of gears.

Alternate Position Sensors

We are using a potentiometer in our design today as it is (a) inexpensive and (b) really available. It is also the type of sensor used in just about every hobby servo motor, which is one of the reasons that small hobby servos are so inexpensive.

But a potentiometer certainly isn’t the only type of position sensor, nor is it the best.  There are several alternate sensors that would probably improve the performance of our design.

Rotary Encoders

We have looked at rotary encoders before, these are devices that send a series of pulses out when rotated, as opposed to changing resistance.  Internally they employ techniques like optical sensors, so they have the advantage of not wearing out as potentiometers do.

The problem with standard rotary encoders is that they are incremental, not absolute, indicators of motion.  To determine absolute position, most encoders add a third track and photosensor as an indicator zero reference track; the shaft must rotate enough to pass the zero reference position for this to signal.  This adds to both their complexity and cost.

The accuracy of a rotary encoder is determined by the internal disk that rotates and contains evenly-spaced openings, which interrupt the photosensors.  The finer this gap is, the more accurate the encoder can be.  A standard rotary encoder, such as the ones we examined in the previous article, would not be suitable for this application.

As with potentiometers rotary encoders can be affected by dirt and other contaminants if they are not completely sealed.

Capacitive Encoders

These devices, as their name implies, use variations of capacitance to sense rotation and position. Unlike optical encoders, these devices use a repeating, etched pattern of conductors on the moving and non-moving parts of the encoder. As the encoder rotates, the relative capacitance between the two parts increases and decreases, and this change in capacitance is sensed.  Multiple tracks can be added to determine absolute position.

CUI Devices make the AMT10 Series of capacitive sensors, which would be quite suitable;le for this application.

These devices are not as susceptible to contamination as potentiometers and rotary encoders are.  They would be an excellent choice for our design!

Hall Effect Sensors

Another fine choice for a position sensor is based upon the Hall Effect principle, which is another subject that we have discussed in previous articles and videos. Hall Effect devices can be used to sense current flow through a conductor that is part of the sensor, or the presence or absence of a nearby magnetic field.

In this type of sensor, a magnet is fixed to the motor shaft. A sensor like the Melexis MLX90367, which is specifically designed for this application, senses the change in magnetic flux and can report the angular position of the motor shaft through a full 360 degrees.

This type of sensor is used in automotive applications, and would probably be a great sensor for Jeremy’s “self-driving go-kart”!

Other Sensors

There are a few other types of sensors we can employ as well, such as resolvers.  and inductive encoders.  A great article that sums up the strengths and weaknesses of many rotary position sensors can be found on the Celera Motion website.

Controller Hookup

Now what we have all of our components together, it’s time to hook them up. Here is the wiring diagram for our servo controller:

If you are using the same logic-level converter as I did, then you can follow the chart on the schematic to wire it up. If you are using a different one, then you’ll need to adjust the wiring accordingly. See the description of the convertor for more details.

The “Power” jumper is optional, it is used to allow the controller to provide 5-volts through the servo connector to power the device that controls the servo. If you are using a device (like an Arduino) that has its own power, then you can eliminate this, or remove the jumper.

Preliminary Tests

Before we get to our actual servo motor sketch, there are a couple of smaller sketches that we need to run.  Actually, the first one is optional, but it’s a good idea to try it out, as it will confirm that you have the wiring correct.  The second one is mandatory, as we will be using it to get some values to plug into the final sketch.

Once again, you’ll need to have the Boards Manager for the Seeeduino XIAO installed before you start.

PWM Input Test

This one is optional, although the information it gives you can be used to “fine-tune” the final sketch to make it even more accurate.

The primary purpose of this sketch is to test the input section of our design, to be sure that we are capable of reading the pulse width of the incoming PWM. As you’ll recall, the pulse width is what determines the desired motor position, so reading it accurately is very important.

Interrupts Instead of pulseIn

Now, when it comes time to measure the width of a pulse, the first inclination is to use the Arduino pulseIn function.  After all, measuring pulse width is exactly what this function is intended for.

But although it seems convenient, pulseIn is not the best choice, at least not in this situation. There are actually a couple of issues with using pulseIn:

  • When the pulseIn function is running, the microcontroller can’t perform any other tasks.
  • It is possible that pulseIn will start sampling the pulse in the middle, rather than at the beginning. This can lead to false results.

So instead of using the pulseIn function, we will create our own function. One that uses interrupts.

If you are not familiar with using interrupts, there is a detailed explanation of this important concept in the article I wrote about using speed sensors with robot cars. As a quick refresher, an interrupt is a signal or event that stops the processor and sends it to an “interrupt handler function”. Once the interrupt has been “handled” the processor resumes where it left off.

Here is the code we are using to measure incoming pulse width using interrupts:

Our sketch starts by declaring a number of variables that will be used to calculate the pulse width. Note the use of the volatile statement preceding some of the variable definitions. This is used to load the variable into system RAM, instead of a register, and it is a common practice when the variable is being modified within an interrupt handler.

We also define variables to hold timestamps, which will be used to calculate pulse width.  And we define the pin we are using to receive the PWM pulses from our servo controller, pin 10 in this case.

In the setup, we set the incoming PWM pin as an input and also initialize the serial monitor. 

The setup also has a statement to attach the interrupt to our interrupt handler function measurePulse, which we will go over in a moment. Note the “CHANGE” parameter, which indicates that we are calling the interrupt handler every time the state of the pin changes. This is so we can call the interrupt handler at both the beginning and the end of the pulse.

Now take a look at the measurePulse function itself, which is the heart of getting this to work.  This is the function called every time the pulse changes state.

We need to determine what state the pulse is in, HIGH or LOW, in order to determine if we are at the beginning or the end of the pulse.  If the pulse is HIGH, then we are at the beginning, and we assign the value of micros() to the start variable.  This is the number of microseconds that have elapsed since the microcontroller was reset, and we are using it to mark the time at the beginning of the pulse.

If, on the other hand, the value is LOW, then we are at the end of the pulse. We then grab the current value of micros() and subtract the value we saved in the start variable.  The result is the exact amount of time, in microseconds, that the pulse was HIGH.  This result updates the pwm_period variable.

The Loop is pretty simple, we check to see that the interval between pulses has elapsed and if it has we print the value of the pwm_period variable.

To test this sketch, you can use one of those cheap servo testers. If you leave the Power jumper in the circuit, then you can power the servo tester directly.

Load the sketch, op[en the serial monitor and observe the results. If you are using a servo tester, you should get a range of at least 1000 to 2000 microseconds.

If you are using the source that you intend to drive your custom servo with, you can record those numbers, in order to improve the response of the final product.

Endpoint Test

While the previous sketch was optional, this one is not.  You’ll need the results from this sketch to get the values that you will plug into the final sketch.

The purpose of this sketch is to establish the “endpoints” for your custom servo motor. By ‘endpoints” I mean the value produced by the analog to digital convertor when the potentiometer is at its leftmost and rightmost position.

Here is the sketch, as you can see it’s pretty simple:

We start by defining the analog input pin on the XIAO. We also define an integer that will hold the value of the ADC output.

In the setup, you will note that we have used an analogReadResolution(12) command to set our ADC for 12-bit operation. If you omit this, then the ADC will default to 10-bits, to maintain compatibility with sketches made for the Arduino AVR boards.  

We also set up the serial monitor.

The Loop is about a simple as it gets. We just read the ADC and print its output to the serial monitor. After a short delay, we do it all over again. It’s as simple as that!

Load the sketch up to the XIAO and open the serial monitor. Now turn your servo mechanism to one extreme and make note of the reading on the serial monitor. Then turn to the other end and note that value as well.

You will need these values when we put together our final sketch.

PID Controllers

Our final design will rely upon something called a “PID Controller”. 

A Proportional Integral Derivative, or PID, controller is a control loop mechanism that employs feedback to apply a correction to its output value. Also called a “Three Term Controller”,  PID controllers have been used for nearly a century to control everything from HVAC systems to automotive cruise controls and maritime guidance systems.

A PID controller is a Closed Loop system, meaning that the final output is fed back to the input. If the output does not match the desired input, then a correction signal is applied to eliminate the error.

The input to a PID controller is called its “Setpoint”. In the case of our servo motor controller, the Setpoint is the desired position of the motor shaft.

The measured output of the PID control system is referred to as a “Process Variable”.  In our servo controller design, this would be the threading from the potentiometer.

The difference between the Setpoint and Process Variable is called an “Error Value”.

A diagram of a PID Controller is shown here.

Note the four values highlighted in yellow:

  • Setpoint – The intended value, determined by the servo PWM input.
  • Error Value –  The difference between the intended value and the measured value.
  • Control Variable – The signal that is sent to the motor to move it into the correct position.
  • Process Value – The measured position of the servo motor shaft.

In order to rectify any differences in the Error Value, we apply proportional, integral, and derivative calculations to the data.

The Proportional process is directly proportional to the Error Value. The amount of this process that is used in the final equation is calculated using a gain factor, represented as Kp.

The Integral process relies upon previous error values. It integrates this value over a period of time. The amount of Integral processing used in the final result is controlled by a gain factor Ki.

And finally, the Derivative process is an estimate of the future trend of the control mechanism.  This can serve to provide a damping effect, and its gain is represented by Kd.

PID Tuning

Setting the three gain factors, Kp, Ki, and Kd, is a procedure known as “PID Tuning”.

For our servo design, I found that we really only needed to use the Proportional process and that a gain of 1 (in other words, no gain) was sufficient. But your mileage may vary, depending upon the application for your custom servo motor.

If you need to tune your servo controller, the following points will be useful to know:

  • Proportional Gain Kp – This controls how fast the motor will move. If set too high, the motor may overshoot its target.
  • Integral Gain Ki – This affects how the servo moves at the end of its travel. If necessary, you can include this to prevent the motor shaft from oscillating.
  • Derivative Gain Kd – This can apply a damping effect, to reduce any overshoot when the servo moves into its desired position.

I would suggest that you use your sketch with the values I have entered and give it a test. If necessary, you can adjust the three gain values to get everything operating to your exact requirements.

PID-based Design Code

Writing our own PID controller code would be a mammoth undertaking. Fortunately, there are a number of great PID libraries that we can use to simplify the task.

Our sketch will make use of the Arduino PID library, which you can install using the Library Manager in the Arduino IDE.

Open your Library Manager and filter by “PID”. Several libraries will be displayed, the one you want is simply called “PID” and is by Brett Beauregard.

Once you have the PID library installed, you can enter the code, which is shown here:

We begin by including the PID library that we just installed.  After that, we set up a few PID variables, which should make some sense now that you are familiar with the operation of PID controls.

You’ll notice that the variables Kp, Ki, and Kd are the gain settings for Proportional, Integral, and Derivative modes.   In the current sketch, I am only using Proportional, with a gain of 1, however, you can coke back later and “fine-tune” these settings if you wish.

After defining the PID variables, we create a PID object, named myPID.  We pass all of our PID variables to this object and use the DIRECT mode, which indicates that we want to move forwards – this is the most common mode of operation.

The variables after that should look familiar, as they are the same ones we used in our interrupt-based pulse width demo earlier.

You will notice two variables, pot_min and pot_max. These are the values you obtained when you did the endpoint test earlier. You will need to replace the values in my sketch with the ones you noted during that endpoint test.

We also define the connections to the servo PWM input, the potentiometer we are using as a feedback sensor and the Cytron motor controller.  If you wanted to use a different motor controller, it would be a simple matter of defining more variables to suit its parameters.

Most of the Setup is pretty self-explanatory, we set our connection input and outputs up and set the ADC to 12-bit resolution.  We also attach the interrupt handler we are using to measure incoming pulse width.

One new thing we are doing in Setup is to initialize the PID controller. It is set to AUTOMATIC so that it runs constantly. 

We also define the Output limits as being a range of -255 to 255. This is the value that we will be using for the PWM signal driving the motor, the negative numbers indicate that the motor should be turning in reverse.  We also set the sample time for the PID controller.

We have already seen the measurePulse function, which is called during every interrupt condition to measure the incoming pulse width.

In the Loop, we perform the same checks as we did during our input test. Then we get the value of our potentiometer.

We then use a couple of map functions to map both the incoming pulses and the potentiometer value to a range of -255 to 255. This way we can compare the two directly.

Now the PID magic happens, all with one simple line of code. We have the PID controller compute the output, which will be the value we need to send to the motor to correct the shaft position.

If the result of the output is zero, then there is nothing to do, as the motor is already in the correct position. But if it isn’t zero, then we see if it is negative or positive.

If it’s positive, then the output is the motor PWM speed value, which we write to the motor controller with an analogWrite command. We also set the direction input of the motor HIGH, so it will spin forward.

If it’s negative, we will be getting a negative output value, which we can’t use for PWM. So we use an abs() function to make it positive (we could also have just multiplied it by negative one to achieve the same result).  In this case, we set the motors’ direction control LOW to make it move in reverse.

Then we finish the Loop and repeat the process.

Hook your motor controller up to your circuit and load the sketch to the XIAO. I would suggest having everything (servo controller and motor) close to the center position before you power it up.

Now try out the servo controller, the motor should move into the proper position as you move the controller knob. If it goes the wrong way, then you probably have your motor wired in reverse!

Otherwise, you should see your custom servo motor in action! Remember, you can tune the PID gain variables to make it behave exactly as you want.

Congratulations, you have just created a custom servo motor using a DC motor!


As you have seen, any DC motor can be used as a servo motor when coupled with a position sensor and a PID controller.   A potentiometer and Seeeduino XIAO make this a very inexpensive setup.

And if you want to see a practical application for this technique, then be sure to check out Jeremy’s video series on building the “self-driving go-kart” to see his servo in action.

Hope you enjoyed the article!


Parts List

Here are some components that you might need to complete the experiments in this article. Please note that some of these links may be affiliate links, and the DroneBot Workshop may receive a commission on your purchases. This does not increase the cost to you and is a method of supporting this ad-free website.




Code used in the article – All the code presented here in an easy-to-use ZIP file.

Arduino PID Library – The Arduino PID Library.

PID Controllers – A Wikipedia article which explains PID controllers in depth.


Build a Custom Servo Motor with a DC Motor
Build a Custom Servo Motor with a DC Motor
Article Name
Build a Custom Servo Motor with a DC Motor
Build a custom servo motor using a DC gear motor. We will use a Seeeduino XIAO as our controller and a potentiometer as a position sensor, as well as a Cytron motor driver (although you could substitute another motor driver if you wish).
Publisher Name
DroneBot Workshop
Publisher Logo
Notify of

Inline Feedbacks
View all comments
Mike C
2 years ago

Fantastic preparation and great explanation – very clearly done! Love your work!

2 years ago

I Agree with Mike C! Very well laid out Webpage and video. The webpage follows the video very nicely and clearly! Thanks! More of these please!!

Trushal sorathiya
2 years ago

hey where i buy this DC Motor

1 year ago

The motor depictted is a car window regulator motor. it is a geared dc motor.

1 year ago

thanks, nice job

Steve Middleton
1 year ago

Awesome project! I am trying to do it with the HiLetgo BTS7960 43A High Power Motor Driver Module. Here is the link to it. have a few of them on and I can’t afford to buy the MD25HV used in this tutorial. Could someone help me adapt this tutorial to using the BTS7960 H-Bridge instead? Thanks in advance for the help!

debebe asefa
1 year ago

You are naturally gifted teacher, keep it up buddy!!!