Ардуино программирование библиотеки



Пишем свою библиотеку под Arduino

Одна из довольно сильных сторон любого программного обеспечени — это возможность единожды написанной программы быть использованной многократно как в виде отдельных частей, так и целиком, что и привело к зарождению концепции «библиотеки».

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

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

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

Такие фрагменты имеет смысл упаковывать в библиотеки, кроме того, если над одним и тем же проектом работает несколько человек, можно с лёгкостью поделиться своими наработками с другими (не забыв дать им описание API, кстати говоря, подробнее расскажу об этом чуть ниже).

Если вы до этого уже интересовались сутью библиотек и пытались разбирать существующие библиотеки Arduino, то наверняка успели заметить, что они состоят из двух отдельных файлов, один из которых имеет расширение .cpp — что означает «С Plus Plus». Так как язык Wiring для Arduino базируется, по сути, на языке C++, то и решили создавать файлы с таким расширением. Видимо, создатели подумали, что «а ещё это просто красиво» ©. Второй же компонент библиотеки имеет расширение .h ( «Headers»):

  • Файл .cpp — называется файлом реализации.
  • Файл .h — называется файлом заголовков.

Теперь рассмотрим эту концепцию разделения на два файла на примере конкретного кода.

Допустим, что у нас есть некий код, который управляет двигателями. Этот код состоит из ряда участков, среди которых инициализация каких-то переменных и какая-то функция:

В принципе, весь этот код мы можем поместить в файл реализации, то есть с расширением .cpp.

Я специально в качестве кода для примера взял код для esp32 (чуть ниже поясню почему).

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

Те, кто давно работает с esp32, знают, что у неё некоторые функции отличаются от стандартных Arduino, теоретически мы могли бы не помещать в этот код импорт стандартных функций Arduino (ведь железка-то отличается!), но это будет неверно, так как в любом случае для инициализации пинов мы используем стандартную функцию pinMode() , кроме того, используется стандартная digitalWrite() . Поэтому, хочешь не хочешь, нам придётся включить строку:

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

Далее мы обратим внимание вот на какой момент. Дело в том, что любая работа с какой-либо периферией требует её подключения с использованием вышеназванной функции pinMode() как минимум, а есть ещё разнообразные настройки, как в нашем случае.

На первый взгляд всё хорошо и в файле присутствует подключение периферии. Однако самые внимательные уже заметили, что обычно подключение периферии в скетче у нас происходит внутри блока setup () <> .

Однако в данном случае мы работаем над созданием библиотеки, и здесь никакого блока setup () <> не существует, и если мы попытаемся оставить всё как есть, и функции подключения периферии останутся лежать «просто так, снаружи», то код с подключённой нашей самодельной библиотекой не сможет скомпилироваться, и компилятор выдаст ошибку, если мы используем вот такое содержимое файла реализации (.cpp):

Я сейчас говорю вот об этом участке, который лежит как «не пришей кобыле хвост»:

И что же делать в таком случае? А вот что: необходимо функции инициализации пинов обернуть в функцию! То есть они не должны лежать снаружи, их нужно поместить внутрь функции (setupMotors() ) :

Такой код благополучно скомпилируется, после того как мы создадим библиотеку, подключим её, а после вызовем вот эту функцию, внутри блока setup :

Есть общее правило: если требуется некий функционал, который должен быть вызван внутри блока setup (для инициализации чего-либо), то он обязательно должен быть обёрнут в функцию.

По сути, ваш файл реализации готов, и мы перейдём к файлу заголовков — с расширением .h.

Для создания файла заголовков вам всего лишь нужно перенести туда названия ваших функций из файла с расширением .cpp в виде простого списка, с точкой с запятой в конце каждой строки:

Файл тоже готов.

Кстати говоря, тут интересный момент: вы сами определяете, какие функции будут доступны «снаружи» для пользователей! То есть этот набор функций, перечисленных в файле с расширением .h — и есть Application Programming Interface (API), то есть набор способов, с помощью которых можно взаимодействовать с вашей программой. Причём, как я уже говорил, у вас в файле реализации могут внутри быть ещё и другие функции, которые вы просто не пожелали дать для использования. Имеете право, почему нет.

А теперь посмотрим чуть более сложный пример, «объектно-ориентированное программирование» у нас или где :)

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

