Как программируют Arduino

Разбираемся, какой язык внутри, как устроена программа и где у платы пределы.

Если первый раз открыть Arduino IDE и увидеть код — он похож на обычный C/C++ с парой непривычных слов вроде setup() и loop(). На самом деле это и есть C++ с лёгкой обёрткой, которая прячет от программиста работу с регистрами микроконтроллера. Дальше — пять коротких частей о том, что это за язык, как устроена любая Arduino-программа, что такая плата умеет, чего не умеет и какие альтернативы есть для смелых.

Язык Arduino

Технически язык Arduino — это C++, скомпилированный для процессоров AVR (или ARM на новых платах) с помощью avr-gcc. Поверх голого C++ накинута небольшая библиотека-фреймворк, которую раньше называли Wiring. Именно она даёт вам функции уровня digitalWrite(), analogRead(), delay() и весь набор простых вызовов, без которых пришлось бы вручную ковыряться в DDR- и PORT-регистрах.

Зачем сделали именно так:

  • Низкий порог входа. Можно зажечь светодиод, не зная, что такое регистр и таймер.

  • Один и тот же код на разных платах. Uno, Nano, Mega, ESP32 — у всех есть digitalWrite(LED_BUILTIN, HIGH), и неважно, что под капотом разное железо.

  • Полный C++ остался под рукой. Если очень нужно — пишите классы, шаблоны, лямбды. Компилятор всё съест.

Среда разработки Arduino IDE — кнопки «Проверить» (компиляция) и «Загрузить» (прошить плату).

Подготовка и бесконечность

В каждом скетче (так в мире Arduino называют исходный файл) есть две обязательные функции — setup() и loop(). Эта пара заменяет привычный main().

void setup() {
  // выполняется один раз при включении или перезагрузке
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // повторяется бесконечно, пока питание не пропадёт
  digitalWrite(13, HIGH);
  delay(500);
  digitalWrite(13, LOW);
  delay(500);
}

setup() запускается ровно один раз — при подаче питания или после нажатия Reset. Его задача — настроить «начальное состояние»: какие пины на вход, какие на выход, какая скорость у Serial-порта, инициализировать датчики и дисплеи.

loop() крутится в бесконечном цикле. Закончили — снова сначала. Внутри — вся «работа» программы: опрос кнопок, чтение датчиков, управление моторами, печать в монитор порта.

Что можно и чего нельзя

ATmega328 на классической Uno/Nano — это 16 МГц, 32 КБ Flash и 2 КБ оперативной памяти. Это одно ядро, без операционной системы и без виртуальной памяти. Из этого вытекает несколько важных ограничений.

  • Многозадачности нет. Если в loop() вызвали delay(1000), то секунду плата буквально стоит на месте — ни кнопки опросить, ни на UART ответить. Имитируют параллельность через millis()-таймеры или библиотеки вроде TaskScheduler.

  • Памяти мало. 2 КБ ОЗУ заканчиваются неожиданно быстро. Длинные строки лучше держать во Flash через макрос F(): Serial.print(F("Hello")) вместо обычной строки.

  • Тяжёлая математика дорого стоит. sin(), sqrt(), операции с float выполняются программно — без аппаратного FPU это сотни и тысячи тактов. Если важна скорость — используйте целые числа и lookup-таблицы.

  • Нет файлов и сети без доп. железа. SD-карта подключается отдельно (модуль + библиотека SD.h), Wi-Fi — отдельно (ESP-01, ESP8266, ESP32). На голой Uno вы только дёргаете ножками.

  • Никаких сообщений об ошибках во время работы. Программа упала в бесконечный цикл — индикатор не зажжётся, окно не всплывёт. Единственный способ что-то узнать — заранее печатать диагностику в Serial.print() и смотреть в монитор порта.

И всё же

Несмотря на скромные ресурсы, Arduino даёт то, чего не даёт ни один веб-фреймворк: вы пишете код — и через секунду в реальном мире что-то двигается, светится или пищит. Сделать автополив для цветов, замок с RFID-карточкой, метеостанцию во дворе или ночник, реагирующий на свет в комнате — это всё реально собирается за один вечер на копеечной плате.

При этом язык остаётся понятным: переменные, функции, циклы, условия — всё как в любом учебнике C++. А когда проект подрастёт, без переписывания можно перенести его на ESP32 или STM32 — Arduino-API на них тоже работает.

Какие ещё языки используют для Arduino

Хотя «родной» язык — C++, прошить плату AVR можно чем угодно, что компилируется в машинный код для этого процессора. Вот основные альтернативы.

  • Чистый C. Подойдёт тем, кто хочет полного контроля и не нуждается в классах. Понадобится avr-gcc (входит в WinAVR/AVRDude) и пакет avr-libc. Кода получится больше, но он будет компактнее и быстрее в критичных местах.

  • Ассемблер AVR. Для самых требовательных к скорости и размеру задач — например, генерации видеосигнала или быстрых протоколов вроде WS2812B. Писать тяжело, но один блок на 50 строк может заменить сотню строк C.

  • Python. На самой Uno полноценный Python не запустится — слишком мало памяти. Но через MicroPython отлично работают ESP8266, ESP32, RP2040 (Raspberry Pi Pico). Для Uno остаётся вариант «компьютер общается с платой по Serial»: на ПК Python через pyserial шлёт команды, плата их выполняет.

  • JavaScript / Node.js. Аналогично — на самой Uno нет, но Espruino и Tessel запускают JS прямо на железе. Для классической Arduino работает паттерн «через Serial»: библиотека johnny-five под Node.js даёт почти полный контроль с ноутбука.

  • Lua. На NodeMCU (тот же ESP8266) есть прошивка с Lua-интерпретатором — короткие скрипты пишутся буквально в три строки.

В общем правило простое: если у языка есть среда выполнения, влезающая в память контроллера, — можно. Если нет — пишите на ПК и общайтесь с платой через UART, I²C или USB-HID.