So pick up your jumper wires, and we’ll spin a servo motor with the Raspberry Pi and make it stop at any angle!
What Is a Servo Motor?
A servo motor is a DC motor that lets you control its angle. You can set it to turn 90 degrees, stop, then go back 90 degrees. They’re useful when you need precision on an automatically-moving part. Inside a servo motor are three parts: DC motor, potentiometer, and a circuit that controls the motor. The potentiometers in servo motors are resistors, just like the resistors you use when lighting up LEDs. The exception is that they can change resistance values when you turn them. In servo motors, the potentiometer is geared to the DC motor so that it will turn when the DC motor spins. This lets you know the motor shaft’s angle. The controller circuit tells it to stop when it reaches a certain angle. With that in mind, using servo motors with the Raspberry Pi means you’re telling the controller circuit to spin the DC motor until it reaches a certain angle.
What Is Pulse Width Modulation?
Pulse Width Modulation (PWM) is the heart of this whole servo-moving thing. It’s a method of controlling the timing between pulse waves in a PWM signal. As a more layman-friendly explanation, imagine the Raspberry Pi emitting 3.3V from pin 7. On an oscillator making a graph of voltage over time, that would make a plot on the 3.3V side, which becomes a long line as time passes by. When it suddenly drops to 0V, the oscillator makes a vertical line to the 0V side, then makes a horizontal line from there over time. That is called a pulse wave. PWM is when you control either the distance between two pulse waves (length of 0s) or the length of the pulse itself (length of 1s). This is also known as a duty cycle and is what you change while using the ChangeDutyCycle() function. PWM control is an important function in many microcontrollers, not just the Raspberry Pi. It lets you control and output so much other stuff, all the while just using a tiny bit of electricity to work.
Things You Need to Spin a Servo Motor
Servo motor3 Jumper wiresRaspberry Pi (any model except the Pico)
Steps to Using a Servo Motor
Servo wires can come in different colors, depending on the manufacturer. Wire them to the Raspberry Pi like as follows:
White, red, black: white = pin 7, red = 5V, black = GNDYellow, red, brown: yellow = pin 7, red = 5V, black = GNDBlue, red, black: blue = pin 7, red = 5V, black = GND
The wire you’re connecting to pin 7 is the servo’s “signal” wire. This is connected straight to the controller circuit. If you don’t have enough male-to-female jumper wires, you can make your own with a male-to-male and a female-to-female jumper wire connected together. Tip: If you’re having trouble looking for pin 7, hold your Raspberry Pi in a way that the GPIO pins are placed on the right. Starting from the top-left pin, that would be pin 1. To the right of it is pin 2. Below pin 1 is pin 3 and so on. Tip: it’s easier to see the Raspberry Pi move when you place a piece of tape on it, but ideally, you should use the “horn” that comes with the servo straight from the box.
How It Works
As with most of the other instructions we do, we are dividing this code into four parts: As always, these are not “standard” divisions. It’s just good practice to divide your code into smaller bits when programming things to make editing and debugging much easier!
Import Commands
The import commands section is where you’re supposed to load your modules. Here, we’re using two modules: RPi.GPIO and time. import RPi.GPIO as GPIO imports the RPi.GPIO module and lets you control the black pins (GPIO) where you fit the code wires in. The latter part of this line, as GPIO, declares a new variable named GPIO. Here, GPIO will always mean RPi.GPIO, unless changed in the later part of the code. You can replace GPIO with any other variable name you want! from time import sleep is another way to import a module, but instead of importing the whole module, you’re only importing part of it. Here, we just took the sleep part of the time module, which lets you use the sleep() function, pausing the code for a given amount of seconds.
Setup Commands
Setup commands let you set up and define things before going to the looped part. servoPin = 7 defines the variable servoPin and gives it the value 7. We’re using this to say that the pin for controlling the servo motor will be pin 7. Why define a pin number? Sometimes, when you change your mind and think, you want to move it to pin 40, for example, then it’s easier to change just one pin number than have to look for the number 7 all over your code. GPIO.setwarnings(False) stops a warning message you will see when you run a Python script that uses the GPIO pins. It’s set to “True” by default. GPIO.setmode(GPIO.BOARD) defines which pinout you’re using. There are two types: BOARD and BCM. In BOARD, you’re defining pins based on where they are. Pin 1 is at the top left, pin 2 to its right, and so on. BCM stands for “Broadcom” and picks pins based on their Broadcom SOC channel. This is a pin-specific number. Unlike BOARD, it’s easier to make mistakes with BCM, as the pin number you’re going to use changes based on which Raspberry Pi model you’re using. GPIO.setup(servoPin, GPIO.OUT) defines pin 7, that pin we defined earlier as servoPin, and assigns it as an output pin. pin7 = GPIO.PWM(servoPin, 50) is another variable we’re defining. GPIO.PWM(servoPin, 50) means that you’re making the servoPinoutput pin release a PWM signal. We’ll talk about PWM (Pulse Width Modulation) later. But to sate some curiosity, PWM means you’re turning the pin on and off at regular intervals. The second part, 50, tells the pin to be turned on to 50% of the interval, then turn off. Lastly, pin7.start(0) makes pin 7 start doing the PWM thing, making it ready to go!
Function Declarations
Programming complex code can be done in two ways: type and retype the same thing over and over and over again or type it once in a variable and reduce ten lines of code into one line. “Function” makes that easier, and no programmer in their right mind would favor the former over a nice function. The functions in the declarations are just one giant “function in a function.” We are starting at the bottom function, sweep(), first, which uses the other function, angleToDutyConvert(), inside it.
Function #1: sweep(degrees)
def sweep(degrees): defines a function name, sweep, and gives it one parameter: degrees. We want this function to take an angle in degrees and sweep slowly until it reaches that angle. That’s while it prints out the current angle as it moves, so you’ll already know how far it’s gone. Normally, servo motors will move to a target angle as fast as they can. To make a servo motor “sweep” slowly, we’ll have to make it move and stop at a small angle until it reaches the target angle. To do that, we need a “for” loop. The line for pos in range(0, degrees, +5): is a for loop that saves the current position in degrees to a variable, pos, and loops between the starting part of the range() function, 0, and the max value in degrees while incrementing by 5. You can visualize it like this: The value for pos starts at 0, then moves by +5. The for loop checks whether it’s still below the max value (the degrees part). If it’s not, then it starts doing what’s inside the loop, then goes back to add +5 to pos. Since it used to be 0, the next value should be 0 + 5 = 5. It checks again and repeats until pos becomes either bigger or at the same level as degrees. The next part does things in reverse. Starting from the value of degrees, the servo will move by -5 until the value of posgoes down to 0. You may notice that each for loop has two lines: print(pos) and angleToDutyConvert(pos). The first one, print(pos), prints the value of pos into the console, which makes things easier to see. angleToDutyConvert(pos), on the other hand, is that custom function in our function.
Function #2: angleToDutyConvert(pos)
While this was mentioned earlier, it’s easier to explain after explaining what it’s going to be used in. angleToDutyConvert(angle) is a custom function, like sweep(degrees). So what’s this for? Remember that sweep(degrees) takes in a number in degrees. That’s great, but computers (and the servo’s controller circuit) don’t know what a “degree” is, yet they do know duty cycles. For most servo motors, they figure out the “angle” by listening to duty cycles. To convert a number from degrees to duty cycles, you should divide the angle by 18 and add 2 to the quotient. That’s exactly what the line dutyCycle = angle / 18 + 2 is for. The next part of the function does most of the legwork. GPIO.output(servoPin, GPIO.HIGH) turns pin 7 on, sending a signal to the servo motor. pin7.ChangeDutyCycle(dutyCycle) changes the duty cycle with the value from converting degrees to duty cycles, then sleep(0.15) pauses the code for 0.15 seconds. The last two parts, GPIO.output(servoPin, GPIO.LOW) and a second sleep(0.15), temporarily turn pin 7 off. They’re not much of a “vital” code but help with jittering, especially when you want the servo to hold its position.
Looped Commands
It’s time to make things work. Using a while loop, turn the servo motor with the Raspberry Pi for as long as you’d like. while True: is a while loop that never ends. Whatever you place inside it will get repeated forever, as long as you feed it electricity. But the real MVP here is the sweep() function, which is that function we made earlier. You can add more of these to make the servo motor sweep at different angles. Just remember that whatever number you put between the parentheses, that’s going to be the angle the servo will move in.