На самом деле, даже в этой ситуации, код ненамного усложнится:

  • Принципиально подобная библиотека, содержащая класс, также будет состоять из двух отдельных файлов, сохранённых с расширениями .cpp и .h.
  • Вся реализация методов также будет собрана в файле .cpp.
  • Сами методы также будут перечислены в файле с расширением .h.

Нюанс будет заключаться в том, что в файле заголовков (.h) у нас будет находиться сам класс, внутрь которого мы и поместим эти методы.

Чтобы всё это было несколько интересней, мы можем даже немного усугубить ситуацию, добавить модификаторы доступа: public и protected .

В результате всё это будет выглядеть примерно так. Файл реализации (.cpp):

Ну и напоследок, если мы хотим, чтобы наша библиотека была «совсем модной», то можем включить туда предварительно настроенные примеры, чтобы люди могли сразу понять, как им взаимодействовать с этой библиотекой. Для этого необходимо в директории, где находится два основых файла этой библиотеки (.cpp и .h), создать ещё и отдельную папку под названием examples, внутри которой в отдельную, совпадающую по названию со скетчем папку, положить код вашего примера.

Таким образом, путь до вашего примера будет выглядеть следующим образом:

Но мало создать библиотеку, необходимо её ещё и положить в специальное место, для того чтобы среда разработки могла её увидеть:

  • В первом случае вы можете подключить заархивированную библиотеку изнутри Arduino IDE, пройдя по пути: скетч-подключить библиотеку-добавить zip. библиотеку .
  • Во втором случае вы можете просто положить её стандартную папку библиотек Arduino: C:\Arduino\libraries
  • Или если вы используете portable-версию среды разработки (т.к. я, например, ношу её везде с собой на флешке, и она не требует установки), то положить сюда: C:\arduino-1.8.19\portable\sketchbook\libraries (в моём случае используется версия Arduino 1.8.19 – у вас может быть другая).

Как «вишенку на торте», мы можем настроить подсветку ключевых слов, так как, к сожалению, для импортированных библиотек подсветка автоматом не срабатывает. Для этого необходимо создать .txt файл, который надо положить рядом с вашими двумя файлами .cpp и .h

В этом файле мы пишем, разделяя с помощью TAB-клавиши клавиатуры, определённое понятие и цвет его подсветки.

У нас есть 3 варианта подсветки:

  • KEYWORD1: толстый оранжевый шрифт (классы, типы данных).
  • KEYWORD2: оранжевый шрифт (методы, функции).
  • LITERAL1: голубой шрифт (константы).

Например, содержимое этого .txt файла может выглядеть следующим образом:

Вот таким нехитрым образом мы можем обеспечить как многократное использование удачного кода, так и лёгкое его «расшаривание» тем, кто работает в этом же направлении.

НЛО прилетело и оставило здесь промокод для читателей нашего блога:

— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS .

Источник

Writing a Library for Arduino

LAST REVISION: 10/05/2022, 01:00 PM

This document explains how to create a library for Arduino. It starts with a sketch for flashing Morse code and explains how to convert its functions into a library. This allows other people to easily use the code that you’ve written and to easily update it as you improve the library.

For more information, see the API Style Guide for information on making a good Arduino-style API for your library.

We start with a sketch that does simple Morse code:

If you run this sketch, it will flash out the code for SOS (a distress call) on pin 13.

The sketch has a few different parts that we’ll need to bring into our library. First, of course, we have the and functions that do the actual blinking. Second, there’s the variable which the functions use to determine which pin to use. Finally, there’s the call to that initializes the pin as an output.

Let’s start turning the sketch into a library!

You need at least two files for a library: a header file (w/ the extension .h) and the source file (w/ extension .cpp). The header file has definitions for the library: basically a listing of everything that’s inside; while the source file has the actual code. We’ll call our library «Morse», so our header file will be . Let’s take a look at what goes in it. It might seem a bit strange at first, but it will make more sense once you see the source file that goes with it.

The core of the header file consists of a line for each function in the library, wrapped up in a class along with any variables you need:

A class is simply a collection of functions and variables that are all kept together in one place. These functions and variables can be public, meaning that they can be accessed by people using your library, or private, meaning they can only be accessed from within the class itself. Each class has a special function known as a constructor, which is used to create an instance of the class. The constructor has the same name as the class, and no return type.

