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

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

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Pixel Art Mini Meow Animated.gif Черновик


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

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

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

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

 1 int pin = 13;
 2 
 3 void setup()
 4 {
 5   pinMode(pin, OUTPUT);
 6 }
 7 
 8 void loop()
 9 {
10   dot(); dot(); dot();
11   dash(); dash(); dash();
12   dot(); dot(); dot();
13   delay(3000);
14 }
15 
16 void dot()
17 {
18   digitalWrite(pin, HIGH);
19   delay(250);
20   digitalWrite(pin, LOW);
21   delay(250);
22 }
23 
24 void dash()
25 {
26   digitalWrite(pin, HIGH);
27   delay(1000);
28   digitalWrite(pin, LOW);
29   delay(250);
30 }

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

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

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

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

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

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

1 class Morse
2 {
3   public:
4     Morse(int pin);
5     void dot();
6     void dash();
7   private:
8     int _pin;
9 };

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

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

#include "Arduino.h"

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

#ifndef Morse_h
#define Morse_h

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

#endif

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

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

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

 1 /*
 2   Morse.h – Библиотека для азбуки Морзе на светодиодах.
 3   Создана Дэвидом А. Меллисом (David A. Mellis)
 4   2 ноября 2007 года.
 5   Опубликована без защиты авторских прав.
 6 */
 7 
 8 #ifndef Morse_h
 9 #define Morse_h
10 
11 #include "Arduino.h"
12 
13 class Morse
14 {
15   public:
16     Morse(int pin);
17     void dot();
18     void dash();
19   private:
20     int _pin;
21 };
22 
23 #endif

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

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

1 #include "Arduino.h"
2 #include "Morse.h"

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

1 Morse::Morse(int pin)
2 {
3   pinMode(pin, OUTPUT);
4   _pin = pin;
5 }

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

Morse::

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

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

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

Morse::

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

 1 void Morse::dot()
 2 {
 3   digitalWrite(_pin, HIGH);
 4   delay(250);
 5   digitalWrite(_pin, LOW);
 6   delay(250);  
 7 }
 8 
 9 void Morse::dash()
10 {
11   digitalWrite(_pin, HIGH);
12   delay(1000);
13   digitalWrite(_pin, LOW);
14   delay(250);
15 }

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

 1 /*
 2   Morse.cpp – Библиотека для азбуки Морзе на светодиодах.
 3   Создана Дэвидом А. Меллисом (David A. Mellis) 2 ноября 2007 года.
 4   Выпущена без защиты авторских прав.
 5 */
 6 
 7 #include "Arduino.h"
 8 #include "Morse.h"
 9 
10 Morse::Morse(int pin)
11 {
12   pinMode(pin, OUTPUT);
13   _pin = pin;
14 }
15 
16 void Morse::dot()
17 {
18   digitalWrite(_pin, HIGH);
19   delay(250);
20   digitalWrite(_pin, LOW);
21   delay(250);  
22 }
23 
24 void Morse::dash()
25 {
26   digitalWrite(_pin, HIGH);
27   delay(1000);
28   digitalWrite(_pin, LOW);
29   delay(250);
30 }

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

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

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

 1 #include <Morse.h>
 2 
 3 Morse morse(13);
 4 
 5 void setup()
 6 {
 7 }
 8 
 9 void loop()
10 {
11   morse.dot(); morse.dot(); morse.dot();
12   morse.dash(); morse.dash(); morse.dash();
13   morse.dot(); morse.dot(); morse.dot();
14   delay(3000);
15 }

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

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

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

Morse morse(13);

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

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

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

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

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

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

1 Morse   KEYWORD1
2 dash    KEYWORD2
3 dot     KEYWORD2

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

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

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

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

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

См.также

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