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

Style Guide для создания библиотек Arduino

Руководство по написанию библиотек в стиле Arduino: понятный API, правильные соглашения об именовании и лучшие практики для авторов библиотек.

Это руководство по стилю для тех, кто пишет библиотеки APIs в духе Arduino. Некоторые рекомендации расходятся с принятой в профессиональном программировании практикой — и это осознанный выбор. Именно такой подход позволил миллионам начинающих легко войти в мир Arduino. Поэтому, пожалуйста, пишите код с учётом этих принципов. Если у вас есть идеи, как сделать библиотеки Arduino ещё понятнее для новичков, — присоединяйтесь к обсуждению.

Принципы проектирования API

Думайте о конечном пользователе. Представьте, что пишете API для умного человека, который никогда раньше не программировал. Сформулируйте чёткую мысленную модель концепции, с которой работаете, и подберите понятные термины и названия функций.

Соответствуйте реальным возможностям устройства. Не стоит раскрывать детали реализации, но и API не должен создавать ложного представления о возможностях. Например, если у параметра есть лишь несколько допустимых значений, не используйте функцию с аргументом типа int — это намекает на то, что можно передать любое число.

Организуйте публичные функции вокруг данных и задач пользователя. Командный набор конкретного модуля нередко избыточно сложен для типовых сценариев или поддаётся реорганизации вокруг функций более высокого уровня. Подумайте, что среднестатистический человек ожидает от устройства, и стройте API вокруг этого. Хороший пример — библиотека Adafruit® BMP085 library. Функция readPressure() выполняет все необходимые шаги для получения итогового значения давления. Библиотека оборачивает типичную последовательность вызовов в одну высокоуровневую команду, которая возвращает результат в ожидаемом формате. При этом она скрывает низкоуровневые команды I2C, а также промежуточные вычисления температуры и давления — хотя эти промежуточные функции остаются публичными для тех, кому они нужны.

Используйте полные, повседневные слова. Не скупитесь на длину имён функций и переменных. Выбирайте бытовые термины вместо технических жаргонизмов. Опирайтесь на то, как люди обычно понимают то или иное понятие. Не рассчитывайте на специализированные знания. Именно поэтому в ядре Arduino использовали analogWrite(), а не pwm(). Аббревиатуры допустимы, если они широко распространены или являются основным названием. Например, «HTML» понятен большинству, а «SPI» — это фактически имя протокола (писать «serial-peripheral interface» каждый раз было бы слишком длинно). Кстати, «Wire» — вероятно, не лучший выбор: протокол чаще называют «TWI» или «I2C».

Избегайте слов, которые у обычных людей означают другое. Например, для программиста «ошибка» — это уведомление о событии. Для обычного человека ошибки — это что-то плохое.

Если без специального термина не обойтись — сначала объясните его простыми словами. Скорее всего, в процессе вы найдёте более удачное слово. А если нет — у вас уже будет готовое начало документации к библиотеке.

Документируйте и комментируйте по ходу работы. При написании примеров и документации следуйте Writing Style Guide

Используйте стандартные библиотеки и соглашения

Используйте read() для чтения входных данных и write() для записи в выходные, например digitalRead(), analogWrite() и т. д. При работе с потоками байт используйте классы Stream и Print. Если это не подходит, хотя бы ориентируйтесь на их API как на образец — подробнее см. ниже. Для сетевых приложений используйте классы Client и Server как основу. Используйте begin() для инициализации экземпляра библиотеки (обычно с передачей настроек) и end() для остановки. * Используйте camelCase для имён функций, а не подчёркивания. Например, analogRead, а не analog_read. Или myNewFunction, а не my_new_function. Это соглашение принято от Processing.org ради читаемости.

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

LONG_CONSTANT_NAMES_FULL_OF_CAPS трудно читать. По возможности упрощайте, не жертвуя ясностью.

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

Не рассчитывайте на знание указателей. Для начинающих это главный камень преткновения в C. Символы & и * вызывают сильное замешательство, поэтому по возможности не выставляйте их в API. Один из способов — передавать по ссылке, используя нотацию массивов вместо нотации *.

Например, вместо:

void printArray(char* array);

можно написать:

void printArray(char[] array);

Хотя в некоторых библиотеках указатели передаются через структуры вроде const char, избегайте ситуаций, когда пользователь вынужден передавать их явно. Например, вместо:

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.

Если вы пишете библиотеку, обеспечивающую байтовую потоковую связь, унаследуйте класс Stream из ядра Arduino — тогда вашу библиотеку можно будет использовать со всеми другими библиотеками, принимающими объекты Stream. По возможности буферизуйте входящие данные: read() должен немедленно обращаться к буферу, не ожидая прихода новых данных. По возможности метод write() должен помещать данные в буфер передачи, однако write() обязан ждать, если в буфере недостаточно места для немедленной записи всех исходящих данных. Пока идёт ожидание, следует вызывать функцию yield().

Примеры хороших библиотек

Вот несколько образцовых библиотек от Adafruit® — в них функции устройств очень удачно разбиты по высокоуровневым действиям:

https://github.com/adafruit/Adafruit-BMP085-Library https://github.com/adafruit/DHT-sensor-library

Эта библиотека хорошо абстрагирует работу с Wire (I2C): https://github.com/adafruit/RTClib