Структура программы на языке C++ для Arduino
Рассмотрим пример минимально возможной программы на C++ для Arduino, которая ничего не делает:
Разберёмся что здесь написано и почему это обязательно: почему нельзя обойтись просто пустым файлом.
Из чего состоит программа
Для начала стоит понять, что программу нельзя читать и писать как книгу: от корки до корки, сверху вниз, строку за строкой. Любая программа состоит из отдельных блоков. Начало блока кода в C/C++ обозначается левой фигурной скобкой < , его конец — правой фигурной скобкой >.
Блоки бывают разных видов и какой из них когда будет исполняться зависит от внешних условий. В примере минимальной программы вы можете видеть 2 блока. В этом примере блоки называются определением функции. Функция — это просто блок кода с заданным именем, которым кто-то затем может пользоваться из-вне.
В данном случае у нас 2 функции с именами setup и loop . Их присутствие обязательно в любой программе на C++ для Arduino. Они могут ничего и не делать, как в нашем случае, но должны быть написаны. Иначе на стадии компиляции вы получите ошибку.
Классика жанра: мигающий светодиод
Давайте теперь дополним нашу программу так, чтобы происходило хоть что-то. На Arduino, к 13-му пину подключён светодиод. Им можно управлять, чем мы и займёмся.
Скомпилируйте, загрузите программу. Вы увидите, что каждую секунду светодиод на плате помигивает. Разберёмся почему этот код приводит к ежесекундному миганию.
В наши ранее пустые функции мы добавили несколько выражений. Они были размещены между фигурными скобками функций setup и loop . В setup появилось одно выражение, а в loop сразу 4.
Каждое выражение — это приказ процессору сделать нечто. Выражения в рамках одного блока исполняются одно за другим, строго по порядку без всяких пауз и переключений. То есть, если мы говорим об одном конкретном блоке кода, его можно читать сверху вниз, чтобы понять что делается.
Теперь давайте поймём в каком порядке исполняются сами блоки, т.е. функции setup и loop . Не задумывайтесь пока что значат конкретные выражения, просто понаблюдайте за порядком.
Если пронумеровать выражения по порядку, как они исполняются, получится:
Ещё раз напомним, что не стоит пытаться воспринимать всю программу, читая сверху вниз. Сверху вниз читается только содержимое блоков. Мы вообще можем поменять порядок объявлений setup и loop .
Результат от этого не изменится ни на йоту: после компиляции вы получите абсолютно эквивалентный бинарный файл.
Что делают выражения
Теперь давайте попробуем понять почему написанная программа приводит в итоге к миганию светодиода.
Как известно, пины Arduino могут работать и как выходы и как входы. Когда мы хотим чем-то управлять, то есть выдавать сигнал, нам нужно перевести управляющий пин в состояние работы на выход. В нашем примере мы управляем светодиодом на 13-м пине, поэтому 13-й пин перед использованием нужно сделать выходом.
Это делается выражением в функции setup :
Выражения бывают разными: арифметическими, декларациями, определениями, условными и т.д. В данном случае мы в выражении осуществляем вызов функции. Помните? У нас есть свои функции setup и loop , которые вызываются чем-то, что мы назвали «нечто». Так вот теперь мы вызываем функции, которые уже написаны где-то.
Конкретно в нашем setup мы вызываем функцию с именем pinMode . Она устанавливает заданный по номеру пин в заданный режим: вход или выход. О каком пине и о каком режиме идёт речь указывается нами в круглых скобках, через запятую, сразу после имени функции. В нашем случае мы хотим, чтобы 13-й пин работал как выход. OUTPUT означает выход, INPUT — вход.
Уточняющие значения, такие как 13 и OUTPUT называются аргументами функции. Совершенно не обязательно, что у всех функций должно быть по 2 аргумента. Сколько у функции аргументов зависит от сути функции, от того как её написал автор. Могут быть функции с одним аргументом, тремя, двадцатью; функции могут быть без аргументов вовсе. Тогда для их вызова круглые скобка открывается и тут же закрывается:
На самом деле, вы могли заметить, наши функции setup и loop также не принимают никакие аргументы. И загадочное «нечто» точно так же вызывает их с пустыми скобками в нужный момент.
Вернёмся к нашему коду. Итак, поскольку мы планируем вечно мигать светодиодом, управляющий пин должен один раз быть сделан выходом и затем мы не хотим вспоминать об этом. Для этого идеологически и предназначена функция setup : настроить плату как нужно, чтобы затем с ней работать.
Перейдём к функции loop :
Она, как говорилось, вызывается сразу после setup . И вызывается снова и снова как только сама заканчивается. Функция loop называется основным циклом программы и идеологически предназначена для выполнения полезной работы. В нашем случае полезная работа — мигание светодиодом.
Пройдёмся по выражениям по порядку. Итак, первое выражение — это вызов встроенной функции digitalWrite . Она предназначена для подачи на заданный пин логического нуля ( LOW , 0 вольт) или логической единицы ( HIGH , 5 вольт) В функцию digitalWrite передаётся 2 аргумента: номер пина и логическое значение. В итоге, первым делом мы зажигаем светодиод на 13-м пине, подавая на него 5 вольт.
Как только это сделано процессор моментально приступает к следующему выражению. У нас это вызов функции delay . Функция delay — это, опять же, встроенная функция, которая заставляет процессор уснуть на определённое время. Она принимает всего один аргумент: время в миллисекундах, которое следует спать. В нашем случае это 100 мс.
Пока мы спим всё остаётся как есть, т.е. светодиод продолжает гореть. Как только 100 мс истекают, процессор просыпается и тут же переходит к следующему выражению. В нашем примере это снова вызов знакомой нам встроенной функции digitalWrite . Правда на этот раз вторым аргументом мы передаём значение LOW . То есть устанавливаем на 13-м пине логический ноль, то есть подаём 0 вольт, то есть гасим светодиод.
После того, как светодиод погашен мы приступаем к следующему выражению. И снова это вызов функции delay . На этот раз мы засыпаем на 900 мс.
Как только сон окончен, функция loop завершается. По факту завершения «нечто» тут же вызывает её ещё раз и всё происходит снова: светодиод поджигается, горит, гаснет, ждёт и т.д.
Если перевести написанное на русский, получится следующий алгоритм:
Arduino Void Setup and Void Loop Functions [Explained]
When you open a new program in the Arduino IDE, you immediately get empty void setup and void loop functions written for you.
What are those void setup and void loop functions in Arduino?
In this tutorial I’ll explain to you the role of those functions and how to use them. At the end I’ll also give you some best practices to improve your Arduino programs.
Here is a quick video you can watch as an additional resource to this article:
If you like this video, subscribe to the Robotics Back-End Youtube channel so you don’t miss the next tutorials!
You are learning how to use Arduino to build your own projects?
Check out Arduino For Beginners and learn step by step.
Table of Contents
Void setup and void loop: mandatory functions in Arduino
The Arduino void setup and void loop functions are mandatory. Try to compile a code with one of those functions missing, and you’ll get an error.
When you run a “standard” C/C++ program, you have to write a “main” function. This main function will be called first, and from there, you will call other functions and execute the functionalities of your program.
In Arduino, there is no main function. This is replaced by setup and loop. Instead of one mandatory function, you have 2.
All your Arduino program must include those functions. On top of them, you can add any number of functions, variables, and create as many files as you want. You just need to make sure that the void setup and void loop are present.
How void setup and void loop work
Principle
As the “main” function is called when you run a C/C++ program, the setup and loop functions will be automatically called.
As soon as the program starts:
- Variables that you create on top of the program will be declared/initialized.
- The setup function will be called once.
- All the code you’ve written inside this function will be executed. After that, the setup function exits.
- Now the loop function will be called, so all the code inside the loop will be executed.
- When the loop function exists, go back to step 4.
The code inside the void setup will be executed once, and only once, at the beginning of the program.
Then, the code inside the void loop will be executed again and again (hence the name “loop”), until you:
- Power off the Arduino board.
- or Restart the Arduino program – by pressing the reset button / uploading a new sketch / re-opening the Serial Monitor on some Arduino boards.
After you start or restart the program, all the data – variable states and execution step – from the previous run will be lost. If you want to keep some data between different program runs, one easy way is to use the EEPROM memory if available.
Code Example
Let’s write a code example to see how the Arduino void setup and void loop work in detail.
And here’s the result when you compile the code, upload it to your Arduino board, and open the Serial Monitor.
Great, now let’s analyze this by breaking down the code line by line.
Init and void setup
First we create a global variable, so we can modify it inside a function and still be able to get its value in another function.
Note: even if you declare a variable without initializing it in Arduino, it will automatically get the value “0” (more info about the Arduino language). So, at this point of the program, the counter variable contains the value “0”.
This is the beginning of the void setup function. The execution will start with Serial.begin(9600); .
Still in the setup function, we set a new value (10) for the counter variable. And just after that we print the value in the Serial Monitor. In the output you get, this is where the line “Counter: 10” comes from.
And as you can notice, in line 9 we close the curly brackets for the void setup function. So, what’s happening now?
void loop
The void loop function is now called and every instruction you’ve provided here will be executed.
In this case, we increase the counter by 1 (so the first time we enter the loop, the counter goes from 10 to 11). Just after that, we print the value for the counter – this will be “Counter: 11”.
And finally, we add a 1 second pause to the program, so the output on the Serial Monitor will not be displayed to fast for our human eyes.
What happens when the void loop function exits?
Well, just after, it is called again. So, it means the line that comes after line 15 is line 12. The instruction just after delay(1000); is counter++; .
As the counter variable is a global variable, its value is not lost when we go out and enter the loop function again. So, now we increment it, it goes from 11 to 12.
And the cycle continues. Every time we enter the loop, we add 1 to the counter, print the value, and wait for 1 second.
Now, if you power off the Arduino, the program will stop. If you restart the program, any progress for the variables will be lost, and the entire program will start from the beginning.
Writing Arduino programs – best practices for void loop and void setup
Great, now that you understand how things work, I’m going to give you a few best practices you can implement right now in your Arduino programs, regarding those void setup and void loop functions.
Arduino void setup
As the void setup function is called only once at the very beginning of the program, this will be the place to:
- Initialize variables’ values.
- Setup communications (ex: Serial).
- Setup modes for digital pins (input/output).
- Initialize any hardware component (sensor/actuator) plugged to the Arduino.
- Etc.
The void setup, as its name suggest, is made for you to do any setup required at the beginning of the program. Don’t write the core functionalities here, just the initialization code.
Depending on the complexity of your program, you may have a lot of instructions to write in that void function. You can create new functions that you call from the void setup, no problem with that.
Also, in general it’s better to avoid using delay(), but there’s no problem in the void setup: if a component needs 2 seconds to be initialized after being powered on, then wait 2 seconds!
Arduino void loop
Now, in the void loop you’ll write your main program, knowing that the initialization is already done. In this function, always keep in mind that the last line is followed by the first line!
Also, any variable you’ve declared inside the void loop will be lost when the program exits and enters the function again. So, if you want to be able to keep data between 2 void loop, make sure to declare variables in a more global scope.
As for void setup, there’s no need to write all the code directly in the function. You can create as many other functions as you want (and classes too), and call those functions in the void loop. Ideally, the void loop should contain just a few lines calling other functions. Don’t bloat your void loop, just as you don’t bloat your “main” in a standard C/C++ program.
Finally, pay attention to delay(). In the void loop (and any function called from the void loop) you don’t want to block the execution of the code too long, especially if you plan to do some kind of multi-threading with your Arduino.
Conclusion
Now you should have a better idea of the concept behind Arduino void setup and loop functions.
For now, focus on creating programs that work, and try to follow the best practices from this guide. As you progress with Arduino, you’ll get more understanding from your own experience. This experience will help you decide what you can/can’t do in your void setup and void loop functions – because in the end, every program is different.
Did you find this tutorial useful?
Do you want to learn Arduino from scratch?
If yes, this course is for you:
Did you find this tutorial useful?
Do you want to become better at programming robots, with Arduino, Raspberry Pi, or ROS2?
If yes, subscribe to receive exclusive content and special offers!