You need a couple of other things in the header file. One is an #include statement that gives you access to the standard types and constants of the Arduino language (this is automatically added to normal sketches, but not to libraries). It looks like this (and goes above the class definition given previously):

Finally, it’s common to wrap the whole header file up in a weird looking construct:

Basically, this prevents problems if someone accidentally ‘s your library twice.

Finally, you usually put a comment at the top of the library with its name, a short description of what it does, who wrote it, the date, and the license.

Let’s take a look at the complete header file:

Now let’s go through the various parts of the source file, Morse.cpp.

First comes a couple of #include statements. These give the rest of the code access to the standard Arduino functions, and to the definitions in your header file:

Then comes the constructor. Again, this explains what should happen when someone creates an instance of your class. In this case, the user specifies which pin they would like to use. We configure the pin as an output save it into a private variable for use in the other functions:

There are a couple of strange things in this code. First is the Morse:: before the name of the function. This says that the function is part of the Morse class. You’ll see this again in the other functions in the class. The second unusual thing is the underscore in the name of our private variable, . This variable can actually have any name you want, as long as it matches the definition in the header file. Adding an underscore to the start of the name is a common convention to make it clear which variables are private, and also to distinguish the name from that of the argument to the function (pin in this case).

Next comes the actual code from the sketch that you’re turning into a library (finally!). It looks pretty much the same, except with Morse:: in front of the names of the functions, and instead of pin:

Finally, it’s typical to include the comment header at the top of the source file as well. Let’s see the whole thing:

And that’s all you need (there’s some other nice optional stuff, but we’ll talk about that later). Let’s see how you use the library.

First, make a Morse directory inside of the libraries sub-directory of your sketchbook directory. Copy or move the Morse.h and Morse.cpp files into that directory. Now launch the Arduino environment. If you open the Sketch > Import Library menu, you should see Morse inside. The library will be compiled with sketches that use it. If the library doesn’t seem to build, make sure that the files really end in .cpp and .h (with no extra .pde or .txt extension, for example).

Let’s see how we can replicate our old SOS sketch using the new library:

There are a few differences from the old sketch (besides the fact that some of the code has moved to a library).

First, we’ve added an #include statement to the top of the sketch. This makes the Morse library available to the sketch and includes it in the code sent to the board. That means if you no longer need a library in a sketch, you should delete the #include statement to save space.

Second, we now create an instance of the Morse class called morse:

When this line gets executed (which actually happens even before the function), the constructor for the class will be called, and passed the argument you’ve given here (in this case, just 13).

Notice that our is now empty; that’s because the call to happens inside the library (when the instance is constructed).

Finally, to call the and functions, we need to prefix them with morse. — the name of the instance we want to use. We could have multiple instances of the class, each on their own pin stored in the _pin private variable of that instance. By calling a function on a particular instance, we specify which instance’s variables should be used during that call to a function. That is, if we had both:

then inside a call to , would be 12.

If you tried the new sketch, you probably noticed that nothing from our library was recognized by the environment and highlighted in color. Unfortunately, the Arduino software can’t automatically figure out what you’ve define in your library (though it would be a nice feature to have), so you have to give it a little help. To do this, create a file called keywords.txt in the Morse directory. It should look like this:

Each line has the name of the keyword, followed by a tab (not spaces), followed by the kind of keyword. Classes should be KEYWORD1 and are colored orange; functions should be KEYWORD2 and will be brown. You’ll have to restart the Arduino environment to get it to recognize the new keywords.

It’s also nice to provide people with an example sketch that uses your library. To do this, create an examples directory inside the Morse directory. Then, move or copy the directory containing the sketch (let’s call it SOS) we wrote above into the examples directory. (You can find the sketch using the Sketch > Show Sketch Folder command.) If you restart the Arduino environment (this is the last time, I promise) — you’ll see a Library-Morse item inside the File > Sketchbook > Examples menu containing your example. You might want to add some comments that better explain how to use your library.

If you’d like to check out the complete library (with keywords and example), you can download it: Morse.zip.

If you’d like to make your library available to others in Arduino’s Library Manager you will also have to include a library.properties file. Check out the library specification for more info on that. For general questions on the Arduino Library Manager, see the FAQ.

If you have any problems or suggestions, please post them to the Software Development forum.

For more information, see the API Style Guide for information on making a good Arduino-style API for your library.

Источник

Adblock
detector