Arduino:Хакинг/Написание библиотеки

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Написание библиотеки[1]

Эта статья объясняет, как создать собственную библиотеку для Arduino. Объяснение будет на примере кода, позволяющего моргать азбукой Морзе на светодиодах Arduino. Далее будет показано, как конвертировать этот код в библиотеку. Это позволит другим пользователям без особого труда использовать и обновлять написанный вами код.

Более подробно читайте в этом руководстве – оно объясняет, как написать хороший API в стиле Arduino для вашей библиотеки.

Начнем со скетча, который выполняет простой код с азбукой Морзе:

int pin = 13;

void setup()
{
  pinMode(pin, OUTPUT);
}

void loop()
{
  dot(); dot(); dot();
  dash(); dash(); dash();
  dot(); dot(); dot();
  delay(3000);
}

void dot()
{
  digitalWrite(pin, HIGH);
  delay(250);
  digitalWrite(pin, LOW);
  delay(250);
}

void dash()
{
  digitalWrite(pin, HIGH);
  delay(1000);
  digitalWrite(pin, LOW);
  delay(250);
}

Если запустить этот скетч, он «напечатает» на 13-ом контакте «SOS» (сигнал бедствия).

Этот скетч состоит из нескольких частей, которые нужно перенести в библиотеку. Во-первых, у нас есть функции dot() и dash(), которые, собственно, и выполняют мигание. Во-вторых, там есть переменная ledPin, которую эти функции используют, чтобы узнать, на каком именно контакте нужно выполнить мигание. Наконец, в скетче вызывается функция pinMode(), которая выставляет контакт в режим OUTPUT.

Итак, приступим к превращению скетча в библиотеку!

Для библиотеки понадобится как минимум два файла: заголовочный файл (с расширением «*.h») и файл-исходник (с расширением «*.cpp»). Заголовочный файл содержит эдакий список основных понятий, т.е. список всего, что находится внутри библиотеки. Сам код находится внутри файла-исходника. Мы даем нашей библиотеке название «Morse», поэтому наш заголовочный файл будет называется «Morse.h».

Давайте-ка взглянем, что у него внутри. На первый взгляд, его содержимое может показаться странным, но вы все поймете, когда мы заглянем внутрь файла-исходника.

«Сердцевина» заголовочного файла состоит из списка функций и необходимых переменных (одна строчка/переменная – одна функция), «обернутых» в класс:

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

Класс – это просто коллекция функций и переменных, хранящихся в одном месте. Эти функции и переменные могут быть public («публичными», могут употребляться внутри любой функции) или private («приватными», могут употребляться лишь внутри самого класса). У каждого класса есть специальная функция-конструктор, которая используется для создания экземпляра класса. Конструктор имеет то же имя, что и класс, но ничего не возвращает.

Содержимое заголовочного файла на этом, впрочем, не заканчивается. Во-первых, вам нужно вписать туда директиву #include, дающую доступ к стандартным типам и константам языка Arduino (она автоматически добавляется к обычным скетчам, но не к библиотекам). Это выглядит примерно так (и ставится выше блока с классом, о котором писалось выше):

#include "Arduino.h"

Во-вторых, весь заголовочный файл нужно «обернуть» в одну очень странную конструкцию:

#ifndef Morse_h
#define Morse_h

// директива #include и код пишутся тут...

#endif

