Define arduino что это
#define is a useful C component that allows the programmer to give a name to a constant value before the program is compiled. Defined constants in arduino don’t take up any program memory space on the chip. The compiler will replace references to these constants with the defined value at compile time.
This can have some unwanted side effects though, if for example, a constant name that had been #defined is included in some other constant or variable name. In that case the text would be replaced by the #defined number (or text).
In general, the const keyword is preferred for defining constants and should be used instead of #define.
Arduino defines have the same syntax as C defines:
Syntax
#define constantName value
Note that the # is necessary.
Example
There is no semicolon after the #define statement. If you include one, the compiler will throw cryptic errors further down the page.
Similarly, including an equal sign after the #define statement will also generate a cryptic compiler error further down the page.
Corrections, suggestions, and new documentation should be posted to the Forum.
The text of the Arduino reference is licensed under a Creative Commons Attribution-ShareAlike 3.0 License. Code samples in the reference are released into the public domain.
#define
#define is a useful C++ component that allows the programmer to give a name to a constant value before the program is compiled. Defined constants in arduino don’t take up any program memory space on the chip. The compiler will replace references to these constants with the defined value at compile time.
This can have some unwanted side effects though, if for example, a constant name that had been #defined is included in some other constant or variable name. In that case the text would be replaced by the #defined number (or text).
In general, the const keyword is preferred for defining constants and should be used instead of #define .
Syntax
#define constantName value
Parameters
constantName : the name of the macro to define.
value : the value to assign to the macro.
Example Code
Notes and Warnings
There is no semicolon after the #define statement. If you include one, the compiler will throw cryptic errors further down the page.
Similarly, including an equal sign after the #define statement will also generate a cryptic compiler error further down the page.
Директивы препроцессора
Процесс компиляции прошивки очень непростой и проходит в несколько этапов, один из первых – работа препроцессора. Препроцессору можно давать команды, которые он выполнит перед компиляцией кода прошивки: это может быть подключение файлов, замена текста, условные конструкции и некоторые другие вещи. Также у препроцессора есть макросы, которые позволяют добавлять в код некоторые интересные вещи.
#include – подключить файл
С подключением файлов мы уже знакомы: директива #include подключает новый документ в текущий, например библиотеку. После #include нужно указать имя файла, который подключается. Указать можно в «двойных кавычках» , а можно в <угловых скобках> . В чём разница? Файл, имя которого указано в двойных кавычках, компилятор будет искать в папке с основным документом, если не найдёт – будет искать в папке с библиотеками. Если указать в скобках – будет сразу искать в папке с библиотеками, путь к которой обычно можно настроить.
Также можно указать путь к файлу, который нужно подключить. Например у нас в папке со скетчем есть папка libs, а в ней – файл mylib.h. Чтобы подключить такой файл, пишем:
Компилятор будет искать его в папке со скетчем, в подпапке libs.
#define / undef
Мы с вами уже сталкивались с #define в предыдущих уроках, сейчас хочу рассказать о некоторых частных случаях. Напомню, #define – это команда препроцессору заменить один набор символов на другой, например #define MOTOR_SPEED 50 заменит все встречающиеся в коде MOTOR_SPEED цифрой 50 при компиляции.
Если не писать ничего после указания первого набора символов, препроцессор заменит их на “ничего”. То есть #define MOTOR_SPEED просто удалит из кода все сочетания MOTOR_SPEED . Также #define позволяет создавать макро-функции, об этом мы говорили в уроке про функции. Например п ри помощи дефайна можно создавать удобные конструкции в стиле вечного цикла
Или быстрого и удобного отключения отладки в коде:
Если DEBUG задефайнен, то DEBUG_PRINT – это макро-функция, которая выводит значение в порт. А если не задефайнен – все вызовы DEBUG_PRINT просто убираются из кода и экономят память!
При разработке проекта важна отладка, мы делаем её средствами Serial.println() . Чтобы после окончания разработки не убирать из кода все вызовы Serial и не нагружать код условными конструкциями #ifdef DEBUG…. #endif, можно сделать так:
Если DEBUG_ENABLE задефайнен – все вызовы DEBUG() в коде будут заменены на вывод в порт. Если не задефайнен – они будут заменены НИЧЕМ, то есть просто “вырежутся” из кода. Также по DEBUG_ENABLE можно запустить сериал и получить полный контроль над отладкой: если она не нужна – убрали DEBUG_ENABLE и из кода убрался запуск порта и все выводы, что резко сокращает объём занимаемой памяти:
Также есть директива #undef , которая отменяет #define , в некоторых случаях может оказаться полезным.
Проблемы
В чём же состоит опасность #define ? Он распространяется на все документы, которые подключаются в код после него. Рассмотрим подробнее: Если ПЕРЕД подключением файла вы объявите #define , то он будет распространяться на этот файл и заменит указанный текст.
Если что-то в подключаемом файле (имена функций и переменных) совпадёт в вашим дефайном – будет ошибка компиляции. Например, в библиотеке FastLED есть цвет DarkMagenta , внутри библиотеки цвета объявлены как enum. Если я сделаю дефайн на такое имя – получу ошибку:
Но, если в подключаемом файле есть свой #define с таким же именем, то работать будет #define файла!
Важный момент: наш скетч в Arduino IDE по сути является .cpp файлом, и #define из него могут распространяться только на заголовочные файлы .h! То есть в файле .h подключаемой библиотеки дефайн будет “видно”, а вот в .cpp – уже нет!
Как решить эту проблему? Например, мы хотим управлять компиляцией библиотеки при помощи define-ов, расположенных не в заголовочном файле библиотеки (потому что из заголовочного можно, это и так понятно). Есть два несложных варианта:
- Поместить исполнительный код библиотеки в заголовочном .h файле (.cpp не создавать вообще), тогда дефайном из скетча можно будет влиять на компиляцию исполнительного кода. Этот пример мы рассматривали в самом первом скриншоте.
- Создать в папке с библиотекой отдельный заголовочный файл, например config.h, в нём собрать необходимые дефайны “настроек”, и этот файл подключать во все файлы библиотеки. В этом случае .cpp файл библиотеки сможет подхватить нужный define. Так сделано, например, в библиотеке FastLED.

