Arduino example class

Классы. Урок 25. Ардуино

Привет! Раньше я уже писал как организовать код для Ардуино в функции. Но есть еще один способ программирования основанный на объектах. Естественно, это объектно-ориентированное программирование. А основа любого объекта — это классы. Так что давайте создадим класс для программы на Ардуино и посмотрим, как это работает.

Нам также понадобятся и функции. Так что, если вы уже забыли тот урок или пропустили его, посмотрите его.

Сегодня напишем небольшую программу для управления светодиодом, но оформим ее в классы. И будем использовать объект для более удобного управления программой и кодом.

Классы

Класс — это часть программы оформленная определенным образом. Классы удобно использовать для группировки кода, относящегося к одной, нужной нам области.

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

Пишем тестовый класс

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

В программе нам будет нужно определить пин, на который подключен светодиод. Создать способ зажечь светодиод и выключить его.

Определение класса похоже на функцию, но чтобы определить в программе класс, мы используем ключевое слово class.

Внутри класса мы можем определить публичные, приватные или защищенные свойства и функции.

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

Приватные методы можно использовать только из текущего класса.

Защищенные методы можно использовать из текущего класса или из подклассов. Об этих методах поговорим позже.

Сейчас мы описали наш класс и два публичных метода on() и off(), для включения и выключения светодиода. Также в разделе public мы описали конструктор класса Led(int pin). В него мы будем передавать номер пина при создании объекта Led.

Переменную int _pin мы внесли в раздел private. Она нужна для определения пина на плате Ардуино. Она должны быть приватной, чтобы было невозможно изменить пин после создания объекта. Таким образом, мы защитим использование объекта от ошибок в программе.

Методы класса

Мы описали конструктор и два публичных метода. Но пока, они ничего не делают. Давайте реализуем эти методы в классе.

При создании объекта класса led, мы вызываем конструктор класса и передаем в него номер пина на плате. Далее конструктор настраивает пин на вывод сигналов и сохраняет номер в переменную _pin в приватной области. Таким образом изменить номер пина для этого объекта будет невозможно.

Метод on() выставляет уровень High на пин и, соответственно метод off() выставляет уровень Low на пин. Таким образом, мы сможем зажигать и выключать светодиод.

Использование класса Led

Теперь сохраним наш класс в отдельный файл led.h. Подключим его к программе, создадим объект и посмотрим как использовать методы класса.

Здесь мы подключили файл класса, создали объект класса и передали в конструктор номер пина. А в основном цикле программы мы вызываем метод класса on() и, после небольшой паузы метод off().

Заключение

В этом уроке мы написали первый класс Led для управления светодиодом. Классы помогут нам организовать код в более наглядную и удобную форму. А также позволят нам создавать готовые программные модули и оформлять их как библиотеки.

Источник

Начинающим на Arduino: Упаковываем конечный автомат в отдельный класс и библиотеку

В прошлой статье про написание конечных автоматов я обещал упаковать наш гениальный код в виде класса на C++ для повторного удобного использования. Делать буду так же на примере своей старой разработки SmartButton. Итак, влезаем в непонятный мир ардуининых библиотек и ООП .

Зачем всё это нужно?

Arduino IDE позволяет использовать синтаксис C++11, оказывается. То есть, там очень развитый объектно-ориентированный язык. Нам же хочется сосредотачиваться на нашем гениальном коде и размазанная по программе лишняя логика частенько мешает сосредоточиться. Взять, например, всякие дисплейчики, кнопочки, датчики и релюшки — у каждого же своя логика, зачем её смешивать с общей логикой программы. Тот же, например, дисплей. У него много полей, статических и изменяемых. Ой, поле — это же класс. Поле может входить в меню (класс меню) или нет, быть часть частью виртуального дисплея (класс), которых на физическом эеране может быть насколько (дисплеи: рабочий, настроек, диагностики и т.п.). Меню, в свою очередь, управляется кнопками (классы кнопок могут быть разными) или джойстиком (класс). Всё это вместе — класс «дисплей», который можно объявить в своей программе как:

Если вы делаете проект не совсем на коленке не кое как и собираетесь что-то потом менять или повторно использовать какие-то свои наработки — лучше оформить сделанное в виде библиотек Arduino. В идеале, конечно же, положить в Github для других людей, если вам не жалко и вы не против, что кто-то ваш код исправит или дополнит.

Раз уж мы в прошлой статье делали кнопочку, давайте её оформим как класс и библиотеку?

Итак, наша задача сделать так, чтобы мы могли в своих скетчах писать:

Как сделать библиотеку Arduino?

