Руководство по стилю для создания библиотек Arduino
Узнайте, как писать API библиотек в стиле Arduino.
Последнее обновление: 30.10.2025
Введение
Это руководство по стилю написания API библиотек в стиле Arduino. Некоторые рекомендации расходятся с общепринятой профессиональной практикой программирования. Мы это осознаём, но именно они позволили стольким начинающим легко освоить Arduino. Поэтому, пожалуйста, пишите код с учётом этих принципов. Если у вас есть предложения по тому, как сделать библиотеки Arduino более понятными для этой основной аудитории, приглашаем вас к обсуждению.
Основные принципы
Будьте внимательны к конечному пользователю. Представьте, что вы пишете API для умного человека, который ещё не умеет программировать. Сформулируйте чёткую мысленную модель концепции, с которой вы работаете, а также термины и функции, которые вы будете использовать.
Согласуйте API с реальными возможностями. Вы не хотите раскрывать детали реализации пользователю, но и не хотите API, который формирует неточное представление о возможностях. Например, если для определённой настройки существует лишь несколько возможных вариантов, не используйте функцию, принимающую int, — это подразумевает, что можно использовать любое значение.
Организуйте публичные функции вокруг данных и функциональности, которые нужны пользователю. Довольно часто набор команд конкретного электронного модуля избыточно усложнён для наиболее распространённых применений или может быть переработан вокруг функциональности более высокого уровня. Подумайте о том, что среднестатистический человек считает предназначением устройства, и постарайтесь организовать функции API вокруг этого. Хороший пример — библиотека Adafruit® BMP085. Функция
readPressure()
выполняет все необходимые шаги для получения итогового значения давления. Библиотека оборачивает эту типичную последовательность функций в единую высокоуровневую команду, которая возвращает нужное пользователю значение в ожидаемом формате. Она абстрагирует не только низкоуровневые команды I2C, но и промежуточные вычисления температуры и давления, при этом по-прежнему предоставляя эти промежуточные функции как публичные для тех, кому они нужны.
Используйте полные, общеупотребительные слова. Не экономьте на именах функций и переменных. Используйте повседневные термины вместо технических. Выбирайте термины, соответствующие народному восприятию рассматриваемой концепции. Не рассчитывайте на специализированные знания. Именно поэтому мы использовали
analogWrite()
вместо
pwm()
Аббревиатуры допустимы, если они широко используются или являются основным названием чего-либо. Например, «HTML» достаточно распространён, а «SPI» — фактически название протокола («serial-peripheral interface» — вероятно, слишком длинно). («Wire» было, пожалуй, ошибкой, поскольку используемый им протокол обычно называется «TWI» или «I2C».)
Избегайте слов, имеющих иное значение для широкой публики. Например, для программистов «ошибка» (error) — это уведомление о том, что что-то произошло. Для широкой публики ошибки — это что-то плохое.
Когда вам приходится использовать специфический термин, сначала напишите одно-два предложения, описывающих его для широкой аудитории. Скорее всего, вы придумаете лучший термин, а если нет — то уже начнёте документацию к своей библиотеке.
Документируйте и комментируйте по ходу работы. При написании примеров и документации следуйте Руководству по стилю написания.
Соглашения по стилю кода
Используйте существующие базовые библиотеки и их стиль.
Используйте
read()
для чтения входных данных и
write()
для записи в выходные, например:
digitalRead() analogWrite()
Используйте классы Stream и
Printпри работе с потоками байтов. Если это неприменимо, постарайтесь хотя бы использовать их API в качестве образца. Подробнее об этом — ниже.
Для сетевых приложений используйте классы
Clientи
Serverв качестве основы.
Используйте
begin()
для инициализации экземпляра библиотеки, как правило, с некоторыми настройками. Используйте
end()
для его остановки.
Используйте camelCase для имён функций, а не подчёркивание. Например, analogRead, а не analog_read. Или myNewFunction, а не my_new_function. Мы переняли это из Processing.org ради удобочитаемости.
Важно
ДЛИННЫЕ_ИМЕНА_КОНСТАНТ_ПОЛНОСТЬЮ_В_ВЕРХНЕМ_РЕГИСТРЕ трудно читать. По возможности старайтесь упростить, не жертвуя ясностью.
Дополнительные рекомендации
Старайтесь избегать булевых аргументов. Вместо этого рассмотрите возможность предоставления двух разных функций с именами, описывающими различия между ними.
Не рассчитывайте на знание указателей. Для начинающих пользователей C это самое большое препятствие, и они очень путаются с
&
и
*
поэтому, когда можно избежать их присутствия в API, делайте это. Один из способов — передача по ссылке с использованием нотации массивов вместо нотации
*
Например:
void printArray(char* array);
можно заменить на:
void printArray(char[] array);
Хотя есть библиотеки, где мы передаём указатели через такие структуры, как const chars, избегайте всего, что требует от пользователя их передачи. Например, вместо:
foo.readAccel(&x, &y, &z);
используйте что-то вроде:
xAxis = adxl.readX();
yAxis = adxl.readY();
zAxis = adxl.readZ();
Работа с последовательной связью
При использовании последовательной связи позвольте пользователю указать любой объект
Stream
вместо того чтобы жёстко задавать
Serial
Это сделает вашу библиотеку совместимой со всеми последовательными портами на платах с несколькими портами (например, Mega), а также позволит использовать альтернативные интерфейсы, такие как SoftwareSerial. Объект Stream может быть передан в конструктор вашей библиотеки или в функцию
begin()
(по ссылке, а не по указателю). Смотрите примеры в Firmata 2.3 или XBee 0.4.
При написании библиотеки, предоставляющей байтовую потоковую связь, наследуйте класс Arduino
Stream
чтобы ваша библиотека могла использоваться со всеми другими библиотеками, принимающими объекты
Stream
По возможности буферизуйте входящие данные, чтобы
read()
немедленно обращался к буферу и не ждал поступления новых данных. Если возможно, ваш метод
write()
должен сохранять данные в буфер передачи, однако
write()
должен ждать, если в буфере недостаточно места для немедленного сохранения всех исходящих данных. Пока происходит ожидание, следует вызывать функцию
yield()
Примеры образцовых библиотек
Вот несколько образцовых библиотек от Adafruit®. Она очень хорошо разбивает функции устройств на их высокоуровневые действия.
Это хороший пример абстрагирования от библиотеки Wire (I2C): https://github.com/adafruit/RTClib