На этом сложности не заканчиваются: #define из одной библиотеки может “пролезть” в другую библиотеку, которая подключена после первой! Вернёмся к тому же примеру с DarkMagenta – если в моей библиотеке я задефайню это слово и подключу библиотеку до подключения FastLED – я получу ошибку компиляции! Если поменять подключение местами – ошибки не будет. Но, если я захочу использовать DarkMagenta в своём скетче, я буду неприятно удивлён =)

Что я хочу сказать в итоге: #define – гораздо более мощный инструмент, чем может показаться на первый взгляд. Использование define с невнимательным отношением к именам может привести к ошибке, которую будет непросто отловить. Это палка о двух концах: с одной стороны хочется использовать в своей библиотеке define, чтобы никто другой случайно не пролез со своими дефайнами. В то же время, своя библиотека может начать конфликтовать с другими библиотеками.
Какой тут выход? Очень простой! Делать имена дефайнов максимально уникальными: если это библиотека – оставлять префикс библиотеки (например библиотека FastBot, префиксы дефайнов FB_MY_CONST), а если это скетч – делать префикс с именем скетча. Также можно отказаться от define в пользу констант или enum, enum кстати удобнее define в плане создания набора констант, а места занимает совсем немного!
#if – условная компиляция
Условная компиляция является весьма мощным инструментом, при помощи которого можно вмешиваться в компиляцию кода и делать его очень универсальным как для пользователя, так и для железа. Рассмотрим директивы условной компиляции:
- #if – аналог if в логической конструкции
- #elif – аналог else if в логической конструкции
- #else – аналог else в логической конструкции
- #endif – директива, завершающая условную конструкцию
- #ifdef – если “определено”
- #ifndef – если “не определено”
- defined – данный оператор возвращает true если указанное слово “определено” через #define , и false – если нет. Используется для конструкций условной компиляции.
Таким образом мы получили задефайненную константу VALUE , которая зависит от “настройки” TEST .
При помощи условной компиляции можно буквально включать и выключать целые части кода из компиляции, то есть из финальной версии программы, которая будет загружена в микроконтроллер. Рассмотрим несколько конструкций для примера:
Функция define Arduino: инструкции и директивы