Сначала надо решить, как ваша библиотека будет называться. Пусть для примера, это будет MyLib.

Найдите, где лежат ваши скетчи на компьютере. Они лежат каждый в своей папочке, а рядом с ними есть папка libraries (библиотеки). Например, на маке /Users/Пользователь/Documents/Arduino/libraries и на виндоусе c:\Users\Пользователь\Документы\Arduino\libraries. Я сам сижу на маке и пути в виндах не знаю. Найдёте.

Вот в этой папке libraries создайте новую папку MyLib, то есть с именем своей библиотеки. Перейдите туда.

В этой новой папке надо создать как минимум один файл MyLib.h, тот, что вы будет включать в ваш проект. Минимальное его содержимое выглядит примерно так:

Расскажу, что здесь зачем. Конструкция ниже позволяет включать вашу библиотеку в код несколько раз без ошибок. Лучше использовать название вашей библиотеки большими буквами. Это не сурово прямо обязательно, но все так делают и вы не выделяйтесь. Задача стоит придумать уникальное слово, в нашем случае MYLIB_H, идентификатор для этого заголовочного файла.

То есть, в вашем скетче может оказаться несколько таких строк:

Вы скажете «тю, да я, да я слежу, да я. » и будете неправы. Лучше один раз написать в одном файле вот такую конструкцию, чем исправлять ваши готовые скетчи, если вдруг вы захотите вложить один в другой или ваша библиотека будет включена в другую итд. Данный код проверяет, определено ли слово MYLIB_H, если нет, то определяет его и включает дальнейший код. Если же слово уже определено, то второй раз код компилировать не нужно.

Следующий важный кусок кода:

Включает определения из исполняющей системы Arduino UDE. Без этого ваша библиотека просто не скомпилируется.

Всё. Закройте Arduino IDE, Откройте заново. Создайте новый скетч, пропишите там #include «MyLib.h» и ура, ваша библиотека есть и подключена!

Я смотрел, в библиотеке вроде как много файлов должно быть?

Да, конечно. Мы сделали минимальные действия, чтобы создать библиотеку. Теперь настало время планирования.

Чтобы я мог помещать сюда куски своего кода копипастом, я назову библиотеку SmartButton, ладно? Болванку MyLib можно прибить за ненадобностью.

По аналогии с предыдущим пунктом, создаём папку SmartButton, в ней:

  • SmartButton.h — То, что мы будем включать в наши программы. Там будут только определения, без кода.
  • SmartButton.cpp — Программный код класса. Это не скетч! Обратите внимание, что расширение файла cpp (C++).
  • README.md — Файл описания библиотеки «для людей», то есть, документация. «md» означает MarkDown, то есть с разметкой. Достаточно назвать просто README.
  • library.json — описание библиотеки для Arduino IDE в хитром формате JSON.
  • examples — папка с примерами, которые будут потом видны в Arduino IDE. В ней должны лежать папки с именами примеров, в а них с тем же именем файлы с расширением ino — скетчи.

Давайте поясню суть затеи. Мы не знаем, что нам будет нужно от кнопки. Наш МКА умеет находиться в состояниях Клик, Нажатие, Удержание и СлишкомДолгоеУдержание, а так же выходить из этих состояний в состояние Выключен. Так как мы делаем библиотеку универсальную, то надо предоставить возможность другому программисту вставить свой код в обработчики состояний. В ООП есть для этого замечательное средство — наследование.

Мы делаем класс, у которого есть несколько методов (функций) и они пустые. То есть, они есть, они будут вызываться в нужный момент, но кода в них нет. Зачем это? Затем, что в скетче можно будет создать свой класс на базе нашего, определить там только нужные из методов и наполнить их своим кодом.

Например, мы захотим сделать кнопку-переключатель, то есть, одно нажатие — включено, другое — выключено. Будем зажигать и гасить светодиод и предоставим функцию isOn() для использования в классическом виде в функции loop().

Как видите, нас совершенно здесь не интересует МКА кнопочки из предыдущей статьи, кода этой кнопки нет, он спрятан. Мы добавили свою функциональность к базовому классу и сделали переключатель по клику. Наш новый класс Toggle тоже можно оформить в виде библиотеки, кстати или положить в отдельный файл Toggle.h рядом с вашим скетчем, вам достаточно будет его подключить директивой #include. Мы так же задаём ногу со светодиодом для подсветки кнопки. Обратите внимание, что мы просто создали два объекта (bt и drill) нового класса Toggle, а МКА обработки кнопки для нас скрыт и не заботит.

