Создание собственной библиотеки управления сервоприводом
Серия-статей: Arduino, использование сервоприводов #6
Серия-статей: Создание библиотек для Arduino #1
Итак, заходим в папку /libraries
нашей среды разработки Arduino, и видим 10-15 папок, каждая из которых отвеяает за стандартную библиотеку Arduino. Создадим здесь папку для нашей библиотеки, например ServoClass
. Внутри этой папки создадим два файла: ServoS.h
, ServoS.cpp
. К сожалению среда Arduino IDE не поддерживает редактирование файлов с разрешением, отличным от .ino
. Поэтому редактировать библиотеку придется либо в другой среде разработки, которая поддерживает с++ или в обычном текстовом редакторе с подсветкой синтаксиса, например AkelPad.
Первый файл, ServoS.h
– заголовочный файл (от слова header file), здесь будут описываться сам класс, переменные и функции. При этом здесь располагают только список переменных и функций и их типов, без тела самих функций.
Скопируем в файл ServoS.h
наш класс ServoClass
, точнее только переменные и названия функций:
/* Пользовательская библиотека управления сервоприводами */
#ifndef ServoS // Если библиотека еще не была подключена
#define ServoS // Подключить ее
#include "Arduino.h" // Используем стандартную библиотеку Arduino
#include "Servo.h" // Используем стандартную библиотеку управления сервоприводами
class ServoClass
{
public:
Servo serv; // Собственно сам сервопривод, управляемый стандартной
// библиотекой servo
int servPosition; // Текущая позиция сервопривода, градусов
int posMax; // Максимально допустимый предел перемещения сервопривода, градусов
int posMin; // Минимальный допустимый предел перемещения сервопривода, градусов
int posInit; // Исходная позиция сервопривода, градусов
// Переменные, которые отвечают за перемещение
boolean move; // Должен ли двигатся ли сервопривод в настоящий момент
int movePosition; // Позиция, которую должен принять сервопривод, градусы
float moveSpeed; // Скорость, с которой сервопривод должен двигаться, об/мин
int moveIncrement; // изменение положения за один интервал времени, градусов
int moveInterval; // Интервал между движениями для обеспечения скорости, миллисекунды
unsigned long lastTimeCheck; // Время последнего движения сервопривода, в мс от начала работы программы
ServoClass(); // Конструктор класса
void AttachServo (int pin); //Инициализация сервопривода с указанием только номеры выхода для подключения
void AttachServo (int pin, int minpos, int maxpos, int initpos); //Инициализация сервопривода с указанием минимальной, максимальной и исходной позиции
int PositionCheck(int movepos); // Проверка, попадает ли позиция в заданный интервал
void MoveTo (int movepos); // Подать команду на перемещение сервопривода
void MoveTo (int movepos, float movespeed) ; // Подать команду на перемещение сервопривода с указанием скорости
void Update (); //Обработка перемещений
};
#endif
При создании библиотеки можно заметить некоторые особенности:
Файл начинается с директивы #ifndef ServoS
, которая проверяет, не был ли подключена эта библиотека ранее. Если мы нигде ранее ее не подключали, то в программу, будет добавлен код, находящийся между #define ServoS
и #endif
Кроме того мы определяем несколько функций с одним именем но разными аргументами. Это связано с тем, что при использовании библиотек вызов функции с меньшим, чем определено, количеством аргументов приведет к ошибк. Задать значения аргументов по умолчанию не получится, точнее для каждого случая нужно будет описать отдельную функцию. Поэтому у нас по две функции AttachServo
и MoveTo
Теперь осталось разобараться с файлом ServoS.cpp
. Здесь находится код для описанных в заголовочном файле функций. При этом нам нужно указывать, к какому классу относятся вызываемые функции. Поскольку в нашей библиотеке используется класс ServoClass
, то все функции будут относиться к нему. В случае конструктора, имя которого совпадает с именем класса, это будет выглядеть как ServoClass::ServoClass()
, остальные функции – соответственно ServoClass::ИмяФункции();
/* Пользовательская библиотека управления сервоприводами */
#include "ServoS.h" // Подключаем заголовочный файл
ServoClass::ServoClass() // Конструктор класса
{
posMin=0;
posMax=180;
posInit=0;
servPosition=0;
lastTimeCheck=millis();
}
void ServoClass::AttachServo (int pin) ////Инициализация сервопривода с указанием только номеры выхода для подключения
{
serv.attach(pin); // Подключаем сервопривод к указанному пину
posMin= 0; // Устанавливаем минимальную позицию
posMax= 180; // Устанавливаем максимальную позицию
posInit= 0; // Устанавливаем исходную позицию
servPosition= posInit; // Позиция сервопривода должна сответствовать исходному положению
serv.write(servPosition); // Даем команду изменить положение
}
void ServoClass::AttachServo (int pin, int minpos, int maxpos, int initpos) //Инициализация сервопривода с указанием минимальной, максимальной и исходной позиции
{
serv.attach(pin); // Подключаем сервопривод к указанному пину
posMin= minpos; // Устанавливаем минимальную позицию
posMax= maxpos; // Устанавливаем максимальную позицию
posInit= initpos; // Устанавливаем исходную позицию
servPosition= posInit; // Позиция сервопривода должна сответствовать исходному положению
serv.write(servPosition); // Даем команду изменить положение
}
int ServoClass::PositionCheck(int movepos) // Проверка, попадает ли позиция в заданный интервал
{
int CheckedPos;
if (movepos<=posMin) // Если указано большее значение, чем максимальный
// предел перемещения
CheckedPos =posMin; // Будем двигаться тоько до этого предела
else
if (movepos>=posMax) // Если указано меньшее значение, чем минимальный
//предел перемещения
CheckedPos =posMin; // Будем двигаться тоько до этого предела
else // В любых других случаях - мы в допустимых для перемещения пределах
CheckedPos =movepos; // Просто устанавливаем позицию, в которую должен
return CheckedPos;
}
void ServoClass::MoveTo (int movepos) // Если скорость не указана - просто даем команду на перемещение сервопривода
{
movePosition = PositionCheck(movepos); // Проверяем, не выходим ли мы за допустимые пределы премещения
serv.write(servPosition); // Даем команду изменить положение
}
void ServoClass::MoveTo (int movepos, float movespeed) // Если скорость указана - организуем плавное перемещение
{
movePosition = PositionCheck(movepos); // Проверяем, не выходим ли мы за допустимые пределы премещения
moveSpeed=movespeed; // Устанавливаем скорость, с которой должен перемещаться сервопривод
int Angle=abs(movePosition-servPosition); // На сколько должен переместиться сервопривод
// вне зависимости от направления
if (movePosition >= servPosition) // Определяем, в какую сторону от текущего положения нам нужно двигаться
moveIncrement=1; // Если вперед - будем добавлять один градус за один интервал времени
else
moveIncrement=-1; // Если назад - то отнимать
float RotationSpeed=moveSpeed*360/60; // Перводим скорость из оборотов в минуту в градусы за секунду
moveInterval=(int)1000/RotationSpeed; // Вычисляем интервал между перемещениями на один градус
move=true; // Даем комнду начать плавное перемещение
}
void ServoClass::Update () ////Обработка перемещений
{
if (move) //Если дана команда на перемещение
{
if ((millis() - lastTimeCheck)>= moveInterval) // проверяем, сколько прошло времени с последнего
//действия и сравниваем с нужным нам интервалом.
//Если времени прошло больше – совершаем следующее действие
{
servPosition+=moveIncrement; // Изменяем угол на вычисленное ранее значение
serv.write(servPosition); // Посылаем сервоприводу команду с новым углом поворота
lastTimeCheck= millis(); // Обновляем счетчик времени, для того чтобы следующий отсчет времени начался с этого момента
if (servPosition==movePosition) // Если мы достигли нужной позиции-
move=false; // остановить движение
}
}
}
Собственно с созданием библиотеки мы закончили, осталось подключить ее к нашей программе. Для этого просто заменим описание нашего класса из предыдущего примера на "#include "ServoS.h"
" Подключим два сервопривода и загрузим скетч:
#include "Servo.h" // Подключаем библиотеку для работы с сервоприводом
#include "ServoS.h" // Подключаем пользовательскую библиотеку для работы с сервоприводом
ServoClass serv1;
ServoClass serv2;
void setup()
{
serv1.AttachServo(2); // Первый сервопривод подключен ко второму пину
// Стандартные пределы перемещения от 0 до 180 градусов
// Стандартное исходное положени 0 градусов
serv2.AttachServo(3, 20, 160, 90); // Второй сервопривод подключен к третьему пину
// Пределы перемещения от 20 до 160 градусов
// Исходное положение - 90 градусов
serv1.MoveTo(20); // Пермещаем первый сервопривод в положение 20 градусов
serv2.MoveTo(150, 0.5); // Пермещаем второй сервопривод в положение 150 градусов со скоростью 0.5 оборота в минуту
}
void loop()
{
serv1.Update(); // Обрабатываем перемещения первого сервопривода
serv2.Update(); // Обрабатываем перемещения второго сервопривода
}
Результат работы программы не изменился – первый сервопривод займет указанное положение сразу, а второй будет поворачиваться достаточно медленно (его исходно положение 90 градусов, для перемещения в положение 150 градусов нужно повернуться на 60 градусво. Скорость 0.5 оборота в минуту соответсвует 3 градуса в секунду, т.е перемещение займет 20 секунд).
Осталось оформить нашу библиотеку для более удобного использования. Если использовать Arduino IDE, то функции стандартных библиотек подсвечиваются цветом, а вот для нашей библиотеки – нет. Поэтому нужно сообщить программе, какие функции нужно подсвечивать, для этого нужно создать файл keywords.txt
в папке нашей библиотеки
Поместим туда информацию о ключевых словах, которые нужно подсвечивать:
# Подсветка синтаксиса для библиотеки ServoS
ServoClass KEYWORD1
AttachServo KEYWORD2
PositionCheck KEYWORD2
MoveTo KEYWORD2
Update KEYWORD2
Слова, помеченные KEYWORD1 будут подсвечены как тип данных, KEYWORD2 – как функции Между словом и его типом ставится знак табуляции, между строками – перевод строки.
Опять перезагрузим Arduino IDE, теперь наш класс и его функции подсвечиваются соответствующим цветом