Hey there! In this post I want to show you how to use a Micro Servo with Micropython controlled by a potentiometer. As with my other posts I will use a Raspberry Pi Pico to achieve this, but you of course can use also any other microcontroller that is suitable for this task.
Micro Servo with Micropython
Which components do you need for this project?
- Raspberry Pi Pico (or any other MCU with PWM and ADC functionalities that will do the job)
- Micro Servo SG90
- Potentiometer
- Breadboard
- Some Jumper Wires
Micro Servo SG90 – Basics
I have the “Tower Pro Micro Servo SG90” for example, which can be operated between about 4.8V and 6V. It has 3 pins, which are: VCC, GND and PWM (control-input pin). This Servo (alike many other ones that are similar to this one) needs to be operated via PWM (Pulse Width Modulation). If you wonder about what that means – click here to read more about PWM.
The servo will act according to the current duty cycle that a controller for example is sending to it. Theoretically they can turn 180° (90° in each direction). The settings for PWM is 50Hz (i.e. 20ms Period) for the frequency and duty cycle range between 1ms and 2ms (so 1ms duty cycle will mean 0° for the servo and 2ms will mean 180°).
Implementation
Since the Servo awaits some higher Voltage than the Pico can give (which is 3.3V), we can either use the VBUS Pin of the Pico which will provide 5V (only when powered over USB), or attach an external power source directly to it (which I would recommend). Next we will do the wiring, let’s go!
The wiring
Here is the wiring of all the parts.
Servo SG90 | |
VCC | Battery Power Supply (4.8V) |
GND | Common GND |
Signal (PWM) | Pico: Pin 20 / GP15 |
POT | |
Right outer Pin | Pico: Pin 36 / 3V3 Out |
Left outer Pin | Common GND |
Center Pin | Pico: Pin 34 / GP28 / ADC2 |
We will take the input of the potentiometer on any of the ADC-Pins (Pin 28 in this example) and then feed it into the duty cycle to tell the servo which position it should take.
The Code
Okay, we’ve done it all. Now what? The next step would be to care about how to control the servo via the MCU that we have at hand. The Pico (among other MCUs) has some built in functionalities for generating PWM-Signals (read here).
The only thing we need to do is to set the output pin for the signal that we will be generating (any GPIO-Pin will do), set the frequency to 50Hz and then set the actual duty cycle in a loop to tell the servo which position it needs to track, which we will do by reading the input of the potentiometer. We need to use the built-in method pwm.duty_u16(...)
to set the duty. This method awaits unsigned short values which means we need to provide values in between 0 and 65535. So we need to normalize the actual duty-cycle value into this range.
For this we can simply calculate the percentage of the time values and then convert them to unsigned shorts. We have 50Hz so a 20ms total Period. 1ms for the minimum which means 5% of whole period. 2ms then are 10% of the period. That gives us 65535 * 0.05 = 3276.75 for the minimum value and 65535 * 0.1 = 6553.5 for the maximum.
However, I found that with the servo I have bought, that not only it can move more than 180°, actually something near to 200°, but also that 1ms and 2ms are not the actual duty cycles that mark the min and max values. I would recommend that you start with the theoretical values (min: 3276, max: 6553) and then carefully expand them until you notice that the servo actually cannot move any further.
import utime
from pot import POT
from machine import Pin, PWM
MIN_DUTY = 1550 # 5 percent of 65.535 = 3276.75 (theoretical minimum)
MAX_DUTY = 8050 # 10 percent of 65.535 = 6553.5 (theoretical maximum)
pwm = PWM(Pin(15))
pwm.freq(50)
p = POT(28)
while True:
duty = int(MIN_DUTY + (MAX_DUTY - MIN_DUTY) * p.pct())
if duty > MAX_DUTY:
duty = MAX_DUTY
elif duty < MIN_DUTY:
duty = MIN_DUTY
pwm.duty_u16(duty)
Copy this code into a new File onto your MCU together with my POT class. Keep in mind that MIN_DUTY
and MAX_DUTY
are working with my servo so that it actually moves between 0° and 180°. You however probably should start with the theoretical values (see comments in code) to be on the safe side.
Well that’s it! I’d really appreciate if you’d leave a comment. I’m keen to know in which projects you are invested in and whether the post was helpful to you.
If you are also curious about how to read ADC-values with a POT, be sure to check my other post about POTs.