Главная chevron_right Уроки chevron_right Участие в проекте chevron_right Написание библиотеки для Arduino

Написание библиотеки для Arduino

Пошаговое руководство по созданию собственной библиотеки Arduino на примере кода азбуки Морзе — от скетча до полноценной библиотеки с примерами.

В этом уроке разберём, как создать собственную библиотеку для Arduino. Начнём со скетча, который мигает светодиодом по азбуке Морзе, и шаг за шагом превратим его в полноценную библиотеку. После этого любой сможет легко использовать ваш код и получать обновления по мере его улучшения.

Дополнительную информацию об оформлении API в стиле Arduino смотрите здесь: API Style Guide.

С чего начать: исходный скетч

Вот простой скетч, который передаёт сигнал Морзе:

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(), которые выполняют само мигание;
  • переменная pin, в которой хранится номер пина;
  • вызов pinMode(), настраивающий пин как выход.

Теперь начнём превращать всё это в библиотеку.

Структура библиотеки: два обязательных файла

Для библиотеки нужно минимум два файла:

  • заголовочный файл с расширением .h — содержит объявления всего, что есть в библиотеке;
  • файл реализации с расширением .cpp — содержит сам рабочий код.

Нашу библиотеку назовём Morse, поэтому заголовочный файл будет называться Morse.h.

Заголовочный файл Morse.h

Основа заголовочного файла — это класс, в котором перечислены все функции и переменные библиотеки:

class Morse
{
  public:
    Morse(int pin);
    void begin();
    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 begin();
    void dot();
    void dash();
  private:
    int _pin;
};

#endif

Файл реализации Morse.cpp

Теперь разберём файл реализации по частям.

Подключение заголовков

Вначале идут директивы #include, дающие доступ к стандартным функциям Arduino и к определениям из вашего заголовочного файла:

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

Конструктор

Конструктор описывает, что происходит при создании объекта класса. Пользователь передаёт номер пина, конструктор сохраняет его в приватной переменной:

Morse::Morse(int pin)
{
  _pin = pin;
}

Обратите внимание на два момента:

  1. Morse:: перед именем функции — указывает, что функция принадлежит классу Morse. Так же оформляются все остальные функции класса.
  2. Подчёркивание в имени приватной переменной _pin — распространённое соглашение, которое помогает отличить приватные переменные от аргументов функции (pin в данном случае). Имя можно выбрать любое, главное — чтобы оно совпадало с объявлением в заголовочном файле.

Функция begin() для настройки железа

Настройку оборудования лучше выносить в отдельную функцию begin(), которую вызывают из setup() скетча. Это важно: в момент выполнения конструктора железо ещё не инициализировано, поэтому настраивать пины там не следует. В нашей библиотеке нужно задать пин как выход:

void Morse::begin()
{
  pinMode(_pin, OUTPUT);
}

Функции dot() и dash()

Теперь сам код из исходного скетча — с префиксом 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

Комментарий-шапку принято добавлять и в файл реализации. Вот полный файл:

/*
  Morse.cpp — библиотека для передачи азбуки Морзе световыми сигналами.
  Создана David A. Mellis, 2 ноября 2007 г.
  Обновлена Jason A. Cox, 18 февраля 2023 г.
  Передана в общественное достояние.
*/

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

Morse::Morse(int pin)
{
  _pin = pin;
}

void Morse::begin()
{
  pinMode(_pin, OUTPUT);
}

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);
}

Это всё, что нужно для минимальной рабочей библиотеки. Дальше — о том, как её использовать.

Установка и подключение библиотеки

  1. Создайте папку Morse внутри папки libraries вашего скетчбука.
  2. Скопируйте туда файлы Morse.h и Morse.cpp.
  3. Перезапустите Arduino IDE.

После этого в меню Sketch > Import Library появится пункт Morse. Библиотека будет автоматически компилироваться вместе со скетчами, которые её используют.

Типичная ошибка: файлы не распознаются, если к расширению случайно добавился .ino, .pde или .txt. Проверьте, что файлы называются именно Morse.cpp и Morse.h.

Скетч с использованием библиотеки

Вот как выглядит переписанный SOS-скетч с нашей библиотекой:

#include <Morse.h>

Morse morse(13);

void setup()
{
  morse.begin();
}

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

Что изменилось по сравнению с исходником

Директива #include в начале скетча подключает библиотеку и включает её код в прошивку. Если библиотека больше не нужна — удалите #include, чтобы не тратить память зря.

Создание объекта класса Morse:

Morse morse(13);

Эта строка выполняется ещё до вызова setup(). В этот момент вызывается конструктор класса Morse и получает аргумент 13.

Вызов begin(): функция morse.begin() теперь вызывается из setup() скетча — именно там нужно настраивать пин.

Вызов функций через имя объекта: чтобы вызвать dot() или dash(), перед ними ставится имя объекта morse. Это позволяет иметь несколько независимых объектов класса Morse, каждый со своим пином в приватной переменной _pin:

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

Внутри вызова morse2.dot() переменная _pin будет равна 12.

Подсветка синтаксиса: файл keywords.txt

Вы, наверное, заметили, что IDE не подсвечивает имена из вашей библиотеки. Нужно помочь редактору — создайте файл keywords.txt в папке Morse:

Morse	KEYWORD1
begin	KEYWORD2
dash	KEYWORD2
dot	KEYWORD2

Каждая строка содержит имя ключевого слова, затем табуляцию (не пробелы!), затем тип:

  • KEYWORD1 — для классов, подсвечивается оранжевым;
  • KEYWORD2 — для функций, подсвечивается коричневым.

После сохранения файла перезапустите Arduino IDE, и подсветка заработает.

Примеры для библиотеки

Хорошая практика — добавить к библиотеке готовые примеры:

  1. Создайте папку examples внутри папки Morse.
  2. Переместите туда папку со скетчем SOS (найти её можно через Sketch > Show Sketch Folder).
  3. Перезапустите Arduino IDE.

Теперь в меню File > Sketchbook > Examples появится пункт Library-Morse с вашим примером. Добавьте в скетч поясняющие комментарии — пользователи скажут спасибо.

Публикация библиотеки

Готовую библиотеку (с keywords.txt и примером) можно скачать здесь: Morse.zip.

Чтобы опубликовать библиотеку в Library Manager Arduino, потребуется добавить файл library.properties. Подробнее — в library specification.

По общим вопросам работы с Library Manager смотрите FAQ.

Если возникнут вопросы или предложения — пишите на Software Development forum.

Дополнительные рекомендации по созданию API в стиле Arduino: API Style Guide.

Практические замечания

  • Не настраивайте пины в конструкторе — делайте это в отдельной функции begin(), которую вызывают из setup(). В момент создания глобального объекта микроконтроллер ещё не готов к работе с периферией.
  • Приватные переменные с подчёркиванием — это соглашение, а не требование языка. Главное, чтобы имена совпадали между .h и .cpp.
  • Защитные макросы в .h (#ifndef / #define / #endif) обязательны: без них повторное подключение заголовка вызовет ошибку компиляции.
  • Проверяйте расширения файлов: IDE на некоторых операционных системах может скрывать расширения, и файл Morse.cpp окажется на самом деле Morse.cpp.txt.