ELECTRONICS

Программирование Arduino: язык, структура скетча и первые команды

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

На каком языке пишут

На C++. Не на «языке Arduino» — такого языка нет. Есть C++ плюс библиотека, которая прячет работу с процессором за простыми словами вроде digitalWrite. Из этого следует важное: всё, что умеет C++, умеет и Arduino — переменные, условия, циклы, функции, массивы. Просто на первых порах это не нужно.

Есть и другие варианты: MicroPython на ESP32 и Raspberry Pi Pico, визуальные блоки в Scratch-подобных средах для детей. Но девяносто процентов примеров, библиотек и ответов на форумах — на C++ в Arduino IDE, и учиться проще всего именно там.

Как устроен скетч

Две функции, и обе обязательны. setup выполняется один раз при включении: здесь назначаются роли выводам и открывается связь с компьютером. loop крутится по кругу, пока есть питание: закончился — начался снова. Всё, что плата делает, живёт внутри loop.

// Всё, что до setup, выполняется один раз при старте
const int LED = 13;          // константа: номер вывода
int counter = 0;             // переменная: будет меняться

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

void loop() {
  // выполняется по кругу, пока есть питание
  counter = counter + 1;
  Serial.println(counter);
  delay(1000);
}

Всё, что написано выше setup, — объявления: переменные, константы, подключение библиотек через #include. Строка с const int LED = 13 даёт выводу имя, чтобы не переписывать номер в пяти местах, когда решите перепаять.

Десять команд, которых хватает

pinMode(pin, mode)Назначить выводу роль: OUTPUT, INPUT или INPUT_PULLUP. Делается один раз в setup.
digitalWrite(pin, HIGH)Подать на вывод 5 В (HIGH) или 0 В (LOW). Зажечь светодиод, щёлкнуть реле.
digitalRead(pin)Узнать, что на выводе: HIGH или LOW. Нажата ли кнопка, сработал ли датчик.
analogRead(pin)Прочитать напряжение на A0–A5 числом от 0 до 1023. Потенциометр, фоторезистор, термистор.
analogWrite(pin, value)ШИМ от 0 до 255 на выводах с ~. Яркость светодиода, скорость мотора.
delay(ms)Остановить всё на указанное число миллисекунд. Просто, но плата в это время глухая.
millis()Сколько миллисекунд прошло со старта. Основа любого кода, который делает несколько дел сразу.
Serial.begin(9600)Открыть связь с компьютером. Дальше Serial.println выводит что угодно в монитор порта.
map(x, 0, 1023, 0, 255)Перевести число из одного диапазона в другой. Из analogRead в analogWrite — одной строкой.
tone(pin, freq)Пищать на заданной частоте. Зуммер, мелодия, сигнал тревоги. noTone останавливает.

Три скетча, которые стоит набрать руками

Не копируйте — набирайте. Так запоминаются скобки, точки с запятой и то, что HIGH пишется большими буквами. Каждый скетч собран под UNO, схемы к ним — на страницах кнопки и потенциометра.

Кнопка и светодиод

Первое условие. Кнопка между выводом 2 и землёй, подтяжка включена в коде, поэтому в покое на входе единица, а нажатие даёт ноль. Это не оговорка, а норма: так подключают кнопки почти везде.

// Кнопка на выводе 2 зажигает светодиод на выводе 13
const int BUTTON = 2;
const int LED = 13;

void setup() {
  pinMode(BUTTON, INPUT_PULLUP);   // вход с подтяжкой: не нажата — HIGH
  pinMode(LED, OUTPUT);
}

void loop() {
  if (digitalRead(BUTTON) == LOW) {   // нажата — замкнута на землю
    digitalWrite(LED, HIGH);
  } else {
    digitalWrite(LED, LOW);
  }
}

Потенциометр и яркость

Аналоговый вход, ШИМ и перевод диапазона. Плюс монитор порта: откройте его кнопкой с лупой в правом верхнем углу среды, и увидите, что читает плата.

// Потенциометр на A0 задаёт яркость светодиода на выводе 9
const int POT = A0;
const int LED = 9;             // вывод с ШИМ, помечен ~

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int raw = analogRead(POT);            // 0..1023
  int bright = map(raw, 0, 1023, 0, 255); // 0..255 для ШИМ
  analogWrite(LED, bright);
  Serial.print("raw=");
  Serial.print(raw);
  Serial.print(" bright=");
  Serial.println(bright);
  delay(50);
}

Мигание без delay

Тот же Blink, но плата не спит. Пока внутри delay она не слышит ни кнопок, ни датчиков. Как только в проекте появляется вторая задача, переходят на millis — и этот шаблон стоит запомнить целиком.

// Мигаем без delay: плата остаётся свободной для других дел
const int LED = 13;
unsigned long lastToggle = 0;
bool ledOn = false;

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  unsigned long now = millis();       // миллисекунд с момента старта
  if (now - lastToggle >= 500) {
    lastToggle = now;
    ledOn = !ledOn;
    digitalWrite(LED, ledOn ? HIGH : LOW);
  }
  // здесь можно читать кнопки, датчики — ничего не ждёт
}

Ошибки компиляции по-русски

Сообщения компилятора не переводятся даже в русифицированной среде. Вот что они значат на самом деле.

expected ';' before ...

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

'x' was not declared in this scope

Используете имя, которое не объявили — или объявили внутри другой функции, где его не видно. Чаще всего опечатка: Led вместо LED, регистр важен.

expected '}' at end of input

Не хватает закрывающей фигурной скобки. Нажмите Ctrl+T — среда расставит отступы, и место разрыва станет видно.

No such file or directory

Не найдена библиотека из строки #include. Она не установлена или называется иначе. Как ставить библиотеки и что делать, если не находится.

Скетч компилируется, но не работает

Это не ошибка кода, а ошибка схемы или загрузки. Проверьте, что выбрана та плата и тот порт, потом — что провода стоят на тех выводах, которые написаны в коде. Если загрузка вообще не идёт — разбор причин.

Что учить дальше

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

И главное: не читайте про это заранее. Берите схему с сайта, собирайте, ломайте скетч и чините. Это единственный способ, которым программирование Arduino вообще учится.

Что дальше

Все три скетча на этой странице собраны под Arduino UNO перед публикацией