Что такое Arduino?
Arduino — это компания, которая разрабатывает аппараты и программы для робототехники и не только. Фактически компания зародилась в 2003 году, когда Эрнандо Барраган в рамках учебной работы создал первую версию платформы Arduino Wiring.
Wiring является библиотекой языка программирования С/С++. Это «расширение» предназначено для работы с платами Arduino. Для передачи кода нашей плате используется среда разработки Arduino IDE. В ней и происходит написание программы.
Среда разработки Arduino IDE
Переменные
Переменная – это ячейка SRAM памяти, которая имеет своё уникальное название и хранит числа соответственно своему размеру. К переменной мы можем обратиться по её имени и получить значение, либо изменить его. Степень двойки преследует нас и дальше, ведь объём одной ячейки памяти в микроконтроллере тоже ей кратен:
- 1 байт = 8 бит = 256
- 2 байта = 16 бит = 65 536
- 4 байта = 32 бита = 4 294 967 296
Да, больше четырёх байт в ардуино (точнее в МК от AVR) уже не влезет, при использовании обычных типов данных. Для работы с разными диапазонами значений используются разные типы данных (переменных). По сути можно использовать 4 байта для хранения чего угодно, но это не оптимально. Это как знать, что вам нужно будет унести максимум 200 мл воды (меньше 1 байта), но вы всё равно берёте 19 литровую бутыль (2 байта). Или железнодорожную цистерну на 120 тонн (4 байта). Если хотите писать красивый и оптимальный код, используйте соответствующие типы данных. Кстати, вот они:
Типы данных
Название | Альт. название | Вес | Диапазон | Особенность |
boolean | bool | 1 байт | 0 или 1, true или false | Логическая переменная. bool на Arduino тоже занимает 1 байт, а не бит! |
char | — | 1 байт | -128… 127 | Хранит номер символа из таблицы символов ASCII |
— | int8_t | 1 байт | -128… 127 | Целочисленный тип |
byte | uint8_t | 1 байт | 0… 255 | Целочисленный тип |
int | int16_t, short | 2 байта | -32 768… 32 767 | Целочисленный тип |
unsigned int | uint16_t, word | 2 байта | 0… 65 535 | Целочисленный тип |
long | int32_t | 4 байта | -2 147 483 648… 2 147 483 647 | Целочисленный тип |
unsigned long | uint32_t | 4 байта | 0… 4 294 967 295 | Целочисленный тип |
float | — | 4 байта | -3.4028235E+38… 3.4028235E+38 | Хранит числа с плавающей точкой (десятичные дроби). Точность: 6-7 знаков |
double | — | 4 байта | Для AVR то же самое, что float. А так он 8 байт на более взрослом железе | |
— | int64_t | 8 байт | -(2^64)/2… (2^64)/2-1 | *Очень большие числа. Стандартный Serial не умеет такие выводить |
— | uint64_t | 8 байт | 2^64-1 | *Очень большие числа. Стандартный Serial не умеет такие выводить |
*Не встречал упоминания об этом в официальных источниках, но Ардуино (точнее компилятор) также поддерживает 64 битные числа, соответственно тип данных int64_t и uint64_t Максимальный размер всех типов данных хранится в константах, и его можно использовать в коде по надобности:
- UINT8_MAX – вернёт 255
- INT8_MAX – вернёт 127
- UINT16_MAX – вернёт 65 535
- INT16_MAX – вернёт 32 767
- UINT32_MAX– вернёт 4 294 967 295
- INT32_MAX – вернёт 2 147 483 647
- UINT64_MAX – вернёт 18 446 744 073 709 551 615
- INT64_MAX – вернёт 9 223 372 036 854 775 807
Помимо целочисленных типов (byte, int, long) есть более интересные:
- bool (boolean) – логический тип данных, принимает значения 0 и 1, или true и false (правда и ложь). По сути ведёт себя как бит, но занимает 8 бит. Экая несправедливость! Есть несколько путей хранить логические переменные так, чтобы они занимали 1 байт, но об этом поговорим позже. Также переменная типа boolean принимает значение true, если присвоить ей значение? отличное от нуля, то есть boolean a = 50; a будет true, и boolean b = -20; тоже будет true! Пример: boolean flag = true;
- char – символьный тип данных, в численном эквиваленте принимает значения от -128 до 127. В случае с char эти значения являются кодами символов в стандартной таблице символов ASCII. Может принимать и хранить данные в символьном формате (буква или символ в одиночных кавычках), например char var = ‘a’;
- float – тип данных с плавающей точкой (англ. float – плавающий), т.е. десятичная дробь. float var = 3.1415;
Есть ещё несколько нестандартных типов, которые иногда встречаются в чужом коде:
- size_t – “синоним” uint16_t, предназначен для отображения размера объекта в байтах. Например этот тип возвращает функция sizeof() и некоторые другие.
- Расширенный char: char16_t, char32_t и wchar_t. Нужен для хранения больших символьных данных для алфавитов разных стран, а не только английского языка.
Объявление и инициализация переменных
Объявление переменной – резервирование имени под данные указанного типа. Инициализация – присвоение переменной начального значения при помощи оператора =.
- тип_данных имя; // объявление
- тип_данных имя = значение; // объявление и инициализация
- Также можно объявить и инициализировать несколько переменных через запятую: int a = 0, b, с = 10;
byte myVal; int sensorRead = 10; byte val1, val2, val3 = 10;
Важные моменты:
- Переменная должна быть объявлена до обращения к себе, буквально находиться выше по коду. Иначе вы получите ошибку “переменная не объявлена” – Not declared in this scope
- Глобальные и статические переменные при объявлении имеют значение 0 по умолчанию, даже если не инициализировать
- Локальные переменные (создаваемые внутри функций в процессе работы программы) при объявлении могут иметь случайное значение, т.к. выделяются из динамической памяти (может иметь последнее значение локальной переменной из другой функции и далее в таком духе). Крайне рекомендуется их инициализировать, если в дальнейшем коде от них ожидается нулевое значение
void setup() { Serial.begin(9600); } int valG; // глобальная, 0 void loop() { static int valS; // статическая, 0 int valL; // локальная, может иметь любое значение }
Преобразование типов
Иногда требуется преобразовать один тип данных в другой: например, функция принимает int, а вы хотите передать ей byte. В большинстве случаев компилятор сам разберётся и преобразует byte в int, но иногда вылетает ошибка в стиле “попытка передать byte туда, где ждут int“. В таком случае можно преобразовать тип данных, для этого достаточно указать нужный тип данных в скобках перед преобразуемой переменной (тип_данных)переменная, иногда можно встретить запись тип_данных(переменная). Результат вернёт переменную с новым типом данных, сам же тип данной у переменной не изменится (работает в рамках одного действия). Например:
// переменная типа byte byte val = 10; // передаём какой-то функции, которая ожидает int sendVal( (int)val );
И всё! (int)val будет обрабатываться как int, а не как byte.
Преобразование _cast (Pro)
Иногда можно встретить преобразование типов через оператор cast. Отличную статью можно глянуть на Хабре, а я кратко опишу 4 основных каста:
- reinterpret_cast– приведение типов без проверки, непосредственное указание компилятору. Применяется только в случае полной уверенности программиста в собственных действиях. Не снимает const и volatile, применяется для приведения указателя к указателю, указателя к целому и наоборот;
- static_cast– преобразует выражения одного статического типа в объекты и значения другого статического типа. Поддерживается преобразование численных типов, указателей и ссылок по иерархии наследования как вверх, так и вниз. Преобразование проверяется на уровне компиляции и в случае ошибки приведения типов будет выдано сообщение;
- dynamic_cast– используется для динамического приведения типов во время выполнения. В случае неправильного приведения типов для ссылок вызывается исключительная ситуация std::bad_cast, а для указателей будет возвращен 0;
- const_cast– самое простое приведение типов. Снимает const и volatile, то есть константность и отказ от оптимизации компилятором переменной. Это преобразование проверяется на уровне компиляции и в случае ошибки приведения типов будет выдано сообщение.
Как пользоваться: на примере предыдущего примера
// переменная типа byte byte val = 10; // передаём какой-то функции, которая ожидает int sendVal( static_cast(val) );
Установка Arduino IDE
Для использования среды Arduino IDE можно установить программу на свой компьютер или использовать онлайн-версию программы (необходима регистрация). Разберем, как установить программу на Windows 8/10.
- Переходим на официальный сайт Arduino. В пункте Download выбираем необходимую операционную систему.
- Попадаем на страницу с возможностью пожертвовать деньги на дальнейшее развитие Arduino. Нажмем на кнопку «Just download», чтобы установить программу без пожертвований.
3. Нас отправляют на сайт Microsoft Store, где нажимаем «Получить».
Программа установлена и готова к работе.
Подключение библиотек и файлов
В реальной работе вы очень часто будете использовать библиотеки или просто внешние файлы, они подключаются к главному файлу (файлу прошивки) при помощи директивы #include, данная директива сообщает препроцессору, что нужно найти и включить в компиляцию указанный файл. Указанный файл может тянуть за собой и другие файлы, но там оно уже всё прописано и подключается автоматически. Рассмотрим пример:
#include // подключает библиотеку Servo.h #include “Servo.h” // тоже подключает библиотеку Servo.h
В чём отличие <> и «»? Когда указываем название «в кавычках», компилятор сначала ищет файл в папке со скетчем, а затем в папке с библиотеками. При использовании <�галочек> компилятор ищет файл только в папке с библиотеками! К слову о папках с библиотеками: их две, в обеих будет производиться поиск библиотек.
- Мои Документы/Arduino/libraries
- C:/Program Files (x86)/Arduino/libraries (или C:/Program Files/Arduino/libraries для 32-разрядной Windows)
В первую папку (в документах) библиотеки попадают при подключении их при помощи команды “подключить .zip библиотеку”. Подключать библиотеки таким способом не рекомендуется, потому что не всегда библиотека попадает к вам в архиве, и проще будет скопировать её вручную в Program files. Также если в обеих папках будут одинаковые по названию библиотеки, это приведёт к конфликту, поэтому библиотеки просто копируем в папку libraries в Program files/Arduino.
Важное замечание: папка с библиотекой, находящаяся в C:/Program Files (x86)/Arduino/libraries, должна содержать файлы и папки библиотеки, а не одну папку с таким же названием, как сама библиотека. Это приведёт к ошибке, сборщик не сможет найти файлы!
Горячие клавиши Arduino IDE
Для упрощения работы в Arduino IDE используются «горячие клавиши» или хоткеи, от английского hotkeys, что в переводе и означает «горячие клавиши». Это комбинации клавиш на клавиатуре, которые выполняют различные действия в операционной системе и программах. Все команды доступны через меню «Файл», но через хоткеи работать гораздо быстрее.
Разберем горячие клавиши и их назначение.
Правка
Ctrl+Z | отмена одной операции |
Ctrl+Y | возврат одной отмененной операции |
Ctrl+F | поиск по коду |
Atrl+A | выделение всего кода |
Ctrl+P | печать содержимого вкладки |
Ctrl+X | вырезать выделенный код |
Ctrl+C | копировать выделенный код |
Ctrl+V | вставить выделенный код |
Компиляция и загрузка
Ctrl+R | компиляция скетча |
Ctrl+U | загрузить скетч |
Ctrl+Shift+U | загрузить скетч с помощью программатора |
Сохранение и работа с вкладками
Ctrl+S | сохранить текущий скетч |
Ctrl+Shift+S | сохранить текущий скетч с выбором имени сохраняемого файла |
Ctrl+W | закрыть текущую вкладку |
Ctrl+Shift+N | новая вкладка |
Ctrl+Alt+Стрелка вправо | переключение на вкладку справа от активной |
Ctrl+Alt+Стрелка влево | переключение на вкладку слева от активной |
Другое
Ctrl+N | открыть новое окно редактора |
Ctrl+O | открыть существующий файл скетча |
Ctrl+Слэш ( / или русская точка) | закомментирование строки |
Ctrl+K | открыть папку со скетчами |
Ctrl+T | автоформатирование кода |
Ctrl+Shift+M | монитор порта |
Ctrl+, (русская буква Б) | страница настроек Arduino IDE. |
Оформление
Форматирование
Есть такое понятие, как форматирование (выравнивание) кода, то есть соблюдение пробелов и интервалов. Чисто для примера, сравните эти два куска кода. Какой смотрится более понятно и наглядно?
Не бойтесь, во всех серьезных средах разработки есть автоформатирование кода, оно работает как в процессе написания, так и по вызову. Arduino IDE – не исключение, в ней код форматируется по горячей комбинации Ctrl+T
- Между математическими действиями, знаками сравнения, присваивания и всем подобным ставится пробел
- Как и в обычном тексте, пробел ставится после и не ставится перед запятой, двоеточием, точкой с запятой
- Отступ от левого края экрана – знак табуляции, код сдвигается вправо и на одном расстоянии формируются команды из одного блока кода. В Arduino IDE одна табуляция равна двум пробелам. Можно использовать клавишу Tab
- Каждое действие выполняется с новой строки (автоформатирование это не исправляет)
- Фигурные скобки начала и окончания блока кода принято писать на отдельной строке. Также очень многие пишут открывающую скобку на строке с оператором, это экономит строку и не раздувает код по высоте
- Имена переменных принято писать начиная с маленькой буквы, называть их так, чтобы было понятно. Да, английский неплохо бы подтянуть! Пример: value
- Если название переменной состоит из двух и более слов, они разделяются верхним регистром первой буквы каждого нового слова, либо слова разделяются подчёркиванием. Пример: myButtonState, button_flag
- Имена типов данных и классов принято писать с большой буквы. Пример: Signal, Servo
- Имена констант принято писать в верхнем регистре, разделение – подчеркивание. Пример: MOTOR_SPEED
- При написании библиотек и классов, имена внутренних (private) переменных принято писать, начиная со знака подчёркивания. Пример: _position
- Несколько общепринятых сокращений для названий переменных, вы часто будете встречать их в чужих прошивках и библиотеках: button – btn, кнопка. Я обычно сокращаю кнопку до butt (с англ. – жоп@), потому что я весёлый
- index – idx – i, индекс
- buffer – buf, буфер
- value – val, значение
- variable – var, переменная
- pointer – ptr, указатель
- get– получить значение (getValue)
Шаблон программы. Функции setup() и loop()
Комментарии
В начале программы принято указывать некоторые комментарии. Здесь автор кода может написать о себе, о программе и ее назначении. Для обозначения комментария в начале строки пишем 2 символа «//», например:
// Файл «matrixb.h» содержит определение класса Matrix_B, реализацию // его методов в программе «program2.vcproj» // Класс Matrix_B является базовым для класса Matrix_D // Создатель программы: Иванов Иван Иванович, 7а // Версия 1.0 от 14.02.14
Шаблон программы
Сама по себе программа напоминает дом. Наш «дом» состоит из 2х частей:
- Функция setup() выступает в качестве «фундамента дома».
- Функция loop() напоминает жилую часть дома: здесь и «живет» сама программа.
Запустим Arduino IDE и напишем шаблон программы:
void setup() { } void loop() { }
После setup и loop скобки () пишутся без пробела. Внутри скобок пробел отсутствует. Сами функции пишутся маленькими буквами. Фигурные скобки располагаются одна под другой на следующих строчках после имени функции.
Если вы все написали правильно, то ваша функция станет другого цвета.
Разберем подробнее назначение каждой функции.
Функция setup()
Функция setup() — это подготовка. Это первая функция, выполняемая программой. Она выполняется только один раз сразу после включения платы или ее перезапуска кнопкой reset. setup() используется чтобы завести новые функции, настроить ПИНы платы, создать переменные. Функция должна быть включена в программу, даже если в ней нет никакого содержания.
Функция loop()
После функции setup() управление переходит к функции loop(). В переводе с английского loop — это петля. Функция делает в точности то, что означает её имя — непрерывно выполняется заново. Она позволяет программе что-то изменять, возвращать данные и управлять платой Arduino.
Структура кода
Прежде чем переходить к структуре и порядку частей кода, нужно кое-что запомнить:
- Переменная любого типа должна вызываться после своего объявления. Иначе будет ошибка
- Объявление и использование классов или типов данных из библиотеки/файла должно быть после подключения библиотеки/файла
- Функция может вызываться как до, так и после объявления, потому что C++ компилируемый язык, компиляция проходит в несколько этапов, и функции “выделяются” отдельно, поэтому могут вызываться в любом месте программы
При запуске Arduino IDE даёт нам заготовку в виде двух обязательных функций: setup и loop
Код в блоке setup() выполняется один раз при каждом запуске микроконтроллера. Код в блоке loop() выполняется “по кругу” на всём протяжении работы микроконтроллера, начиная с момента завершения выполнения setup(). Для любознательных: если вы уже знакомы с языком C++, то вероятно спросите “а где же int main() и вообще файл main.cpp?”. Всё очень просто: int main() за вас уже написали внутри файла main.cpp, который лежит глубоко в файлах “ядра”, а setup() и loop() встроены в него следующим образом:
// main.cpp // где-то в глубинах ядра Arduino int main() { setup(); for (;;) { loop(); } return 0; }
На протяжении нескольких лет работы с Arduino я сформировал для себя следующую структуру скетча:
- Описание прошивки, полезные ссылки, заметки, авторство
- Константы настройки (define и обычные)
- Служебные константы (которые следует менять только с полным осознанием дела)
- Подключаемые библиотеки и внешние файлы, объявление соответствующих им типов данных и классов
- Глобальные переменные
- setup()
- loop()
- Свои функции
Пример кода
/* Данный скетч плавно крутит сервопривод туда-обратно между мин. и макс. углами by AlexGyver */ // ——— НАСТРОЙКИ ——— #define SERVO_PIN 13 // сюда подключена серво #define SERVO_SPEED 3 // скорость серво #define MIN_ANGLE 50 // мин. угол #define MAX_ANGLE 120 // макс. угол // ——- БИБЛИОТЕКИ ——- #include Servo myservo; // ——- ПЕРЕМЕННЫЕ ——- uint32_t servoTimer; boolean servoDirection; int servoAngle; // ——— SETUP ———- void setup() { myservo.attach(SERVO_PIN); } // ———- LOOP ———- void loop() { turnServo(); } // ——— ФУНКЦИИ ——— void turnServo() { if (millis() — servoTimer >= 50) { // каждые 50 мс servoTimer = millis(); if (servoDirection) { servoAngle += SERVO_SPEED; if (servoAngle >= MAX_ANGLE) { servoAngle = MAX_ANGLE; servoDirection = false; } } else { servoAngle -= SERVO_SPEED; if (servoAngle <= MIN_ANGLE) { servoAngle = MIN_ANGLE; servoDirection = true; } } myservo.write(servoAngle); } }
Это удобная структура для “скетча”, крупные проекты так писать не рекомендуется и следует приучать себя к более взрослым подходам, описанным в уроке по разработке крупных проектов.
Пространство имён (Pro)
Пространство имён – очень удобная возможность языка, с её помощью можно разделить функции или переменные с одинаковыми именами друг от друга, то есть защитить свой набор данных инструментов от конфликтов имён с другими именами. “Именная область” определяется при помощи оператора namespace:
namespace mySpace { // функции или данные };
Чтобы использовать содержимое из пространства имён, нужно обратиться через его название и оператор разрешения области видимости ::
mySpace::имя_функции
Более подробный пример:
Пример с namespace
namespace mySpace { byte val; void printKek() { Serial.println(«kek»); } }; void setup() { Serial.begin(9600); // printKek(); // приведёт к ошибке mySpace::printKek(); }
Также есть оператор using, позволяющий не использовать каждый раз обращение к пространству имён. Например, в отдельном файле у нас есть пространство имён с различными функциями. Чтобы в основном файле программы каждый раз не писать ярлык пространства имён с ::, можно написать using имя_пространства_имён;
И ниже по коду можно будет пользоваться содержимым пространства имён без обращения через имя::
Библиотека Servo
Библиотека позволяет управлять сервомоторами. Поддерживает до 24 сервоприводов на любых доступных GPIO. По умолчанию первые 12 сервоприводов будут использовать Timer0 и будут независимы от любых других процессов. Следующие 12 сервоприводов будут использовать Timer1 и будут разделять ресурсы с другими функциями, использующими Timer1. Большинство сервоприводов будут работать с управляющим сигналом ESP8266 3,3в, но не смогут работать на напряжении 3,3в и потребуют отдельный источник питания. Не забудьте соединить общий провод GND этого источника с GND ESP8266
Другие библиотеки, не включенные в поставку Arduino IDE
Почти все библиотеки, которые не используют низкоуровневый доступ к регистрам микропроцессора AVR должны работать без каких-либо доработок. На сегодняшний день можно точно сказать, что протестированы и полностью работоспособны следующие библиотеки:
- arduinoWebSockets — WebSocket сервер и клиент для esp8266 (RFC6455)
- aREST REST API handler библиотека, позволяет управлять GPIO через http запросы вида https://192.168.1.101/digital/6/1
- Blynk — легкий в освоении IoT фреймворк (страница на Kickstarter). Статья на нашем сайте об этой библиотеке и мобильном приложении ESP8266 – Управляем со смартфона через Blynk
- DallasTemperature DS18B20, DS1820, DS18S20, DS1822
- DHT11 — используйте для инициализации следующие параметры DHT dht(DHTPIN, DHTTYPE, 15)
- NeoPixelBus — Arduino NeoPixel библиотека для esp8266
- PubSubClient Библиотека MQTT by @Imroy. Статья на нашем сайте об этой библиотеке ESP8266 подключаемся к OpenWRT+Mosquitto+mqttwarn и передаем данные на ThingSpeak, EMAIL, Android, iOS, Twitter, CloudMQTT в 100 строчек кода в Arduino IDE
- RTC — библиотека for Ds1307 & Ds3231 для esp8266
- Souliss, Smart Home — фреймворк для Умного Дома, построенный на Arduino, Android и OpenHAB
Основы основ
Несколько формальных вещей, то есть таких, о которых все знают, но иногда забывают…
В Arduino IDE, как в C/C++, необходимо помнить о регистрах символов. Ключевые слова, такие как if, for всегда записываются в нижнем регистре. Каждая инструкция заканчивается на «;». Точка с запятой сообщает компилятору, какую часть интерпретировать как инструкцию.
Скобки {..} используются для обозначения программных блоков. Мы используем их для ограничения тела функции (см. ниже), циклов и условных операторов.
Хорошей практикой является добавление комментариев к содержимому программы, это помогает легко понять код. Однострочные комментарии начинаются с // (двойная косая черта). Многострочные комментарии начинаются с /* и заканчиваются на */
Если мы хотим подключить в нашу программу какую-либо библиотеку, мы используем команду include. Вот примеры подключения библиотек:
Лабораторный блок питания 30 В / 10 А
Подробнее
#include // стандартная библиотека #include «svoya_biblioteka.h» // библиотека в каталоге проекта
Перечисления (Pro)
Перечисления (enum – enumeration) – тип данных, представляющий собой набор именованных констант, нужен в первую очередь для удобства программиста. Сразу пример из опыта: допустим у нас есть переменная mode, отвечающая за номер режима работы устройства. Мы для себя запоминаем, какому значению переменной какой режим будет соответствовать, и где-нибудь себе записываем, например 0 – обычный режим, 1 – режим ожидания, 2 – режим настройки_1, 3 – режим настройки_2, 4 – калибровка, 5 – аварийный режим, ошибка. При написании или чтении программы часто придётся обращаться к этому списку, чтобы не запутаться. Можно сделать первый шаг по оптимизации: обозвать каждый режим при помощи дефайна:
#define NORMAL 0 #define WAITING 1 #define SETTINGS_1 2 #define SETTINGS_2 3 #define CALIBRATION 4 #define ERROR_MODE 5
Таким образом вместо цифры можно будет использовать понятные слова и ориентироваться в коде будет гораздо проще. Использование enum ещё немного упрощает эту конструкцию: перечисление позволяет создать переменную (по умолчанию типа int), которая может принимать только те “названия”, которые для неё указаны. Это удобно тем, что в одной программе могут находиться разные хранители режимов с одинаковыми названиями, и в отличие от #define это не будет приводить к ошибкам. Объявление перечисления чем-то похоже на объявление структуры:
enum ярлык {имя1, имя2, имя3, имя4, имя5};
Таким образом мы объявили ярлык. Теперь, используя этот ярлык, можно объявить само перечисление:
ярлык имя_перечисления;
Также как и у структур, можно объявить перечисление без создания ярлыка (зачем нам лишняя строчка?):
enum {имя1, имя2, имя3, имя4, имя5} имя перечисления;
Созданное таким образом перечисление является переменной, которая может принимать указанные для неё имена, также с этими именами её можно сравнивать. Теперь самое главное: имена для программы являются числами, начиная с 0 и далее по порядку увеличиваясь на 1. В абстрактном примере выше имя1 равно 0, имя2 равно 1, имя3 равно 2, и так далее. Помимо указанных имён, перечислению можно приравнять и число напрямую, но как бы зачем. Рассмотрим пример!
Большой пример с перечислениями
// создаём перечисление modes // не создавая ярлык enum { NORMAL, WAITING, SETTINGS_1, SETTINGS_2, CALIBRATION, ERROR_MODE, } modes; void setup() { Serial.begin(9600); // для отладки modes = CALIBRATION; // присваивание значения // можем сравнивать if (modes == CALIBRATION) { Serial.println(«calibr»); } else if (modes == ERROR_MODE) { Serial.println(«error»); } // присваиваем числом modes = 3; // по нашему порядку это будет SETTINGS_2 }
Также порядок автонумерации перечислений можно изменить, задав начальное значение. От него всё будет и дальше изменяться на единицу: enum {SET1 = 1, SET2, SET3, SET4, SET5} settings;
Таким образом SET1 имеет значение 1, SET2 будет 2 и так далее по порядку.