Wilmer
Wilmer
. 11 min read

Control the speed of a 360° servomotor from a potentiometer in the BeagleBone Black

Control the speed of a 360° servomotor from a potentiometer in the BeagleBone Black

In this post, I show how to control the speed of the 360° continuous rotation servomotor SM-S4303R which can run to 60rpm if the servomotor is powered with 4.8V or 70rpm if it is powered with6.0V.

In this work, these absolute values for the speed will not be used, instead, the speed wil be computed in percentage ranging from -100 for the maximum speed in the counterclockwise direction and 100% for the maximum speed in the clockwise direction.

The control will be done through a potentiometer using the BeagleBone and the library that I have been written in C++ which you can find here. In the last post, I showed the technical details about how to control the position from a potentiometer. In this case, I use the potentiometer too, but to control the speed of the 360° SM-S4303R continuos rotation servomotor instead of the angle of a 180° SG-90 servomotor

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 SM-S4303R, 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 1. It consists of an SM-S4303R servomotor, 1 Potentiometer, 4 AA batteries, and the BeagleBone.

Circuit.png
Figure 1: Circuit to control the speed of a 360° continuous rotation servomotor SM-S4303R.

The components are:

  • 1 Servomotor SM-S4303R 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</h1>

Some Servo and ADC objects are declared with global scope to initialize the servomotor and the pin from which the potentiometer will be attached to.

1
2
3
4
5
// Declare the Servo object
Servo myServo(P8_13);

// Declare the ADC pin to attach the potentiometer
ADC myPotentiometer(P9_39);

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.

Three global variables are defined and initialized to disable the servo movement, store the reading coming from the potentiometer, and store the desired speed, respectively.

1
2
3
4
// Global variables
bool stopMoveServo = false;
int adcValueOut = 0;
int speed = 0;

To control the servomotor speed from the potentiometer, the ReadADC() method is used and store in the adcValueOut variable. Then, this value which is between 0 - 4095, is mapped to the range of the servomotor speed, i.e. -100% - 100%, and store in the speed variable.

1
2
3
4
5
// Read the analog converted value
adcValueOut = myPotentiometer.ReadADC();

// Map the adc value to the speed
speed = (adcValueOut - 2048) / 4095.0 * 200;

This mapping is a linear function that takes the ADC value from the potentiometer and returns the corresponding speed, taking into account the clockwise and counterclockwise direction. The graph of this mapping can be seen in Figure 2.

MappingSpeed.png
Figure 2: Mapping between the potentiometer reading and the speed for the 360° continuous rotation servomotor SM-S4303R. The green area corresponds to the clockwise direction while the red area to counterclockwise.

To rotate the servo you can use the SetSpeed() method which receives the speed in percentage in a range from -100 to 100 and delivers the corresponding pulse width in a range from the minimum and maximum pulse width for the servomotor. In this case, the default values of the class are 544444ns and 2500000ns, respectively. The positive speed values correspond to the clockwise direction and the negative values to the counterclockwise

1
2
// Move the servo
myServo.SetSpeed(speed);

The implementation of this method in the class Servo can be shown in the next listing where can be observed the equation which does the mapping between the desired speed and the corresponding pulse width.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
Public method to set the speed for a continuous servo
@param int: the desired angle (-100-100)     
*/
void Servo::SetSpeed(int newSpeed)
{
  speed = newSpeed;
  int averagePulseWidth = (maximumPulseWidth-minimumPulseWidth) / 2 + minimumPulseWidth;
  double mapping = (maximumPulseWidth-minimumPulseWidth)/200.0 * speed + averagePulseWidth;
  int pulseWidth = static_cast<int>(mapping);
  pwmPin.SetDutyCycleByPeriod(pulseWidth);
  
  std::string message;
  message = "speed: " + std::to_string(speed) + " -> pulse width: " +
  std::to_string(pulseWidth) + "ns\n";
  std::cout << RainbowText(message, "Light Blue"); 
}

This mapping can be seen in Figure 3 where a linear function takes the speed and returns the corresponding pulse width.

MappingPulseWidth.png
Figure 3: Mapping between the Pulse Width and the Speed for the 360° continuous rotation servomotor SM-S4303R. The green area corresponds to the clockwise direction while the red area to counterclockwise.

The lines of code that read the potentiometer and move the servo are inside of a callback function to do these processes in the background while the stopMoveServo variable will be true. This callback function is activated on the ADC object, in this case, through the myPotentiometer object with this line:

1
2
// Activate the ADC object's callback function
myPotentiometer.DoUserFunction(&MoveServo);

The complete code for this implementation is shown in the next listing:

SM-S4303R_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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/******************************************************************************
SM-S4303R_1.1.cpp
@wgaonar
31/07/2021
https://github.com/wgaonar/BeagleCPP

- Move a continuous rotation servo with the readings from a potentiometer

Class: Servo
******************************************************************************/
#include <iostream>
#include "../../../Sources/Servo.h"
#include "../../../Sources/ADC.h"

using namespace std;

// Declare the Servo object
Servo myServo(P8_13);

// Declare the ADC pin to attach the potentiometer
ADC myPotentiometer(P9_39);

// Global variables
bool stopMoveServo = false;
int adcValueOut = 0;
int speed = 0;

// Function to move the servo in background
int MoveServo() 
{
  while (stopMoveServo == false)
  {
    // Read the analog converted value
    adcValueOut = myPotentiometer.ReadADC();

    // Map the adc value to the speed
    speed = (adcValueOut - 2048) / 4095.0 * 200;

    // Move the servo
    myServo.SetSpeed(speed);

    Delayms(250);
  }
  return 0;
}

int main()
{
  string message = "Main program starting here...";
  cout << RainbowText(message,"Blue", "White", "Bold") << endl;

  // Activate the ADC object's callback function
  myPotentiometer.DoUserFunction(&MoveServo);

  char userInput = '\0';
  while (userInput != 'y')
  {
    message = "Enter an option 'y' for exit: ";
    cout << RainbowText(message, "Blue");
    
    cin >> userInput;
    if (userInput == 'y') 
      stopMoveServo = true;
  }
  
  message = "Main program finishes here...";
  cout << RainbowText(message,"Blue", "White","Bold") << endl;

  return 0;
}

Execution of the program:

Video: Execution of the program.

Se you in the next post.

Rating:
comments powered by Disqus