Основываясь на классе SmartButton можно сделать свои классы, что понимают двойной клик, например, водят курсор по меню или поворачивают пулемётную турель медленно-быстрее в зависимости от времени удержания кнопки. Для этого достаточно определить свои методы, описанные в SmartButton.h как virtual. Все определять не обязательно, только нужные вам.

По просьбе целевой аудитории, вот пример класса PressButton, который предоставляет методы:

  • pressed() — кнопка была нажата, можно вызывать много раз.
  • ok() — я понял, слушай кнопку дальше, то есть сброс.

Таким образом мы получаем две независимо работающие «залипающие» кнопки, которые после нажатия находятся в состоянии pressed пока их не сбросить методом ok().

Если у вас есть меню, вы можете определить методы onClick() у кнопок «вверх» и «вниз», которые будут вызывать перемещение курсора меню на дисплее с соответствующем направлении. Определение onHold() у них может вызывать перемещение курсора в начало и конец меню, например. У кнопки «ентер» можно определить onClick() как выбор меню, onHold() как выход с сохранением, а onLongHold() как выход без сохранения.

Если вам нужен двойной клик, ну, определите onClick так, чтобы у вас там был счётчик нажатий и время с предыдущего нажатия. Тогда вы сможете различать одинарный и двойной клик.

SmartButton — это просто МКА, это инструмент для реализации поведения ваших кнопок.

Где же скрыта вся магия? Магия кроется в файле SmartButton.cpp

Логика местами спорная, я знаю :) Но это работает.

Теперь осталось заполнить файл README описанием вашей библиотеки и заполнить по аналогии файлик library.json, где поля вполне очевидны:

Если у вас нет репозитория, можно эту секцию не указывать.

Ура! Библиотека готова. Можно запаковать папку в ZIP и раздавать друзьям или копировать на другие свои компьютеры.

По аналогии, можно сделать класс для любой МКА. Принцип общий: вы делаете класс, определяете виртуальные методы, которые потом надо будет переопределить, чтобы вставить свой код или готовые методы, если универсальность не требуется.

Что за Github и зачем он мне?

Github — это огромное сообщество программистов. Да, ваш код будет публично светиться на весь интернет, но… любой человек может предложить свои правки к вашему коду. Мне, например, очень помогли с SmartDelay два человека, один из которых сделал свою подобную библиотеку и мы поподсматривали чуть-чуть код друг у друга. Лучше две хорошие библиотеки, чем две глюкавые, правда?

Чтобы поместить вашу библиотеку в Github надо сделать там аккаунт, сгенерить ключ и создать репозиторий с там же именем, что ваша библиотека (папка). Файлы можно загрузить через web-шнтерфейс.

Для установки библиотеки из Github в Arduino IDE достаточно скопировать URL и воспользоваться утилитой git:

Или загрузить ZIP — это будет как раз библиотека Arduino, как и все прочие библиотеки.

Как пользоваться git вообще и Github в частности, есть много статей наверняка. Попробуйте поискать. Если не найдёте, я напишу как им пользуюсь я.

Источник

Объекты и классы


Класс является одним из самых крупных и важных инструментов языка C++, именно он делает язык объектно-ориентированным и очень мощным. Именно благодаря добавлению классов в язык Си и появился язык C++, его даже называют “Си с классами”. Вы уже сталкивались с классами, потому что 99% библиотек состоят из класса, описанного в отдельном файле! Некоторые встроенные инструменты Arduino также являются объектами, например Serial .

Класс – это просто независимая подпрограмма, в которой есть свой набор переменных и функций (обязательно изучи урок про функции). В основной программе мы можем создать экземпляр класса (он же называется объект) и пользоваться теми инструментами, которые имеются в классе. Использование классов позволяет:

  • Разделить сложную программу на отдельные независимые части
  • Создавать удобные библиотеки
  • Использовать свои “наработки” в другом проекте, не переписывая один и тот же код
  • Облегчить и упростить программу, если в ней используются повторяющиеся конструкции и алгоритмы

Давайте рассмотрим пример из библиотеки, пусть это будет стандартная и всем знакомая библиотека Servo. Возьмём из неё пример Knob и разберёмся, кто и как называется:

Мы создали объект myservo класса Servo и можем управлять углом сервопривода при помощи функции write() – она применяется к объекту через точку . . Функция, применяемая к объекту, называется методом.

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

Создание класса

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

Класс объявляется при помощи ключевого слова class и содержит внутри себя члены класса – переменные и функции:

Важное отличие от структуры: содержимое класса делится на области: публичные и приватные. Они определяются при помощи ключевых слов public и private , область действует до начала следующей области или до закрывающей фигурной скобки класса:

  • public – члены класса в этой области доступны для взаимодействия из основной программы (скетча), в которой будет создан объект. Например write() у серво.
  • private – члены класса в этой области доступны только внутри класса, то есть из программы к ним обратиться нельзя.