Это призвано предотвратить проблемы, которые могут возникнуть, если кто-то случайно подключит (#include) вашу библиотеку дважды.

Наконец, в верхней части заголовочного файла пишется комментарий с названием библиотеки, кратким описанием того, что она делает, автором, датой и лицензией.

Давайте взглянем, как заголовочный файл будет выглядеть полностью:

/*
  Morse.h – Библиотека для азбуки Морзе на светодиодах.
  Создана Дэвидом А. Меллисом (David A. Mellis)
  2 ноября 2007 года.
  Опубликована без защиты авторских прав.
*/

#ifndef Morse_h
#define Morse_h

#include "Arduino.h"

class Morse
{
  public:
    Morse(int pin);
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Теперь давайте пробежимся по разным фрагментам файла-исходника – «Morse.cpp».

Сначала идут две директивы #include. Они дают остальному коду доступ к стандартным функциям языка Arduino, а также к понятиям, указанным в «Morse.h»:

#include "Arduino.h"
#include "Morse.h"

Затем идет конструктор. Опять же, он объясняет, что должно произойти, если кто-то создаст экземпляр вашего класса. В данном случае пользователь должен указать, какой контакт он будет использовать. Мы выставляем этот контакт в режим OUTPUT и сохраняем в приватную переменную для использования в других функциях:

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

Далее идет пара странных вещей. Сначала

Morse::

перед названием функции. Это значит, что эта функция является частью класса Morse. Вы увидите такой фрагмент и при указании других функций этого класса.

Вторая непонятная вещь – это нижнее подчеркивание при написании приватной переменной, _pin. По сути, эту переменную можно назвать как угодно – до тех пор, пока она соответствует определению, указанному в заголовочном файле. Добавление нижнего подчеркивания перед названием переменной – это общепринятый способ показать, что эта переменная является приватной, а также отличить ее от аргумента функции (в данном случае – pin).

Дальше идет, собственно, сам код из скетча, который мы превращаем в библиотеку (наконец-то!). Он выглядит примерно так же, как в скетче, за исключением

Morse::

, стоящим перед названиями функций, а также _pin вместо pin.

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

Наконец, пишем в верхней части файла-исходника заголовочный комментарий. И давайте взглянем, что получилось:

/*
  Morse.cpp – Библиотека для азбуки Морзе на светодиодах.
  Создана Дэвидом А. Меллисом (David A. Mellis) 2 ноября 2007 года.
  Выпущена без защиты авторских прав.
*/

#include "Arduino.h"
#include "Morse.h"

Morse::Morse(int pin)
{
  pinMode(pin, OUTPUT);
  _pin = pin;
}

void Morse::dot()
{
  digitalWrite(_pin, HIGH);
  delay(250);
  digitalWrite(_pin, LOW);
  delay(250);  
}

void Morse::dash()
{
  digitalWrite(_pin, HIGH);
  delay(1000);
  digitalWrite(_pin, LOW);
  delay(250);
}

По сути, это все, что нужно (но есть пара дополнений, о которых мы поговорим чуть позже).

Теперь давайте рассмотрим, как использовать эту библиотеку. Сначала идем в папку «libraries», которая находится в папке скетчей (расположение папки скетчей можно узнать, кликнув в IDE Arduino на Файл > Настройки; откроется окно, в самой верхней части которого будет поле «Размещение папки скетчей») и создаем там папку «Morse». Затем копируем в эту папку файлы «Morse.h» и «Morse.cpp». Теперь запускаем IDE Arduino и кликаем по Скетч > Подключить библиотеку. Откроется выпадающее меню со списком загруженных библиотек, и в их числе уже должна быть наша Morse. Библиотека будет скомпилирована вместе со скетчами, которые ее используют. Если библиотеки не видно, то убедитесь, что скопированные вами файлы действительно имеют расширения «*.h» и «*.cpp» (то есть, к примеру, без дополнительных «*.pde» или «*.txt»).

Теперь давайте посмотрим, как можно воспроизвести наш старый скетч «SOS» при помощи новой библиотеки:

#include <Morse.h>

Morse morse(13);

void setup()
{
}

void loop()
{
  morse.dot(); morse.dot(); morse.dot();
  morse.dash(); morse.dash(); morse.dash();
  morse.dot(); morse.dot(); morse.dot();
  delay(3000);
}

Как видите, этот скетч имеет пару отличий от старой версии (не считая того факта, что некоторый код переместился в саму библиотеку).

Сначала в самом верху скетча добавляется директива #include. Благодаря этому скетч получает доступ к библиотеке Morse, а сама библиотека подключается к коду, который отправляется на плату. Таким образом, если вам в этом скетче больше не понадобится библиотека Morse, удалите директиву #include – чтобы сэкономить программную память.

Во-вторых, создаем экземпляр класса Morse и называем его morse:

Morse morse(13);

При обработке этой строки – а это происходит до обработки функции setup() – вызывается конструктор для класса Morse, после чего ему передается аргумент в виде числа «13».

Обратите внимание, что блок setup() пуст, потому что вызов pinMode() теперь происходит внутри библиотеки (при создании экземпляра класса).

Наконец, чтобы вызвать функции dot() и dash(), нужно подставить к ним префикс morse. Это название экземпляра класса, которое мы хотим использовать. Мы можем использовать несколько экземпляров класса Morse – каждый со своим контактом, хранящимся в приватной переменной _pin. Таким образом, при вызове функции, использующей какой-либо из этих экземпляров, будет использоваться переменная, указанная при создании этого экземпляра. Например, если воспользоваться двумя экземплярами класса Morse...

Morse morse(13);
Morse morse2(12);

...то внутри morse2.dot() приватной переменной _pin будет «12».

Если вы попытаетесь написать новый скетч, то IDE Arduino, возможно, не распознает ничего из вашей новой библиотеки, окрасив неизвестные конструкции цветными полосками. К сожалению, IDE Arduino не умеет автоматически распознавать, что именно вы задали в своей библиотеке (хотя такая функция очень пригодилась бы), поэтому ей нужно немножко помочь. Для этого в папке «Morse» нужно создать специальный файл «keywords.txt». Его содержимое должно выглядеть следующим образом:

Morse   KEYWORD1
dash    KEYWORD2
dot     KEYWORD2

Здесь каждая строчка начинается с ключевого слова, после чего идет  Tab ⇆ (не пробелы) и тип ключевого слова. Классы – это KEYWORD1 и окрашиваются в оранжевый; функции – это KEYWORD2 и окрашиваются в коричневый. Чтобы IDE Arduino начала распознавать новые ключевые слова, ее нужно перезапустить.

Также будет полезно снабдить вашу библиотеку скетчем-примером, который демонстрирует, как ее использовать. Для этого создайте внутри папки «Morse» папку «examples». Затем переместите или скопируйте папку со скетчем (можно назвать ее «SOS»), который мы написали выше, в папку «examples». Чтобы найти скетч, в данный момент загруженный в IDE Arduino, кликните на Скетч > Показать папку скетча. Далее перезапускаем IDE Arduino (обещаю, это в последний раз!), кликаем на Файл > Примеры; откроется выпадающий список, в котором должен быть пункт «Library-Morse». Также имеет смысл добавить пару комментариев, объясняющих, как использовать вашу библиотеку.

Если вы хотите сами опробовать библиотеку, создание которой объяснялось в этой статье (в комплекте с ключевыми словами и скетчем-примером), то загрузить ее можно здесь.

На этом все, но в будущем, возможно, мы напишем более подробное руководство по написанию библиотек. Если у вас возникли какие-то проблемы или пожелания, поделиться ими можно на форуме.

Дополнительную информацию читайте в руководстве по написанию API для Arduino.

См.также

Внешние ссылки