Программирование
Начнем данную статью с определения того, что такое define. В первую очередь, по версии официальной документации C++ функция define это директива препроцессора. Препроцессор это довольно абстрактное понятие. Препроцессор-некая программа, проводящая манипуляции с кодом до процесса компиляции. Можно сказать, что препроцессор обрабатывает код, выполняет предварительные операции с файлами, после чего передает их компилятору. Он используется для компиляции программной части, написания макроса или вставки изображения. Именно директива препроцессора осуществляет подключение какой-либо библиотеки, файла или определение макроса.

Основы функции
Дословно define переводится как определить. Функция пишется в следующем виде:
#define <имя константы> <значение(то, на что меняем)>
#define <часть кода, который будет изменен> <то, на что изменяется наш код>
Функция define создает макрос(макроопределения), выполняющий замену заданных символов или строк на заданное значение. Макросы схожи с константами, их также можно использовать для задания констант. С помощью директивы #define зададим следующую константу:
В программе языка программирования C++ это будет выглядеть так:
еще один вариант реализации, только в данном случае реализовано это конкретно для ардуино:
Строка номер 5 для нас выглядит именно так, как она написана. Однако при попытке компиляции для программного компилятора она будет заменена на:
К тому же, данную функцию можно использовать для объявления переменных:
#define BLACK 11 // присваиваем имя BLACK для пина 11
#define GREEN 12 // присваиваем имя GRN для пина 12
#define PINK 13 // присваиваем имя BLU для пина 13
void setup() <
pinMode(BLACK, OUTPUT); // используем Pin11 для вывода
pinMode(GREEN, OUTPUT); // используем Pin12 для вывода
pinMode(PINK, OUTPUT); // используем Pin13 для вывода
>
Данный код- это часть программы мигающего трехцветного светодиода. Пинам 11, 12 и 13 присвоены имена соответствующих светодиодов(их цветов), подключенных к данным пинам. При объявлении номеров пинов удобнее использовать соответствующие цвета светодиодов, для того, чтобы не запутаться в программе, поэтому в данном случае лучше использовать функцию #define. После чего программа будет просто заменять данный названия на соответствующие им номера пинов.
Отличие функции #define от констант
Константы задаются точно также, как переменные, однако перед типом данных нужно ставить const. При попытке изменить константу компилятор выдаст ошибку «assignment of read-only variable»имя переменной»»-присвоение переменной, доступной только для чтения. Приведем пример программы:
byte sound_sensor_pin = 6;
void setup() <
pinMode(sound_sensor_pin, INPUT);
int a = 5100;
int b = 9500;
int c = 22222;
const byte d = 120;
>
void loop() <
boolean val = digitalRead(sound_sensor_pin);
>
В данной программе в диапазоне функции void setup(до его закрытия >) можно использовать локальную переменную байтового типа d. Она будет доступна только в данной функции. Такой тип констант называются локальными.
void setup() <
byte sound_sensor_pin = 6;
pinMode(sound_sensor_pin, INPUT);
int a = 5100;
sound_sensor_pin*3
>
void loop() <
boolean val = digitalRead(sound_sensor_pin);
sount_sensor_pin*4;
>
При запуске данной программы вы получите ошибку “sound_sensor_pin was not declared in this scope”, поскольку константа является таковой и имеет свое уникальное значение только в void setup. Однако если задать ему локальное произвольное значение в void loop, то в конечном счете все пройдет без ошибки. В данном случае у нас будет две локальные переменные, имеющие уникальное значение, нол одинаковое имя. То есть каждая переменная с одинаковым значением доступна только там, где она задана.
Переменные, объявленные без функций доступны во всей программе. Такая переменная называется глобальной.