В классе должна быть указана хотя бы одна область. Например только public .

Теперь давайте заглянем “под капот” класса Servo и сравним теорию с практикой:


Собственно мы видим в “публичном доступе” все те методы, которыми можно пользоваться при работе с Servo. Это очень удобно, потому что не нужно гуглить искать документацию – всё написано в описании класса. Методы объявляются точно так же, как обыкновенные функции (читай урок про функции).

Приватные члены в классе серво – переменные, из их названий можно понять, что они в себе хранят. Доступа к этим переменным “из скетча” мы не имеем, обращаться к этим переменным могут только методы класса.

Также на картинке выше вы могли увидеть слово конструктор – это функция, которая вызывается в момент создания объекта и имеет такое же имя, как сам класс. Может использоваться для инициализации переменных, передачи настроек и так далее.

Создание объекта

Создаётся объект точно так же, как структура – по имени класса:

Обращение к членам класса осуществляется точно так же, как в структурах: через оператор точка . :

Пишем свой класс

Рассмотрим абстрактный пример: мигающий светодиод. Мигать будем по таймеру на миллис (читай урок про многозадачность). Нам понадобятся следующие переменные:

  • Переменная для хранения номера пина
  • Переменная таймера
  • Переменная для хранения периода
  • Флаг текущего состояния светодиода
  • Нужно сделать пин выходом при запуске программы, внутри setup()
  • В основном цикле программы loop() будет конструкция таймера, внутри которой светодиод переключается

Итоговая программа, которая асинхронно мигает светодиодом на 13 пине с периодом 500 миллисекунд:

Всё хорошо, программа работает, светодиод мигает. Далее нам захотелось добавить второй светодиод на пин 12 и сделать с ним всё то же самое, но мигать с периодом 1 секунда. Нужно продублировать все имеющиеся переменные и выполняемый код. Чтобы не запутаться в именах переменных, нам придётся их пронумеровать.

Отлично! А если нужно добавить ещё 10 светодиодов? Да, можно оптимизировать программу в таком виде как она есть, добавить массив таймеров и использовать циклы, но этот урок – про классы. Поэтому давайте обернём всю подпрограмму “мигающего светодиода” в отдельный независимый класс.

Напишем заготовку и сразу разместим все переменные в приватной области: прямой доступ к ним из программы нам не нужен.

Разместим данный код в самом верху скетча, до блоков setup() и loop() .

На данный момент мы уже можем создать объекты двух наших светодиодов:

Теперь в нашей программе есть два объекта, led1 и led2 , каждый хранит в себе набор переменных из класса. Первым делом нужно присвоить значения жизненно необходимым переменным – это номер пина и период работы. Эти параметры являются основными для нашего мигающего светодиода и будут отличаться у разных светодиодов, поэтому было бы удобно задавать их сразу при создании объекта.

Здесь на помощь придёт конструктор – функция, которая будет вызвана при создании объекта. Она должна иметь такое же название, как и сам класс. Давайте передадим в неё номер пина и период, а затем запишем их во внутренние переменные. Также в конструкторе сразу можно сделать пин выходом!

Теперь создание объектов можно переписать. Укажем параметры из первого примера:

Готово, значения записаны. Осталось реализовать функцию мигания: назовём её blink() и поместим в неё конструкцию из самого первого примера:

Теперь в главном цикле программы нам достаточно вызывать эти методы у обоих объектов:

Что делать, если захочется изменить период мигания во время работы? Есть два варианта:

  • Сделать переменную периода публичной и обращаться к ней из программы напрямую
  • Сделать отдельный метод для изменения этой переменной

Чаще всего используют второй вариант, чтобы вообще не оперировать в программе переменными, а использовать только методы. Добавим в класс функцию:

И сможем менять период мигания, например так:

Ну вот, мы с вами на практике разобрали создание класса. Теперь можно переместить весь код класса в отдельный файл, назвать его например LED.h, поместить рядом со скетчем, а затем подключить в программу как #include «LED.h» . Наша программа стала компактнее, в ней нет кучи глобальных переменных и повторяющихся блоков кода. Более того, программа стала занимать меньше памяти, потому что одинаковые конструкции теперь представлены одним блоком кода, который работает для всех объектов одинаково.

Да, фактически мы сделали свою собственную библиотеку! Оформление библиотек и некоторые другие трюки с классами мы разберём в следующем уроке про написание библиотек.

Источник

Adblock
detector