Arduino:Хакинг/Написание библиотеки: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Нет описания правки
 
Нет описания правки
 
(не показаны 4 промежуточные версии 1 участника)
Строка 2: Строка 2:
{{Перевод от Сubewriter}}
{{Перевод от Сubewriter}}
{{Myagkij-редактор}}
{{Myagkij-редактор}}
{{Черновик}}


=Написание библиотеки<ref>[https://www.arduino.cc/en/Hacking/LibraryTutorial www.arduino.cc - Writing a Library for Arduino]</ref>=
=Написание библиотеки<ref>[https://www.arduino.cc/en/Hacking/LibraryTutorial www.arduino.cc - Writing a Library for Arduino]</ref>=


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


Более подробно читайте в [https://www.arduino.cc/en/Reference/APIStyleGuide этом руководстве] – оно объясняет, как написать хороший '''API''' в стиле '''Arduino''' для вашей библиотеки.
Более подробно читайте в [https://www.arduino.cc/en/Reference/APIStyleGuide этом руководстве] – оно объясняет, как написать хороший [[API]] в стиле [[Arduino]] для вашей библиотеки.


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
int pin = 13;
int pin = 13;


Строка 46: Строка 44:
</syntaxhighlight>
</syntaxhighlight>


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


Этот скетч состоит из нескольких частей, которые нужно перенести в библиотеку. Во-первых, у нас есть функции '''dot()''' и '''dash()''', которые, собственно, и выполняют мигание. Во-вторых, там есть переменная '''ledPin''', которую эти функции используют, чтобы узнать, на каком именно контакте нужно выполнить мигание. Наконец, в скетче вызывается функция [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/pinMode()|pinMode()]], которая выставляет контакт в режим [[Arduino:Справочник языка Arduino/Константы/OUTPUT|OUTPUT]].
Этот [[скетч]] состоит из нескольких частей, которые нужно перенести в библиотеку. Во-первых, у нас есть функции '''dot()''' и '''dash()''', которые, собственно, и выполняют мигание. Во-вторых, там есть переменная '''ledPin''', которую эти функции используют, чтобы узнать, на каком именно контакте нужно выполнить мигание. Наконец, в [[скетч]]е вызывается функция [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/pinMode()|pinMode()]], которая выставляет контакт в режим [[Arduino:Справочник языка Arduino/Константы/OUTPUT|OUTPUT]].


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


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


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
class Morse
class Morse
{
{
Строка 70: Строка 68:
</syntaxhighlight>
</syntaxhighlight>


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


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


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
#include "Arduino.h"
#include "Arduino.h"
</syntaxhighlight>
</syntaxhighlight>
Строка 80: Строка 78:
Во-вторых, весь заголовочный файл нужно «обернуть» в одну очень странную конструкцию:
Во-вторых, весь заголовочный файл нужно «обернуть» в одну очень странную конструкцию:


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
#ifndef Morse_h
#ifndef Morse_h
#define Morse_h
#define Morse_h
Строка 95: Строка 93:
Давайте взглянем, как заголовочный файл будет выглядеть полностью:
Давайте взглянем, как заголовочный файл будет выглядеть полностью:


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
/*
/*
   Morse.h – Библиотека для азбуки Морзе на светодиодах.
   Morse.h – Библиотека для азбуки Морзе на светодиодах.
Строка 121: Строка 119:
</syntaxhighlight>
</syntaxhighlight>


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


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
#include "Arduino.h"
#include "Arduino.h"
#include "Morse.h"
#include "Morse.h"
Строка 132: Строка 130:
Затем идет конструктор. Опять же, он объясняет, что должно произойти, если кто-то создаст экземпляр вашего класса. В данном случае пользователь должен указать, какой контакт он будет использовать. Мы выставляем этот контакт в режим [[Arduino:Справочник языка Arduino/Константы/OUTPUT|OUTPUT]] и сохраняем в приватную переменную  для использования в других функциях:
Затем идет конструктор. Опять же, он объясняет, что должно произойти, если кто-то создаст экземпляр вашего класса. В данном случае пользователь должен указать, какой контакт он будет использовать. Мы выставляем этот контакт в режим [[Arduino:Справочник языка Arduino/Константы/OUTPUT|OUTPUT]] и сохраняем в приватную переменную  для использования в других функциях:


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
Morse::Morse(int pin)
Morse::Morse(int pin)
{
{
Строка 146: Строка 144:
</syntaxhighlight>
</syntaxhighlight>


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


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


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


<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
Строка 158: Строка 156:
, стоящим перед названиями функций, а также '''_pin''' вместо '''pin'''.
, стоящим перед названиями функций, а также '''_pin''' вместо '''pin'''.


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
void Morse::dot()
void Morse::dot()
{
{
Строка 178: Строка 176:
Наконец, пишем в верхней части файла-исходника заголовочный комментарий. И давайте взглянем, что получилось:
Наконец, пишем в верхней части файла-исходника заголовочный комментарий. И давайте взглянем, что получилось:


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
/*
/*
   Morse.cpp – Библиотека для азбуки Морзе на светодиодах.
   Morse.cpp – Библиотека для азбуки Морзе на светодиодах.
Строка 213: Строка 211:
По сути, это все, что нужно (но есть пара дополнений, о которых мы поговорим чуть позже).  
По сути, это все, что нужно (но есть пара дополнений, о которых мы поговорим чуть позже).  


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


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
#include <Morse.h>
#include <Morse.h>


Строка 235: Строка 233:
</syntaxhighlight>
</syntaxhighlight>


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


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


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


<syntaxhighlight lang="c" enclose="div">
<syntaxhighlight lang="c">
Morse morse(13);
Morse morse(13);
</syntaxhighlight>
</syntaxhighlight>


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


Обратите внимание, что блок [[Arduino:Справочник языка Arduino/setup()|setup()]] пуст, потому что вызов [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/pinMode()|pinMode()]] теперь происходит внутри библиотеки (при создании экземпляра класса).
Обратите внимание, что блок [[Arduino:Справочник языка Arduino/setup()|setup()]] пуст, потому что вызов [[Arduino:Справочник языка Arduino/Функции/Цифровой ввод/вывод/pinMode()|pinMode()]] теперь происходит внутри библиотеки (при создании экземпляра класса).


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
Morse morse(13);
Morse morse(13);
Morse morse2(12);
Morse morse2(12);
</syntaxhighlight>
</syntaxhighlight>


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


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


<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="c" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
Morse  KEYWORD1
Morse  KEYWORD1
dash    KEYWORD2
dash    KEYWORD2
Строка 266: Строка 264:
</syntaxhighlight>
</syntaxhighlight>


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


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


Если вы хотите сами опробовать библиотеку, создание которой объяснялось в этой статье (в комплекте с ключевыми словами и скетчем-примером), то загрузить ее можно [[Media:Morse.zip|здесь]].
Если вы хотите сами опробовать библиотеку, создание которой объяснялось в этой статье (в комплекте с ключевыми словами и скетчем-примером), то загрузить ее можно [[Media:Morse.zip|здесь]].
Строка 281: Строка 279:


<references />
<references />
{{Навигационная таблица/Портал/Arduino}}


[[Категория:Хакинг]]
[[Категория:Хакинг]]
[[Категория:Хакинг Arduino]]
[[Категория:Хакинг Arduino]]

Текущая версия от 11:29, 8 июля 2023

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


Написание библиотеки[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.

См.также

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