Использование #define в данном случае поможет избежать нагрузки на память и освободит место для хранения других переменных. Однако с ним стоит быть осторожнее, поскольку он может заменить не то, что нужно.
Упрощение кода при помощи define на Arduino IDE
К тому же, данная функция используется не только для задания постоянных переменных(констант), её функционал позволяет заменять и части кода(строки). Команды можно заменить функцией #define при помощи создания более простых команд в программе. На примере данного кода можно увидеть реализацию данного способа:
#define o(pin) pinMode(pin, INPUT) // точку не ставим, поскольку она уже есть в i
Таким образом можно легко упростить код и написать его в несколько строчек. Такой способ удобен в применении при частом повторении одной и той же команды, которую написать в цикле не представляется возможным. в приведенном выше примере слово out заменялось на pinmode(pin, OUTPUT) с заданным параметром pin. Точно таким же способом можно заменять несколько команд. Например, у нас есть три светодиода, которые нужно подключить к микроконтроллеру:
#define o(pin) pinMode(pin, OUTPUT)
#define on(pin, d) digitalWrite(pin, HIGH); delay(d)
#define off(pin, d) digitalWrite(pin, LOW); delay(dl)
void setup() <
o(11);
o(12);
o(13);
>
void loop() <
on(11, 1000);
off(11, 1000);
on(12, 1000);
off(12, 1000);
on(13, 1000);
off(13, 1000);
>
Примите к сведению, что код on(11,1000), который заменяет функции, сам как таковой не носит характер функции, а функция define просто заменяет заданным кодом текст непосредственно на функцию.
Функцию следует использовать внимательно, поскольку в сложных программах существует риск создать кучу ошибок при компиляции кода, так как программа может потребовать подключения нескольких библиотек, в которых дефайн может не так понять и заменить что-то не то.
Функции команд #ifdef, #ifndef, #endif
Согласно официальной документации Arduino IDE #ifdef осуществляет поиск определения функции #define в программе, если оно встречается, то будут выполнены заданные действия. Например, если встретилось Checkbypass, то будет выведено “Yes”
Если в программной части микроконтроллера встречен цвет светодиода YELLOW, то внутреннее содержание #ifndef игнорируется, в ином случае-в код добавляется define константа светодиода с цветом YELLOW.
#ifndef YELLOW // если не нашлось YELLOW, ТО
#define YELLOW 11 // ВЫВОДИМ YELLOW
Использование const или #define-что универсальнее?
Директива #define не всегда универсальна для создания констант, в таком случае советуем вам использовать const. В отличие от глобальных переменных, значение константы может быть определено только в начале программы. При использовании #define имена следует делать максимально уникальными, дабы исключить любые совпадения с командами из подключаемых библиотек, что может сломать весь функционал программы.
const int YELLOW = 11; // 11 пин-YELLOW
const int PINK = 12; // 12 пин-PINK
const int LBLUE = 13; // 13 пин- LBLUE(голубой)
void setup() <
pinMode(YELLOW, OUTPUT); // 11 пин на вывод-светодиод
pinMode(PINK, OUTPUT); // 12 пин на вывод-светодиод
pinMode(LBLUE, OUTPUT); // 13 пин на вывод-светодиод
>
По сути, использование DEFINE и CONST не имеет разницы, использование константы в данном примере аналогично дефайну-вместо PINK подставится 12. На константах правила видимости глобальных и локальных переменных аналогичен дефайну, является стандартным. К тому же, ни одна из данных функций не имеет преимущества для памяти микроконтроллера.
