Sweep on a Servomotor SG90 in the BeagleBone Black
In this post, I show how to control the position of a Servomotor SG90 using the BeagleBone and the library that I have been written in C++ which you can find it here. The remarkable aspect is the use of the OOP paradigm to work with the SG90 servomotor.
The control of a servomotor position is done by means of a PWM signal with a period of 20ms. The pulse width of this signal should be between 0.5ms and 2.5ms, i.e., between 2.5% and 12.5% of the duty cycle to move the servo between 0° and 180°, respectively. Figure 1 shows a diagram of the signal and its corresponding servomotor position. As you can see in that figure, when you send a pulse width of approximately 0.5ms the servo will rotate to 0° when you send 1.5ms the servo will rotate to 90°, and finally, when you send approximately 2.5ms the servo will rotate to 180°.
If you have another servo, it can be used as well. The difference will be in the minimum and maximum values for the pulse width. In the case of the servomotor SG90, the exact values are 0.54ms and 2.5ms, in other servos, these values could be in ranges of 0.5 to 1.0ms for 0° and 2.0 to 2.5° for 180°. On the page of Luis Llamasyou can find a very well condensed way, general information about servomotors and how to control the SG90 with Arduino. It can be a good start point if you want to know more about it.
It is important to remember that the logic voltage for the BeagleBone is 3.3V. If the user provides a greater voltage, the BeagleBone could be damaged. Furthermore, if your servo is like the SG90, which needs 4.8V or more, you should provide an external power source, wiring its ground to the BeagleBone’s ground.
Circuit and components
The circuit can be seen in Figure 2. It consists of an SG90 servomotor, 4 AA batteries, and the BeagleBone.
The components are:
- 1 Servomotor SG90 4.8 - 6.0V
- 4 AA Batteries
- 1 Protoboard mini
- Jumpers male-male to make the connections
The PWM pin to control the servomotor is:
- PWM P8_13
Coding
An Servo
object is declared with global scope to initialize the PWM pin to control the servomotor.
1
2
// Declare the SG90 object
Servo myServo(P8_13);
This Servo
object is initialized with default values of 544444 and 2500000 for the minimum and maximum pulse width, respectively. It is important to note that the pulse width units in the BeagleBone are in nanoseconds instead of milliseconds. For this reason, these default values are used, instead of the typical values of 544 and 2500 which are in microseconds and are used in the Arduino attach() function.
1
2
3
4
5
6
7
8
9
// Overload Constructor
Servo::Servo (PWM newPWMPin) : pwmPin(newPWMPin)
{
angle = 0;
minimumPulseWidth = 544444;
maximumPulseWidth = 2500000;
this->InitServo();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Private method to set the Servo period for the PWM pin
void Servo::InitServo()
{
// Set the period of Servo in ns
pwmPin.SetPeriod(ServoPeriod);
std::string message;
message = "\nServo object with the next parameters / pins was created:\n" +
std::string("\tPWM Pin: ") +
this->pwmPin.GetPinHeaderId() + "\n" +
std::string("\tPWM Period: ") +
std::to_string(this->pwmPin.GetPeriod()) + "ns\n" +
std::string("\tMinimum Pulse Width: ") +
std::to_string(this->GetMinimumPulseWidth()) + "ns\n" +
std::string("\tMaximum Pulse Width: ") +
std::to_string(this->GetMaximumPulseWidth()) + "ns\n\n";
std::cout << RainbowText(message, "Light Blue");
}
Finally, the period for the PWM signal is declared and initialized to a constant value of 20000000 (instead of 20) inside the class. The next listings show the constructor for this Servo
object:
1
2
/* The standard period of 20ms to control any servo */
const int ServoPeriod = 20000000;
To rotate the servo you can use the SetAngle()
method which receives the angle en degrees and makes the mapping between this value to the corresponding pulse width. After that, this mapped value is sent to the PWM pin.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
Public method to set the speed rotation
@param int: the desired angle (0-180)
*/
void Servo::SetAngle(int newAngle)
{
angle = newAngle;
double mapping = (maximumPulseWidth-minimumPulseWidth)/180.0 * angle + minimumPulseWidth;
int pulseWidth = static_cast<int>(mapping);
pwmPin.SetDutyCycleByPeriod(pulseWidth);
std::string message;
message = "angle: " + std::to_string(angle) + " -> pulse width: " +
std::to_string(pulseWidth) + "ns\n";
std::cout << RainbowText(message, "Light Blue");
}
This mapping is a linear function that takes the angle and returns the corresponding pulse width taking into account the minimum and maximum pulse width values defined when the SG90
object is constructed at the begging of code. The graph of this mapping can be seen in Figure 3.
In order to make a sweep with the servomotor, two for loops
can be used in both directions, from 0° to 180° and vice versa, giving a delay of 1000ms to observe the movement and giving to the servomotor more than enough time to reach the desired angle.
1
2
3
4
5
6
7
8
9
10
11
12
13
// Sweep from 0-180
for (int angle = 0; angle <= 180; angle += 10)
{
myServo.SetAngle(angle);
Delayms(1000); // Wait 1000ms for the servo to reach the position
}
// Sweep from 180-0
for (int angle = 180; angle >= 0; angle -= 10)
{
myServo.SetAngle(angle);
Delayms(1000); // Wait 1000ms for the servo to reach the position
}
The complete code for this implementation is shown in the next listing:
SG90_1.1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/******************************************************************************
SG90_1.1.cpp
@wgaonar
21/07/2021
https://github.com/wgaonar/BeagleCPP
- Sweep a Servomotor
Class: Servo
******************************************************************************/
#include <iostream>
#include "../../../Sources/Servo.h"
using namespace std;
// Declare the Servo object
Servo myServo(P8_13);
int main()
{
string message = "Main program starting here...";
cout << RainbowText(message,"Blue", "White", "Bold") << endl;
// Sweep from 0-180
for (int angle = 0; angle <= 180; angle += 10)
{
myServo.SetAngle(angle);
Delayms(1000); // Wait 1000ms for the servo to reach the position
}
// Sweep from 180-0
for (int angle = 180; angle >= 0; angle -= 10)
{
myServo.SetAngle(angle);
Delayms(1000); // Wait 1000ms for the servo to reach the position
}
message = "Main program finishes here...";
cout << RainbowText(message,"Blue", "White","Bold") << endl;
return 0;
}
To check the pulse width that is sent by the BeagleBone to the servomotor, it has been captured with an oscilloscope at 0°, 90°, and 180°. These pictures are shown in hte next set of figures:
Execution of the program:
Se you in the next post.