Creating your own servo control library
Article-series: Arduino - using_servos #6
So, we go to the /libraries
folder of our Arduino development environment, and we see 10-15 folders, each of which represents the standard Arduino library. Let's create a folder for our library here, for example ServoClass
. Inside this folder we will create two files: ServoS.h
, ServoS.cpp
. Unfortunately, the Arduino IDE does not support editing files with a resolution other than .ino
. Therefore, you will have to edit the library either in another development environment that supports C++ or in a regular text editor with syntax highlighting, for example AkelPad.
The first file, ServoS.h
is a header file (from the word header file), the class itself, variables and functions will be described here. At the same time, there is only a list of variables and functions and their types, without the body of the functions themselves.
Let's copy our class ServoClass
into the file ServoS.h
, more precisely only the variables and function names:
/* Custom servo control library */
#ifndef ServoS // If the library has not yet been included
#define ServoS // Connect it
#include "Arduino.h" // Use the standard Arduino library
#include "Servo.h" // Use the standard servo control library
class ServoClass
{
public:
Servo serv; // Actually the servo itself, controlled by the standard
// servo library
int servPosition; // Current servo position, degrees
int posMax; // Maximum permissible limit of servo movement, degrees
int posMin; // Minimum permissible limit of servo movement, degrees
int posInit; // Initial position of the servo, degrees
// Variables that are responsible for movement
boolean move; // Should the servo currently move?
int movePosition; // Position the servo should take, degrees
float moveSpeed; // Speed at which the servo should move, rpm
int moveIncrement; // change in position over one time interval, degrees
int moveInterval; // Interval between movements to ensure speed, milliseconds
unsigned long lastTimeCheck; // Time of the last movement of the servo drive, in ms from the start of the program
ServoClass(); // Class constructor
void AttachServo(int pin); //Initialize the servo drive indicating only the output numbers for connection
void AttachServo(int pin, int minpos, int maxpos, int initpos); //Initialize the servo with minimum, maximum and home position
int PositionCheck(int movepos); // Check if the position falls within the specified interval
void MoveTo(int movepos); // Give a command to move the servo
void MoveTo(int movepos, float movespeed) ; // Give a command to move the servo indicating the speed
void Update(); //Movement processing
};
#endif
When creating a library, you may notice some features:
The file begins with the #ifndef ServoS
directive, which checks whether this library has been included previously. If we have not connected it anywhere before, then the code located between #define ServoS
and #endif
will be added to the program
In addition, we define several functions with the same name but different arguments. This is due to the fact that when using libraries, calling a function with fewer than the defined number of arguments will result in an error. It will not be possible to set default argument values; more precisely, for each case you will need to describe a separate function. Therefore, we have two functions each: AttachServo
and MoveTo
Now all that remains is to deal with the ServoS.cpp
file. Here is the code for the functions described in the header file. In this case, we need to indicate which class the called functions belong to. Since our library uses the ServoClass
class, all functions will relate to it. In the case of a constructor whose name matches the name of the class, it will look like ServoClass::ServoClass()
, the remaining functions will look like ServoClass::FunctionName();
/* Custom servo control library */
#include "ServoS.h" // Include the header file
ServoClass::ServoClass() // Class constructor
{
posMin=0;
posMax=180;
posInit=0;
servPosition=0;
lastTimeCheck=millis();
}
void ServoClass::AttachServo (int pin) ////Initializing the servo drive indicating only the output numbers to connect
{
serv.attach(pin); // Connect the servo to the specified pin
posMin= 0; // Set the minimum position
posMax= 180; // Set the maximum position
posInit= 0; // Set the starting position
servPosition= posInit; // The servo position must match the home position
serv.write(servPosition); // Give the command to change position
}
void ServoClass::AttachServo (int pin, int minpos, int maxpos, int initpos) //Initialize the servo with minimum, maximum and home position
{
serv.attach(pin); // Connect the servo to the specified pin
posMin= minpos; // Set the minimum position
posMax= maxpos; // Set the maximum position
posInit= initpos; // Set the starting position
servPosition= posInit; // The servo position must match the home position
serv.write(servPosition); // Give the command to change position
}
int ServoClass::PositionCheck(int movepos) // Checking whether the position falls within the specified interval
{
int CheckedPos;
if (movepos<=posMin) // If a value greater than the maximum is specified
// movement limit
CheckedPos =posMin; // We will only move up to this limit
else
if (movepos>=posMax) // If a value less than the minimum is specified
//travel limit
CheckedPos =posMin; // We will only move up to this limit
else // In any other cases, we are within the permissible limits for movement
CheckedPos = movepos; // Just set the position where we should
return CheckedPos;
}
void ServoClass::MoveTo (int movepos) // If the speed is not specified, simply give a command to move the servo
{
movePosition = PositionCheck(movepos); // Check if we are within the permissible movement limits
serv.write(servPosition); // Give the command to change position
}
void ServoClass::MoveTo (int movepos, float movespeed) // If the speed is specified, we organize smooth movement
{
movePosition = PositionCheck(movepos); // Check if we are within the permissible movement limits
moveSpeed=movespeed; // Set the speed at which the servo should move
int Angle=abs(movePosition-servPosition); // How much should the servo move
// regardless of direction
if (movePosition >= servPosition) // Determine which direction from the current position we need to move
moveIncrement=1; // If forward, we will add one degree per time interval
else
moveIncrement=-1; // If back, then take away
float RotationSpeed=moveSpeed*360/60; // Convert the speed from revolutions per minute to degrees per second
moveInterval=(int)1000/RotationSpeed; // Calculate the interval between movements by one degree
move=true; // Give the command to start smooth movement
}
void ServoClass::Update () ////Movement processing
{
if (move) //If a command to move is given
{
if ((millis() - lastTimeCheck)>= moveInterval) // check how much time has passed since the last
//actions and compare with the interval we need.
//If more time has passed, perform the next action
{
servPosition+=moveIncrement; // Change the angle to the previously calculated value
serv.write(servPosition); // Send a command to the servo with a new rotation angle
lastTimeCheck= millis(); // Update the time counter so that the next time countdown starts from this moment
if (servPosition==movePosition) // If we have reached the desired position -
move=false; // stop movement
}
}
}
Actually, we are done with creating the library, all that remains is to connect it to our program. To do this, simply replace the description of our class from previous example to "#include "ServoS.h"
" Let's connect two servos and upload the sketch:
#include "Servo.h" // Include the library for working with the servo drive
#include "ServoS.h" // Include a custom library for working with the servo drive
ServoClass serv1;
ServoClass serv2;
void setup()
{
serv1.AttachServo(2); // The first servo is connected to the second pin
// Standard movement limits from 0 to 180 degrees
// Default home position is 0 degrees
serv2.AttachServo(3, 20, 160, 90); // The second servo is connected to the third pin
// Limits of movement from 20 to 160 degrees
// Starting position - 90 degrees
serv1.MoveTo(20); // Move the first servo to the 20-degree position
serv2.MoveTo(150, 0.5); // Move the second servo to the 150-degree position at 0.5 rpm
}
void loop()
{
serv1.Update(); // Process the movements of the first servo
serv2.Update(); // Process the movements of the second servo
}
The result of the program has not changed - the first servo will take the specified position immediately, and the second will rotate quite slowly (its initial position is 90 degrees, to move to the 150 degree position you need to turn 60 degrees. A speed of 0.5 revolutions per minute corresponds to 3 degrees per second, t This movement will take 20 seconds).
All that remains is to design our library for more convenient use. If you use the Arduino IDE, then the functions of standard libraries are highlighted in color, but for our library they are not. Therefore, we need to tell the program which functions need to be highlighted; to do this, we need to create a file keywords.txt
in our library folder
Let's put information about the keywords that need to be highlighted there:
# Syntax highlighting for the ServoS library
ServoClass KEYWORD1
AttachServo KEYWORD2
PositionCheck KEYWORD2
MoveTo KEYWORD2
Update KEYWORD2
Words marked KEYWORD1 will be highlighted as a data type, KEYWORD2 – as functions. A tab sign is placed between a word and its type, and a line feed is placed between lines.
Let's restart the Arduino IDE again, now our class and its functions are highlighted in the appropriate color